Skip to content

Commit cddc280

Browse files
committed
Add persistence events to indexed DB
1 parent 0ede306 commit cddc280

File tree

14 files changed

+231
-79
lines changed

14 files changed

+231
-79
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,32 @@ export class AuthImplCompat<T extends User> implements Auth {
102102
});
103103
}
104104

105+
/**
106+
* If the persistence is changed in another window, the user manager will let us know
107+
*/
108+
async _onStorageEvent(): Promise<void> {
109+
const user = await this.persistenceManager!.getCurrentUser();
110+
111+
// If the same user is to be synchronized.
112+
if (this.currentUser && user && this.currentUser.uid === user.uid) {
113+
// Data update, simply copy data changes.
114+
this.currentUser._copy(user);
115+
// If tokens changed from previous user tokens, this will trigger
116+
// notifyAuthListeners_.
117+
await this.currentUser.getIdToken();
118+
return;
119+
} else if (!this.currentUser && !user) {
120+
// No change, do nothing (was signed out and remained signed out).
121+
return;
122+
} else {
123+
// Update current Auth state. Either a new login or logout.
124+
await this.updateCurrentUser(user);
125+
// TODO: If a new user is signed in, enabled popup and redirect on that user.
126+
// Notify external Auth changes of Auth change event.
127+
this.notifyAuthListeners();
128+
}
129+
}
130+
105131
_createUser(params: UserParameters): T {
106132
return new this._userProvider(params);
107133
}

packages-exp/auth-exp/src/core/persistence/persistence_user_manager.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import * as sinonChai from 'sinon-chai';
2323
import { testAuth, testUser, TestAuth } from '../../../test/helpers/mock_auth';
2424
import { UserImpl } from '../user/user_impl';
2525
import { _getInstance } from '../util/instantiator';
26-
import { Persistence, PersistenceType } from './';
26+
import { Persistence, PersistenceType, StorageEventListener } from './';
2727
import { inMemoryPersistence } from './in_memory';
2828
import { PersistenceUserManager } from './persistence_user_manager';
2929

@@ -42,7 +42,9 @@ function makePersistence(
4242
get() {
4343
return Promise.resolve(null);
4444
},
45-
remove: async () => {}
45+
remove: async () => {},
46+
addListener(_key: string, _listener: StorageEventListener) {},
47+
removeListener(_key: string, _listener: StorageEventListener) {}
4648
};
4749

4850
const stub = sinon.stub(persistence);

packages-exp/auth-exp/src/core/persistence/persistence_user_manager.ts

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717

1818
import { ApiKey, AppName, Auth } from '../../model/auth';
1919
import { User } from '../../model/user';
20-
import { AuthErrorCode } from '../errors';
21-
import { PersistedBlob, Persistence, PersistenceValue } from '../persistence';
20+
import { PersistedBlob, Persistence } from '../persistence';
2221
import { UserImpl } from '../user/user_impl';
23-
import { assert } from '../util/assert';
2422
import { _getInstance } from '../util/instantiator';
2523
import { inMemoryPersistence } from './in_memory';
2624

@@ -37,14 +35,9 @@ function _persistenceKeyName(
3735
return `${PERSISTENCE_NAMESPACE}:${key}:${apiKey}:${appName}`;
3836
}
3937

