Skip to content

Commit 0a55186

Browse files
committed
Expose a callback function that is triggered for token refresh
1 parent 010e0cc commit 0a55186

File tree

4 files changed

+101
-3
lines changed

4 files changed

+101
-3
lines changed

packages/auth/demo/src/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,14 @@ async function getActiveUserBlocking() {
149149
}
150150
}
151151

152+
class TokenRefreshHandlerImpl {
153+
refreshToken() {
154+
log("inside here");
155+
console.log("inside handler");
156+
return;
157+
}
158+
}
159+
152160
/**
153161
* Refreshes the current user data in the UI, displaying a user info box if
154162
* a user is signed in, or removing it.
@@ -2090,6 +2098,8 @@ function initApp() {
20902098
popupRedirectResolver: browserPopupRedirectResolver,
20912099
tenantConfig: tenantConfig
20922100
});
2101+
const tokenRefreshHandler = new TokenRefreshHandlerImpl();
2102+
regionalAuth.setTokenRefreshHandler(tokenRefreshHandler);
20932103

20942104
tempApp = initializeApp(
20952105
{

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import {
4949
Subscribe
5050
} from '@firebase/util';
5151

52-
import { AuthInternal, ConfigInternal } from '../../model/auth';
52+
import { AuthInternal, ConfigInternal, TokenRefreshHandler } from '../../model/auth';
5353
import { PopupRedirectResolverInternal } from '../../model/popup_redirect';
5454
import { UserInternal } from '../../model/user';
5555
import {
@@ -102,6 +102,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
102102
currentUser: User | null = null;
103103
emulatorConfig: EmulatorConfig | null = null;
104104
firebaseToken: FirebaseToken | null = null;
105+
tokenRefreshHandler?: TokenRefreshHandler;
105106
private operations = Promise.resolve();
106107
private persistenceManager?: PersistenceUserManager;
107108
private redirectPersistenceManager?: PersistenceUserManager;
@@ -112,6 +113,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
112113
private redirectUser: UserInternal | null = null;
113114
private isProactiveRefreshEnabled = false;
114115
private readonly EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION: number = 1;
116+
private readonly TOKEN_EXPIRATION_BUFFER = 30_000;
115117

116118
// Any network calls will set this to true and prevent subsequent emulator
117119
// initialization
@@ -159,6 +161,10 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
159161
this.tenantConfig = tenantConfig;
160162
}
161163

164+
setTokenRefreshHandler(tokenRefreshHandler: TokenRefreshHandler): void {
165+
this.tokenRefreshHandler = tokenRefreshHandler;
166+
}
167+
162168
_initializeWithPersistence(
163169
persistenceHierarchy: PersistenceInternal[],
164170
popupRedirectResolver?: PopupRedirectResolver
@@ -196,7 +202,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
196202
}
197203

198204
await this.initializeCurrentUser(popupRedirectResolver);
199-
await this.initializeFirebaseToken();
205+
this.firebaseToken = await this.getFirebaseAccessToken(false);
200206

201207
this.lastNotifiedUid = this.currentUser?.uid || null;
202208

@@ -210,6 +216,33 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
210216
return this._initializationPromise;
211217
}
212218

219+
async getFirebaseAccessToken(forceRefresh?: boolean):
220+
Promise<FirebaseToken | null> {
221+
const firebaseAccessToken =
222+
(await this.persistenceManager?.getFirebaseToken()) ?? null;
223+
224+
if (
225+
firebaseAccessToken &&
226+
this.isFirebaseAccessTokenValid(firebaseAccessToken) &&
227+
!forceRefresh
228+
) {
229+
this.firebaseToken = firebaseAccessToken;
230+
this.firebaseTokenSubscription.next(this.firebaseToken);
231+
return firebaseAccessToken;
232+
}
233+
234+
if (firebaseAccessToken && this.tokenRefreshHandler) {
235+
// Resets the Firebase Access Token to null i.e. logs out the user.
236+
await this._updateFirebaseToken(null);
237+
// Awaits for the callback method to execute. The callback method
238+
// is responsible for performing the exchangeToken(auth, valid3pIdpToken)
239+
await this.tokenRefreshHandler.refreshToken();
240+
return this.getFirebaseAccessToken(false);
241+
}
242+
243+
return null;
244+
}
245+
213246
/**
214247
* If the persistence is changed in another window, the user manager will let us know
215248
*/
@@ -240,6 +273,20 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
240273
await this._updateCurrentUser(user, /* skipBeforeStateCallbacks */ true);
241274
}
242275

