Skip to content

Commit 5af4931

Browse files
committed
Implement token refresh mechanism for BYO-CIAM
1 parent a4d5ad7 commit 5af4931

File tree

3 files changed

+55
-2
lines changed

3 files changed

+55
-2
lines changed

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ import {
3838
Unsubscribe,
3939
PasswordValidationStatus,
4040
TenantConfig,
41-
FirebaseToken
41+
FirebaseToken,
42+
RefreshIdpTokenResult,
43+
TokenRefreshHandler
4244
} from '../../model/public_types';
4345
import {
4446
createSubscribe,
@@ -85,6 +87,7 @@ import { PasswordPolicyInternal } from '../../model/password_policy';
8587
import { PasswordPolicyImpl } from './password_policy_impl';
8688
import { getAccountInfo } from '../../api/account_management/account';
8789
import { UserImpl } from '../user/user_impl';
90+
import { exchangeToken } from '../strategies/exhange_token';
8891

8992
interface AsyncAction {
9093
(): Promise<void>;
@@ -102,6 +105,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
102105
currentUser: User | null = null;
103106
emulatorConfig: EmulatorConfig | null = null;
104107
firebaseToken: FirebaseToken | null = null;
108+
private tokenRefreshHandler?: TokenRefreshHandler;
105109
private operations = Promise.resolve();
106110
private persistenceManager?: PersistenceUserManager;
107111
private redirectPersistenceManager?: PersistenceUserManager;
@@ -112,6 +116,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
112116
private redirectUser: UserInternal | null = null;
113117
private isProactiveRefreshEnabled = false;
114118
private readonly EXPECTED_PASSWORD_POLICY_SCHEMA_VERSION: number = 1;
119+
private readonly TOKEN_EXPIRATION_BUFFER = 30_000;
115120

116121
// Any network calls will set this to true and prevent subsequent emulator
117122
// initialization
@@ -210,6 +215,10 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
210215
return this._initializationPromise;
211216
}
212217

218+
setTokenRefreshHandler(tokenRefreshHandler: TokenRefreshHandler): void {
219+
this.tokenRefreshHandler = tokenRefreshHandler;
220+
}
221+
213222
/**
214223
* If the persistence is changed in another window, the user manager will let us know
215224
*/
@@ -240,6 +249,35 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
240249
await this._updateCurrentUser(user, /* skipBeforeStateCallbacks */ true);
241250
}
242251

252+
async getFirebaseAccessToken(forceRefresh?: boolean):
253+
Promise<FirebaseToken | null> {
254+
const firebaseAccessToken =
255+
(await this.persistenceManager?.getFirebaseToken()) ?? null;
256+
257+
if (
258+
firebaseAccessToken &&
259+
this.isFirebaseAccessTokenValid(firebaseAccessToken) &&
260+
!forceRefresh
261+
) {
262+
this.firebaseToken = firebaseAccessToken;
263+
this.firebaseTokenSubscription.next(this.firebaseToken);
264+
return firebaseAccessToken;
265+
}
266+
267+
if (firebaseAccessToken && this.tokenRefreshHandler) {
268+
// Resets the Firebase Access Token to null i.e. logs out the user.
269+
await this._updateFirebaseToken(null);
270+
// Awaits for the callback method to execute. The callback method
271+
// is responsible for performing the exchangeToken(auth, valid3pIdpToken)
272+
const result: RefreshIdpTokenResult = await this.tokenRefreshHandler.refreshIdpToken();
273+
_assert(result.idToken && result.idpConfigId, AuthErrorCode.INVALID_CREDENTIAL);
274+
await exchangeToken(this, result.idpConfigId, result.idToken);
275+
return this.getFirebaseAccessToken(false);
276+
}
277+
278+
return null;
279+
}
280+
243281
private async initializeCurrentUserFromIdToken(
244282
idToken: string
245283
): Promise<void> {
@@ -405,6 +443,20 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
405443
return this.directlySetCurrentUser(user);
406444
}
407445

446+
private isFirebaseAccessTokenValid(
447+
firebaseToken: FirebaseToken | null
448+
): boolean {
449+
if(
450+
firebaseToken &&
451+
firebaseToken.expirationTime &&
452+
(Date.now() >
453+
firebaseToken.expirationTime - this.TOKEN_EXPIRATION_BUFFER)
454+
) {
455+
return false;
456+
}
457+
return true;
458+
}
459+
408460
private async initializeFirebaseToken(): Promise<void> {
409461
this.firebaseToken =
410462
(await this.persistenceManager?.getFirebaseToken()) ?? null;

packages/auth/src/model/auth.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export interface ConfigInternal extends Config {
6767
export interface AuthInternal extends Auth {
6868
currentUser: User | null;
6969
emulatorConfig: EmulatorConfig | null;
70+
getFirebaseAccessToken(forceRefresh?: boolean): Promise<FirebaseToken | null>;
7071
_agentRecaptchaConfig: RecaptchaConfig | null;
7172
_tenantRecaptchaConfigs: Record<string, RecaptchaConfig>;
7273
_projectPasswordPolicy: PasswordPolicy | null;

packages/auth/src/model/public_types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export interface Auth {
274274
* @param tokenRefreshHandler - An object that implements the `TokenRefreshHandler`
275275
* interface, providing the logic to refresh the IDP token.
276276
*/
277-
setTokenRefreshHandler(tokenRefreshHandler: TokenRefreshHandler): void;
277+
setTokenRefreshHandler(tokenRefreshHandler: TokenRefreshHandler): void;
278278

279279
/**
280280
* The {@link Auth} instance's language code.

0 commit comments

Comments
 (0)