Skip to content

Commit 32e8358

Browse files
committed
Redirect strategy updates & test
1 parent 7e23065 commit 32e8358

13 files changed

+412
-108
lines changed

packages-exp/auth-exp/demo/src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,8 @@ function initApp() {
16301630
log('Initializing app...');
16311631
app = initializeApp(config);
16321632
auth = initializeAuth(app, {
1633-
persistence: browserSessionPersistence
1633+
persistence: browserSessionPersistence,
1634+
popupRedirectResolver: browserPopupRedirectResolver,
16341635
});
16351636

16361637
// tempApp = initializeApp({

packages-exp/auth-exp/src/core/auth/auth_event_manager.test.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ import * as sinon from 'sinon';
2020
import * as sinonChai from 'sinon-chai';
2121

2222
import {
23-
AuthEvent,
24-
AuthEventConsumer,
25-
AuthEventError,
26-
AuthEventType
23+
AuthEvent, AuthEventConsumer, AuthEventError, AuthEventType
2724
} from '../../model/popup_redirect';
2825
import { AuthErrorCode } from '../errors';
2926
import { AuthEventManager } from './auth_event_manager';
@@ -145,7 +142,8 @@ describe('src/core/auth/auth_event_manager', () => {
145142
consumer = makeConsumer([
146143
AuthEventType.SIGN_IN_VIA_REDIRECT,
147144
AuthEventType.LINK_VIA_REDIRECT,
148-
AuthEventType.REAUTH_VIA_REDIRECT
145+
AuthEventType.REAUTH_VIA_REDIRECT,
146+
AuthEventType.UNKNOWN,
149147
]);
150148
});
151149

@@ -181,15 +179,13 @@ describe('src/core/auth/auth_event_manager', () => {
181179
expect(consumerB.onAuthEvent).not.to.have.been.called;
182180
});
183181

184-
it('unknown auth error prevents consumption of future redirect events', () => {
182+
it('queues unknown events', () => {
185183
const event = makeEvent(AuthEventType.UNKNOWN);
186184
event.error = { code: 'auth/no-auth-event' } as AuthEventError;
187185
expect(manager.onEvent(event)).to.be.true;
188-
expect(manager.onEvent(makeEvent(AuthEventType.SIGN_IN_VIA_REDIRECT))).to
189-
.be.false;
190186

191187
manager.registerConsumer(consumer);
192-
expect(consumer.onAuthEvent).not.to.have.been.called;
188+
expect(consumer.onAuthEvent).to.have.been.calledWith(event);
193189
});
194190
});
195191
});

packages-exp/auth-exp/src/core/auth/auth_event_manager.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@
1616
*/
1717

1818
import {
19-
AuthEvent,
20-
AuthEventConsumer,
21-
AuthEventType,
22-
EventManager
19+
AuthEvent, AuthEventConsumer, AuthEventType, EventManager
2320
} from '../../model/popup_redirect';
2421
import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../errors';
2522

@@ -47,13 +44,6 @@ export class AuthEventManager implements EventManager {
4744
}
4845