276+
private isFirebaseAccessTokenValid(
277+
firebaseToken: FirebaseToken | null
278+
): boolean {
279+
if(
280+
firebaseToken &&
281+
firebaseToken.expirationTime &&
282+
(Date.now() >
283+
firebaseToken.expirationTime - this.TOKEN_EXPIRATION_BUFFER)
284+
) {
285+
return false;
286+
}
287+
return true;
288+
}
289+
243290
private async initializeCurrentUserFromIdToken(
244291
idToken: string
245292
): Promise<void> {

packages/auth/src/core/auth/firebase_internal.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,17 @@ interface TokenListener {
2828
(tok: string | null): unknown;
2929
}
3030

31+
// interface FirebaseAccessTokenRefreshListener {
32+
// (tok: string | null): unknown;
33+
// }
34+
3135
export class AuthInterop implements FirebaseAuthInternal {
3236
private readonly TOKEN_EXPIRATION_BUFFER = 30_000;
3337
private readonly internalListeners: Map<TokenListener, Unsubscribe> =
3438
new Map();
39+
// private readonly firebaseAccessTokenRefreshListeners:
40+
// Map<FirebaseAccessTokenRefreshListener, Unsubscribe> =
41+
// new Map();
3542

3643
constructor(private readonly auth: AuthInternal) {}
3744

@@ -51,7 +58,11 @@ export class AuthInterop implements FirebaseAuthInternal {
5158
'Refresh token is not a valid operation for Regional Auth instance initialized.'
5259
);
5360
}
54-
return this.getTokenForRegionalAuth();
61+
const firebaseAccessToken = (await this.auth.getFirebaseAccessToken())?.token
62+
if (!firebaseAccessToken) {
63+
return null;
64+
}
65+
return { accessToken: firebaseAccessToken ?? null};
5566
}
5667
if (!this.auth.currentUser) {
5768
return null;
@@ -76,6 +87,23 @@ export class AuthInterop implements FirebaseAuthInternal {
7687
this.updateProactiveRefresh();
7788
}
7889

90+
// addFirebaseAccessTokenRefreshListener(
91+
// listener: FirebaseAccessTokenRefreshListener): void {
92+
// this.assertAuthConfigured();
93+
// this.assertRegionalAuthConfigured();
94+
// if (this.firebaseAccessTokenRefreshListeners.has(listener)) {
95+
// return;
96+
// }
97+
98+
// const unsubscribe = this.auth.onFirebaseAccessTokenRefreshTriggered(
99+
// firebaseAccessToken => {
100+
// listener(
101+
// (firebaseAccessToken as FirebaseToken)?.token || null
102+
// );
103+
// });
104+
// this.firebaseAccessTokenRefreshListeners.set(listener, unsubscribe);
105+
// }
106+
79107
removeAuthTokenListener(listener: TokenListener): void {
80108
this.assertAuthConfigured();
81109
const unsubscribe = this.internalListeners.get(listener);
@@ -95,6 +123,13 @@ export class AuthInterop implements FirebaseAuthInternal {
95123
);
96124
}
97125

126+
// private assertRegionalAuthConfigured(): void {
127+
// _assert(
128+
// this.auth.tenantConfig,
129+
// AuthErrorCode.DEPENDENT_SDK_INIT_BEFORE_AUTH)
130+
// ;
131+
// }
132+
98133
private updateProactiveRefresh(): void {
99134
if (this.internalListeners.size > 0) {
100135
this.auth._startProactiveRefresh();

packages/auth/src/model/auth.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ export interface ConfigInternal extends Config {
5858
clientPlatform: ClientPlatform;
5959
}
6060

61+
export interface TokenRefreshHandler {
62+
refreshToken(): void | Promise<void>
63+
}
64+
6165
/**
6266
* UserInternal and AuthInternal reference each other, so both of them are included in the public typings.
6367
* In order to exclude them, we mark them as internal explicitly.
@@ -67,6 +71,7 @@ export interface ConfigInternal extends Config {
6771
export interface AuthInternal extends Auth {
6872
currentUser: User | null;
6973
emulatorConfig: EmulatorConfig | null;
74+
tokenRefreshHandler?: TokenRefreshHandler;
7075
_agentRecaptchaConfig: RecaptchaConfig | null;
7176
_tenantRecaptchaConfigs: Record<string, RecaptchaConfig>;
7277
_projectPasswordPolicy: PasswordPolicy | null;
@@ -75,6 +80,7 @@ export interface AuthInternal extends Auth {
7580
_isInitialized: boolean;
7681
_initializationPromise: Promise<void> | null;
7782
_persistenceManagerAvailable: Promise<void>;
83+
getFirebaseAccessToken(forceRefresh?: boolean): Promise<FirebaseToken | null>;
7884
_updateCurrentUser(user: UserInternal | null): Promise<void>;
7985
_updateFirebaseToken(firebaseToken: FirebaseToken | null): Promise<void>;
8086

0 commit comments

Comments
 (0)