40-
export interface UserEventListener {
41-
(): void;
42-
}
43-
4438
export class PersistenceUserManager {
4539
private readonly fullUserKey: string;
4640
private readonly fullPersistenceKey: string;
47-
private listener: UserEventListener | null = null;
4841

4942
private constructor(
5043
public persistence: Persistence,
@@ -58,6 +51,7 @@ export class PersistenceUserManager {
5851
config.apiKey,
5952
name
6053
);
54+
this.persistence.addListener(this.fullUserKey, auth._onStorageEvent);
6155
}
6256

6357
setCurrentUser(user: User): Promise<void> {
@@ -92,27 +86,11 @@ export class PersistenceUserManager {
9286
}
9387
}
9488

95-
_onStorageEvent(_value: PersistenceValue | null): void {
96-
assert(this.listener, AuthErrorCode.INTERNAL_ERROR, {
97-
appName: this.auth.name
98-
});
99-
this.listener();
100-
}
101-
102-
async addListener(listener: UserEventListener): Promise<void> {
103-
assert(!this.listener, AuthErrorCode.INTERNAL_ERROR, {
104-
appName: this.auth.name
105-
});
106-
this.listener = listener;
107-
this.persistence.addListener(this.fullUserKey, this._onStorageEvent);
108-
}
109-
110-
removeListener(listener: UserEventListener): void {
111-
assert(this.listener === listener, AuthErrorCode.INTERNAL_ERROR, {
112-
appName: this.auth.name
113-
});
114-
this.persistence.removeListener(this.fullUserKey, this._onStorageEvent);
115-
this.listener = null;
89+
delete(): void {
90+
this.persistence.removeListener(
91+
this.fullUserKey,
92+
this.auth._onStorageEvent
93+
);
11694
}
11795

11896
static async create(

packages-exp/auth-exp/src/core/user/reload.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
ProviderUserInfo
2323
} from '../../api/account_management/account';
2424
import { User } from '../../model/user';
25-
import { UserMetadata } from './user_impl';
25+
import { UserMetadata } from './user_metadata';
2626
import { assert } from '../util/assert';
2727
import { AuthErrorCode } from '../errors';
2828

packages-exp/auth-exp/src/core/user/token_manager.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ export class StsTokenManager {
141141
};
142142
}
143143

144+
_copy(stsTokenManager: StsTokenManager): void {
145+
this.accessToken = stsTokenManager.accessToken;
146+
this.refreshToken = stsTokenManager.refreshToken;
147+
this.expirationTime = stsTokenManager.expirationTime;
148+
}
149+
144150
_performRefresh(): never {
145151
return debugFail('not implemented');
146152
}

packages-exp/auth-exp/src/core/user/user_impl.ts

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,21 @@
1717

1818
import * as externs from '@firebase/auth-types-exp';
1919
import { NextFn } from '@firebase/util';
20-
2120
import {
2221
APIUserInfo,
2322
deleteAccount
2423
} from '../../api/account_management/account';
2524
import { FinalizeMfaResponse } from '../../api/authentication/mfa';
25+
import { Auth } from '../../model/auth';
2626
import { IdTokenResponse } from '../../model/id_token';
27-
import { User, UserParameters, MutableUserInfo } from '../../model/user';
27+
import { MutableUserInfo, User, UserParameters } from '../../model/user';
28+
import { AuthErrorCode } from '../errors';
2829
import { PersistedBlob } from '../persistence';
2930
import { assert } from '../util/assert';
3031
import { getIdTokenResult } from './id_token_result';
3132
import { reload, _reloadWithoutSaving } from './reload';
3233
import { StsTokenManager } from './token_manager';
33-
import { Auth } from '../../model/auth';
34-
import { utcTimestampToDateString } from '../util/time';
35-
import { AuthErrorCode } from '../errors';
34+
import { UserMetadata } from './user_metadata';
3635

3736
function assertStringOrUndefined(
3837
assertion: unknown,
@@ -45,26 +44,6 @@ function assertStringOrUndefined(
4544
);
4645
}
4746

48-
export class UserMetadata implements externs.UserMetadata {
49-
readonly creationTime?: string;
50-
readonly lastSignInTime?: string;
51-
52-
constructor(
53-
private readonly createdAt?: string | number,
54-
private readonly lastLoginAt?: string | number
55-
) {
56-
this.lastSignInTime = utcTimestampToDateString(lastLoginAt);
57-
this.creationTime = utcTimestampToDateString(createdAt);
58-
}
59-
60-
toJSON(): object {
61-
return {
62-
createdAt: this.createdAt,
63-
lastLoginAt: this.lastLoginAt
64-
};
65-
}
66-
}
67-
6847
export class UserImpl implements User {
6948
// For the user object, provider is always Firebase.
7049
readonly providerId = externs.ProviderId.FIREBASE;
@@ -74,7 +53,7 @@ export class UserImpl implements User {
7453
auth: Auth;
7554
emailVerified = false;
7655
isAnonymous = false;
77-
tenantId = null;
56+
tenantId: string | null = null;
7857
readonly metadata: UserMetadata;
7958
providerData: MutableUserInfo[] = [];
8059

@@ -123,6 +102,25 @@ export class UserImpl implements User {
123102
private reloadUserInfo: APIUserInfo | null = null;
124103
private reloadListener: NextFn<APIUserInfo> | null = null;
125104

105+
_copy(user: User): void {
106+
if (this === user) {
107+
return;
108+
}
109+
assert(this.uid === user.uid, AuthErrorCode.INTERNAL_ERROR, {
110+
appName: this.auth.name
111+
});
112+
this.displayName = user.displayName;
113+
this.photoURL = user.photoURL;
114+
this.email = user.email;
115+
this.emailVerified = user.emailVerified;
116+
this.phoneNumber = user.phoneNumber;
117+
this.isAnonymous = user.isAnonymous;
118+
this.tenantId = user.tenantId;
119+
this.providerData = user.providerData.map(userInfo => ({ ...userInfo }));
120+
this.metadata._copy(user.metadata);
121+
this.stsTokenManager._copy(user.stsTokenManager);
122+
}
123+
126124
_onReload(callback: NextFn<APIUserInfo>): void {
127125
// There should only ever be one listener, and that is a single instance of MultiFactorUser
128126
assert(!this.reloadListener, AuthErrorCode.INTERNAL_ERROR, {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as externs from '@firebase/auth-types-exp';
19+
20+
import { utcTimestampToDateString } from '../util/time';
21+
22+
export class UserMetadata implements externs.UserMetadata {
23+
creationTime?: string;
24+
lastSignInTime?: string;
25+
26+
constructor(
27+
private createdAt?: string | number,
28+
private lastLoginAt?: string | number
29+
) {
30+
this._initializeTime();
31+
}
32+
33+
private _initializeTime(): void {
34+
this.lastSignInTime = utcTimestampToDateString(this.lastLoginAt);
35+
this.creationTime = utcTimestampToDateString(this.createdAt);
36+
}
37+
38+
_copy(metadata: UserMetadata): void {
39+
this.createdAt = metadata.createdAt;
40+
this.lastLoginAt = metadata.lastLoginAt;
41+
this._initializeTime();
42+
}
43+
44+
toJSON(): object {
45+
return {
46+
createdAt: this.createdAt,
47+
lastLoginAt: this.lastLoginAt
48+
};
49+
}
50+
}

packages-exp/auth-exp/src/model/auth.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export interface Auth extends AuthCore {
4545
_isInitialized: boolean;
4646
updateCurrentUser(user: User | null): Promise<void>;
4747

48+
_onStorageEvent(): void;
4849
_createUser(params: UserParameters): User;
4950
_setPersistence(persistence: externs.Persistence): void;
5051
_onAuthStateChanged(

packages-exp/auth-exp/src/model/user.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { PersistedBlob } from '../core/persistence';
2424
import { Auth } from './auth';
2525
import { IdTokenResponse, TaggedWithTokenResponse } from './id_token';
2626
import { StsTokenManager } from '../core/user/token_manager';
27+
import { UserMetadata } from '../core/user/user_metadata';
2728

2829
type MutableUserInfo = {
2930
-readonly [K in keyof externs.UserInfo]: externs.UserInfo[K];
@@ -58,7 +59,7 @@ export interface User extends externs.User {
5859
emailVerified: boolean;
5960
tenantId: string | null;
6061
providerData: MutableUserInfo[];
61-
metadata: externs.UserMetadata;
62+
metadata: UserMetadata;
6263

6364
stsTokenManager: StsTokenManager;
6465
_redirectEventId?: string;
@@ -68,6 +69,7 @@ export interface User extends externs.User {
6869
reload?: boolean
6970
): Promise<void>;
7071

72+
_copy(user: User): void;
7173
_onReload: (cb: NextFn<APIUserInfo>) => void;
7274
_notifyReloadListener: NextFn<APIUserInfo>;
7375

packages-exp/auth-exp/src/platform_browser/auth.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,8 @@ import { testUser, testAuth } from '../../test/helpers/mock_auth';
2828
import { browserPopupRedirectResolver } from './popup_redirect';
2929
import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../core/errors';
3030
import { Persistence } from '../core/persistence';
31-
import {
32-
browserLocalPersistence,
33-
browserSessionPersistence
34-
} from './persistence/browser';
31+
import { browserLocalPersistence } from './persistence/local_storage';
32+
import { browserSessionPersistence } from './persistence/session_storage';
3533
import { inMemoryPersistence } from '../core/persistence/in_memory';
3634
import { PersistenceUserManager } from '../core/persistence/persistence_user_manager';
3735
import * as reload from '../core/user/reload';

0 commit comments

Comments
 (0)