4946
onEvent(event: AuthEvent): boolean {
50-
if (isNullRedirectEvent(event)) {
51-
// A null redirect event comes through as the first event when no pending
52-
// redirect actions are in the auth domain
53-
this.hasHandledPotentialRedirect = true;
54-
return true;
55-
}
56-
5747
let handled = false;
5848
this.consumers.forEach(consumer => {
5949
if (this.isEventForConsumer(event, consumer)) {
@@ -62,7 +52,7 @@ export class AuthEventManager implements EventManager {
6252
}
6353
});
6454

65-
if (this.hasHandledPotentialRedirect || !isRedirectEvent(event.type)) {
55+
if (this.hasHandledPotentialRedirect || !isRedirectEvent(event)) {
6656
// If we've already seen a redirect before, or this is a popup event,
6757
// bail now
6858
return handled;
@@ -80,7 +70,7 @@ export class AuthEventManager implements EventManager {
8070
}
8171

8272
private sendToConsumer(event: AuthEvent, consumer: AuthEventConsumer): void {
83-
if (event.error) {
73+
if (event.error && !isNullRedirectEvent(event)) {
8474
const code =
8575
(event.error.code?.split('auth/')[1] as AuthErrorCode) ||
8676
AuthErrorCode.INTERNAL_ERROR;
@@ -112,12 +102,14 @@ function isNullRedirectEvent({ type, error }: AuthEvent): boolean {
112102
);
113103
}
114104

115-
function isRedirectEvent(type: AuthEventType): boolean {
116-
switch (type) {
105+
function isRedirectEvent(event: AuthEvent): boolean {
106+
switch (event.type) {
117107
case AuthEventType.SIGN_IN_VIA_REDIRECT:
118108
case AuthEventType.LINK_VIA_REDIRECT:
119109
case AuthEventType.REAUTH_VIA_REDIRECT:
120110
return true;
111+
case AuthEventType.UNKNOWN:
112+
return isNullRedirectEvent(event);
121113
default:
122114
return false;
123115
}

packages-exp/auth-exp/src/core/auth/auth_impl.ts

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@ import { getApp } from '@firebase/app-exp';
1919
import { FirebaseApp } from '@firebase/app-types-exp';
2020
import * as externs from '@firebase/auth-types-exp';
2121
import {
22-
CompleteFn,
23-
createSubscribe,
24-
ErrorFn,
25-
NextFn,
26-
Observer,
27-
Subscribe,
28-
Unsubscribe
22+
CompleteFn, createSubscribe, ErrorFn, NextFn, Observer, Subscribe, Unsubscribe
2923
} from '@firebase/util';
3024

3125
import { Auth, Dependencies } from '../../model/auth';
@@ -34,8 +28,7 @@ import { User } from '../../model/user';
3428
import { AuthErrorCode } from '../errors';
3529
import { Persistence } from '../persistence';
3630
import {
37-
_REDIRECT_USER_KEY_NAME,
38-
PersistenceUserManager
31+
_REDIRECT_USER_KEY_NAME, PersistenceUserManager
3932
} from '../persistence/persistence_user_manager';
4033
import { _reloadWithoutSaving } from '../user/reload';
4134
import { assert } from '../util/assert';
@@ -60,6 +53,7 @@ export class AuthImpl implements Auth {
6053
private idTokenSubscription = new Subscription<User>(this);
6154
private redirectUser: User | null = null;
6255
_isInitialized = false;
56+
_popupRedirectResolver: PopupRedirectResolver | null = null;
6357

6458
// Tracks the last notified UID for state change listeners to prevent
6559
// repeated calls to the callbacks
@@ -79,21 +73,23 @@ export class AuthImpl implements Auth {
7973
popupRedirectResolver?: externs.PopupRedirectResolver
8074
): Promise<void> {
8175
return this.queue(async () => {
76+
if (popupRedirectResolver) {
77+
this._popupRedirectResolver = _getInstance(popupRedirectResolver);
78+
}
79+
8280
this.persistenceManager = await PersistenceUserManager.create(
8381
this,
8482
persistenceHierarchy
8583
);
8684

87-
await this.initializeCurrentUser(popupRedirectResolver);
85+
await this.initializeCurrentUser();
8886

8987
this._isInitialized = true;
9088
this.notifyAuthListeners();
9189
});
9290
}
9391

94-
private async initializeCurrentUser(
95-
popupRedirectResolver?: externs.PopupRedirectResolver
96-
): Promise<void> {
92+
private async initializeCurrentUser(): Promise<void> {
9793
const storedUser = await this.assertedPersistence.getCurrentUser();
9894
if (!storedUser) {
9995
return this.directlySetCurrentUser(storedUser);
@@ -104,16 +100,8 @@ export class AuthImpl implements Auth {
104100
return this.reloadAndSetCurrentUserOrClear(storedUser);
105101
}
106102

107-
assert(popupRedirectResolver, this.name, AuthErrorCode.ARGUMENT_ERROR);
108-
const resolver: PopupRedirectResolver = _getInstance(popupRedirectResolver);
109-
110-
this.redirectPersistenceManager = await PersistenceUserManager.create(
111-
this,
112-
[_getInstance(resolver._redirectPersistence)],
113-
_REDIRECT_USER_KEY_NAME
114-
);
115-
116-
this.redirectUser = await this.redirectPersistenceManager.getCurrentUser();
103+
assert(this._popupRedirectResolver, this.name, AuthErrorCode.ARGUMENT_ERROR);
104+
await this.getOrInitRedirectPersistenceManager();
117105

118106
// If the redirect user's event ID matches the current user's event ID,
119107
// DO NOT reload the current user, otherwise they'll be cleared from storage.
@@ -189,11 +177,30 @@ export class AuthImpl implements Auth {
189177
);
190178
}
191179

192-
async _setRedirectUser(user: User): Promise<void> {
193-
return this.redirectPersistenceManager?.setCurrentUser(user);
180+
async _setRedirectUser(user: User|null, popupRedirectResolver?: externs.PopupRedirectResolver): Promise<void> {
181+
const redirectManager = await this.getOrInitRedirectPersistenceManager(popupRedirectResolver);
182+
return user === null ? redirectManager.removeCurrentUser() : redirectManager.setCurrentUser(user);
194183
}
195184

196-
_redirectUserForId(id: string): User | null {
185+
private async getOrInitRedirectPersistenceManager(popupRedirectResolver?: externs.PopupRedirectResolver): Promise<PersistenceUserManager> {
186+
if (!this.redirectPersistenceManager) {
187+
const resolver: PopupRedirectResolver | null = (popupRedirectResolver && _getInstance(popupRedirectResolver) || this._popupRedirectResolver);
188+
assert(resolver, this.name, AuthErrorCode.ARGUMENT_ERROR);
189+
this.redirectPersistenceManager = await PersistenceUserManager.create(
190+
this,
191+
[_getInstance(resolver._redirectPersistence)],
192+
_REDIRECT_USER_KEY_NAME
193+
);
194+
this.redirectUser = await this.redirectPersistenceManager.getCurrentUser();
195+
}
196+
197+
return this.redirectPersistenceManager;
198+
}
199+
200+
async _redirectUserForId(id: string): Promise<User | null> {
201+
// Make sure we've cleared any pending ppersistence actions
202+
await this.queue(async () => {});
203+
197204
if (this.currentUser?._redirectEventId === id) {
198205
return this.currentUser;
199206
}

packages-exp/auth-exp/src/core/strategies/abstract_popup_redirect_operation.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@ import { testAuth, testUser } from '../../../test/mock_auth';
2929
import { makeMockPopupRedirectResolver } from '../../../test/mock_popup_redirect_resolver';
3030
import { Auth } from '../../model/auth';
3131
import {
32-
AuthEvent,
33-
AuthEventType,
34-
EventManager,
35-
PopupRedirectResolver
32+
AuthEvent, AuthEventType, EventManager, PopupRedirectResolver
3633
} from '../../model/popup_redirect';
3734
import { AuthEventManager } from '../auth/auth_event_manager';
3835
import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../errors';
@@ -144,7 +141,7 @@ describe('src/core/strategies/abstract_popup_redirect_operation', () => {
144141

145142
it('emits the user credential returned from idp task', async () => {
146143
finishPromise(authEvent());
147-
const cred = await operation.execute();
144+
const cred = (await operation.execute())!;
148145
expect(cred.user.uid).to.eq('uid');
149146
expect(cred.credential).to.be.null;
150147
expect(cred.operationType).to.eq(OperationType.SIGN_IN);

packages-exp/auth-exp/src/core/strategies/abstract_popup_redirect_operation.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,15 @@ import { FirebaseError } from '@firebase/util';
1919

2020
import { Auth } from '../../model/auth';
2121
import {
22-
AuthEvent,
23-
AuthEventConsumer,
24-
AuthEventType,
25-
EventManager,
26-
PopupRedirectResolver
22+
AuthEvent, AuthEventConsumer, AuthEventType, EventManager, PopupRedirectResolver
2723
} from '../../model/popup_redirect';
2824
import { User, UserCredential } from '../../model/user';
2925
import { AuthErrorCode } from '../errors';
3026
import { debugAssert, fail } from '../util/assert';
3127
import { _link, _reauth, _signIn, IdpTask, IdpTaskParams } from './idp';
3228

3329
interface PendingPromise {
34-
resolve: (cred: UserCredential) => void;
30+
resolve: (cred: UserCredential|null) => void;
3531
reject: (error: Error) => void;
3632
}
3733

@@ -58,8 +54,8 @@ export abstract class AbstractPopupRedirectOperation
5854

5955
abstract onExecution(): Promise<void>;
6056

61-
execute(): Promise<UserCredential> {
62-
return new Promise<UserCredential>(async (resolve, reject) => {
57+
execute(): Promise<UserCredential|null> {
58+
return new Promise<UserCredential|null>(async (resolve, reject) => {
6359
this.pendingPromise = { resolve, reject };
6460

6561
try {
@@ -115,7 +111,7 @@ export abstract class AbstractPopupRedirectOperation
115111
}
116112
}
117113

118-
protected resolve(cred: UserCredential): void {
114+
protected resolve(cred: UserCredential|null): void {
119115
debugAssert(this.pendingPromise, 'Pending promise was never set');
120116
this.pendingPromise.resolve(cred);
121117
this.unregisterAndCleanUp();

packages-exp/auth-exp/src/core/strategies/popup.test.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ import * as chaiAsPromised from 'chai-as-promised';
2020
import * as sinon from 'sinon';
2121
import * as sinonChai from 'sinon-chai';
2222

23-
import {
24-
OperationType,
25-
PopupRedirectResolver,
26-
ProviderId
27-
} from '@firebase/auth-types-exp';
23+
import { OperationType, PopupRedirectResolver, ProviderId } from '@firebase/auth-types-exp';
2824
import { FirebaseError } from '@firebase/util';
2925

3026
import { delay } from '../../../test/delay';
@@ -43,11 +39,8 @@ import * as eid from '../util/event_id';
4339
import { AuthPopup } from '../util/popup';
4440
import * as idpTasks from './idp';
4541
import {
46-
_AUTH_EVENT_TIMEOUT,
47-
_POLL_WINDOW_CLOSE_TIMEOUT,
48-
linkWithPopup,
49-
reauthenticateWithPopup,
50-
signInWithPopup
42+
_AUTH_EVENT_TIMEOUT, _POLL_WINDOW_CLOSE_TIMEOUT, linkWithPopup, reauthenticateWithPopup,
43+
signInWithPopup
5144
} from './popup';
5245

5346
use(sinonChai);

packages-exp/auth-exp/src/core/strategies/popup.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,10 @@
1818
import * as externs from '@firebase/auth-types-exp';
1919

2020
import { Auth } from '../../model/auth';
21-
import {
22-
AuthEventType,
23-
PopupRedirectResolver
24-
} from '../../model/popup_redirect';
21+
import { AuthEventType, PopupRedirectResolver } from '../../model/popup_redirect';
2522
import { User } from '../../model/user';
2623
import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../errors';
27-
import { debugAssert } from '../util/assert';
24+
import { assert, debugAssert } from '../util/assert';
2825
import { Delay } from '../util/delay';
2926
import { _generateEventId } from '../util/event_id';
3027
import { _getInstance } from '../util/instantiator';
@@ -49,7 +46,7 @@ export async function signInWithPopup(
4946
provider,
5047
resolver
5148
);
52-
return action.execute();
49+
return action.executeNotNull();
5350
}
5451

5552
export async function reauthenticateWithPopup(
@@ -67,7 +64,7 @@ export async function reauthenticateWithPopup(
6764
resolver,
6865
user
6966
);
70-
return action.execute();
67+
return action.executeNotNull();
7168
}
7269

7370
export async function linkWithPopup(
@@ -85,7 +82,7 @@ export async function linkWithPopup(
8582
resolver,
8683
user
8784
);
88-
return action.execute();
85+
return action.executeNotNull();
8986
}
9087

9188
/**
@@ -114,6 +111,12 @@ class PopupOperation extends AbstractPopupRedirectOperation {
114111
PopupOperation.currentPopupAction = this;
115112
}
116113

114+
async executeNotNull(): Promise<externs.UserCredential> {
115+
const result = await this.execute();
116+
assert(result, this.auth.name);
117+
return result;
118+
}
119+
117120
async onExecution(): Promise<void> {
118121
debugAssert(
119122
this.filter.length === 1,

0 commit comments

Comments
 (0)