diff --git a/docs-devsite/_toc.yaml b/docs-devsite/_toc.yaml
index 088facef70f..4515bb46fb7 100644
--- a/docs-devsite/_toc.yaml
+++ b/docs-devsite/_toc.yaml
@@ -306,6 +306,8 @@ toc:
path: /docs/reference/js/auth.samlauthprovider.md
- title: TenantConfig
path: /docs/reference/js/auth.tenantconfig.md
+ - title: TokenResponse
+ path: /docs/reference/js/auth.tokenresponse.md
- title: TotpMultiFactorAssertion
path: /docs/reference/js/auth.totpmultifactorassertion.md
- title: TotpMultiFactorGenerator
diff --git a/docs-devsite/auth.auth.md b/docs-devsite/auth.auth.md
index 170447bc43c..502cf53628e 100644
--- a/docs-devsite/auth.auth.md
+++ b/docs-devsite/auth.auth.md
@@ -33,6 +33,7 @@ export interface Auth
| [settings](./auth.auth.md#authsettings) | [AuthSettings](./auth.authsettings.md#authsettings_interface) | The [Auth](./auth.auth.md#auth_interface) instance's settings. |
| [tenantConfig](./auth.auth.md#authtenantconfig) | [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface) | The [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface) used to initialize a Regional Auth. This is only present if regional auth is initialized and DefaultConfig.REGIONAL_API_HOST
backend endpoint is used. |
| [tenantId](./auth.auth.md#authtenantid) | string \| null | The [Auth](./auth.auth.md#auth_interface) instance's tenant ID. |
+| [tokenResponse](./auth.auth.md#authtokenresponse) | [TokenResponse](./auth.tokenresponse.md#tokenresponse_interface) \| null | The token response initialized via [exchangeToken()](./auth.md#exchangetoken_b6b1871) endpoint. |
## Methods
@@ -156,6 +157,18 @@ const result = await signInWithEmailAndPassword(auth, email, password);
```
+## Auth.tokenResponse
+
+The token response initialized via [exchangeToken()](./auth.md#exchangetoken_b6b1871) endpoint.
+
+This field is only supported for [Auth](./auth.auth.md#auth_interface) instance that have defined [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface).
+
+Signature:
+
+```typescript
+readonly tokenResponse: TokenResponse | null;
+```
+
## Auth.authStateReady()
returns a promise that resolves immediately when the initial auth state is settled. When the promise resolves, the current user might be a valid user or `null` if the user signed out.
diff --git a/docs-devsite/auth.md b/docs-devsite/auth.md
index ab460d58e51..4e357662166 100644
--- a/docs-devsite/auth.md
+++ b/docs-devsite/auth.md
@@ -139,6 +139,7 @@ Firebase Authentication
| [ReactNativeAsyncStorage](./auth.reactnativeasyncstorage.md#reactnativeasyncstorage_interface) | Interface for a supplied AsyncStorage
. |
| [RecaptchaParameters](./auth.recaptchaparameters.md#recaptchaparameters_interface) | Interface representing reCAPTCHA parameters.See the [reCAPTCHA docs](https://developers.google.com/recaptcha/docs/display#render_param) for the list of accepted parameters. All parameters are accepted except for sitekey
: Firebase Auth provisions a reCAPTCHA for each project and will configure the site key upon rendering.For an invisible reCAPTCHA, set the size
key to invisible
. |
| [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface) | The tenant config that can be used to initialize a Regional [Auth](./auth.auth.md#auth_interface) instance. |
+| [TokenResponse](./auth.tokenresponse.md#tokenresponse_interface) | Interface for TokenRespone returned via [exchangeToken()](./auth.md#exchangetoken_b6b1871) endpoint. This is expected to be returned only if [Auth](./auth.auth.md#auth_interface) object initialized has defined [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface). |
| [TotpMultiFactorAssertion](./auth.totpmultifactorassertion.md#totpmultifactorassertion_interface) | The class for asserting ownership of a TOTP second factor. Provided by [TotpMultiFactorGenerator.assertionForEnrollment()](./auth.totpmultifactorgenerator.md#totpmultifactorgeneratorassertionforenrollment) and [TotpMultiFactorGenerator.assertionForSignIn()](./auth.totpmultifactorgenerator.md#totpmultifactorgeneratorassertionforsignin). |
| [TotpMultiFactorInfo](./auth.totpmultifactorinfo.md#totpmultifactorinfo_interface) | The subclass of the [MultiFactorInfo](./auth.multifactorinfo.md#multifactorinfo_interface) interface for TOTP second factors. The factorId
of this second factor is [FactorId](./auth.md#factorid).TOTP. |
| [User](./auth.user.md#user_interface) | A user account. |
diff --git a/docs-devsite/auth.tokenresponse.md b/docs-devsite/auth.tokenresponse.md
new file mode 100644
index 00000000000..540b529cd7d
--- /dev/null
+++ b/docs-devsite/auth.tokenresponse.md
@@ -0,0 +1,42 @@
+Project: /docs/reference/js/_project.yaml
+Book: /docs/reference/_book.yaml
+page_type: reference
+
+{% comment %}
+DO NOT EDIT THIS FILE!
+This is generated by the JS SDK team, and any local changes will be
+overwritten. Changes should be made in the source code at
+https://github.com/firebase/firebase-js-sdk
+{% endcomment %}
+
+# TokenResponse interface
+Interface for TokenRespone returned via [exchangeToken()](./auth.md#exchangetoken_b6b1871) endpoint. This is expected to be returned only if [Auth](./auth.auth.md#auth_interface) object initialized has defined [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface).
+
+Signature:
+
+```typescript
+export interface TokenResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [expiresIn](./auth.tokenresponse.md#tokenresponseexpiresin) | string | |
+| [token](./auth.tokenresponse.md#tokenresponsetoken) | string | |
+
+## TokenResponse.expiresIn
+
+Signature:
+
+```typescript
+readonly expiresIn: string;
+```
+
+## TokenResponse.token
+
+Signature:
+
+```typescript
+readonly token: string;
+```
diff --git a/packages/auth/src/api/authentication/exchange_token.ts b/packages/auth/src/api/authentication/exchange_token.ts
index dde1b8f9b2f..88fc32af54a 100644
--- a/packages/auth/src/api/authentication/exchange_token.ts
+++ b/packages/auth/src/api/authentication/exchange_token.ts
@@ -28,7 +28,7 @@ export interface ExchangeTokenRequest {
export interface ExchangeTokenRespose {
accessToken: string;
- expiresIn?: string;
+ expiresInSec: string;
}
export async function exchangeToken(
diff --git a/packages/auth/src/core/auth/auth_impl.ts b/packages/auth/src/core/auth/auth_impl.ts
index 8aac4fb12f4..5fcaf2eb1ca 100644
--- a/packages/auth/src/core/auth/auth_impl.ts
+++ b/packages/auth/src/core/auth/auth_impl.ts
@@ -37,7 +37,8 @@ import {
NextFn,
Unsubscribe,
PasswordValidationStatus,
- TenantConfig
+ TenantConfig,
+ TokenResponse
} from '../../model/public_types';
import {
createSubscribe,
@@ -99,6 +100,7 @@ export const enum DefaultConfig {
export class AuthImpl implements AuthInternal, _FirebaseService {
currentUser: User | null = null;
emulatorConfig: EmulatorConfig | null = null;
+ tokenResponse: TokenResponse | null = null;
private operations = Promise.resolve();
private persistenceManager?: PersistenceUserManager;
private redirectPersistenceManager?: PersistenceUserManager;
@@ -454,6 +456,12 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
});
}
+ async _updateTokenResponse(tokenResponse: TokenResponse): Promise {
+ if (tokenResponse) {
+ this.tokenResponse = tokenResponse;
+ }
+ }
+
async signOut(): Promise {
if (_isFirebaseServerApp(this.app)) {
return Promise.reject(
@@ -621,6 +629,11 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
}
}
+ async getTokenForRegionalAuth():
+ Promise {
+ if (Date.now() > this.tokenResponse?.expiresIn)
+ }
+
toJSON(): object {
return {
apiKey: this.config.apiKey,
diff --git a/packages/auth/src/core/auth/firebase_internal.ts b/packages/auth/src/core/auth/firebase_internal.ts
index 4fad0754375..9c267d9cc33 100644
--- a/packages/auth/src/core/auth/firebase_internal.ts
+++ b/packages/auth/src/core/auth/firebase_internal.ts
@@ -43,6 +43,9 @@ export class AuthInterop implements FirebaseAuthInternal {
): Promise<{ accessToken: string } | null> {
this.assertAuthConfigured();
await this.auth._initializationPromise;
+ if (this.auth.tenantConfig) {
+ return this.getTokenRegionalAuth();
+ }
if (!this.auth.currentUser) {
return null;
}
@@ -51,6 +54,23 @@ export class AuthInterop implements FirebaseAuthInternal {
return { accessToken };
}
+ async getTokenRegionalAuth() :
+ Promise<{ accessToken: string } | null> {
+ this.assertRegionalAuthConfigured();
+ if (!this.auth.tokenResponse) {
+ return null;
+ }
+
+ if (!this.auth.tokenResponse.expirationTime ||
+ Date.now() > this.auth.tokenResponse.expirationTime) {
+ await this.auth._updateTokenResponse(null);
+ return null;
+ }
+
+ const accessToken = await this.auth.tokenResponse.token;
+ return { accessToken };
+ }
+
addAuthTokenListener(listener: TokenListener): void {
this.assertAuthConfigured();
if (this.internalListeners.has(listener)) {
@@ -85,6 +105,10 @@ export class AuthInterop implements FirebaseAuthInternal {
);
}
+ private assertRegionalAuthConfigured(): void {
+ _assert(this.auth.tenantConfig, AuthErrorCode.OPERATION_NOT_ALLOWED);
+ }
+
private updateProactiveRefresh(): void {
if (this.internalListeners.size > 0) {
this.auth._startProactiveRefresh();
diff --git a/packages/auth/src/core/strategies/exchange_token.test.ts b/packages/auth/src/core/strategies/exchange_token.test.ts
index 60166034b64..1548735de2d 100644
--- a/packages/auth/src/core/strategies/exchange_token.test.ts
+++ b/packages/auth/src/core/strategies/exchange_token.test.ts
@@ -61,6 +61,8 @@ describe('core/strategies/exchangeToken', () => {
'projects/test-project-id/locations/us/tenants/tenant-1/idpConfigs/idp-config',
token: 'custom-token'
});
+ expect(regionalAuth.tokenResponse?.token).to.equal('outbound-token');
+ expect(regionalAuth.tokenResponse?.expiresIn).to.equal('1000');
expect(mock.calls[0].method).to.eq('POST');
expect(mock.calls[0].headers!.get(HttpHeader.CONTENT_TYPE)).to.eq(
'application/json'
diff --git a/packages/auth/src/core/strategies/exhange_token.ts b/packages/auth/src/core/strategies/exhange_token.ts
index 8b2c37a2b93..c5fb88816a2 100644
--- a/packages/auth/src/core/strategies/exhange_token.ts
+++ b/packages/auth/src/core/strategies/exhange_token.ts
@@ -54,7 +54,12 @@ export async function exchangeToken(
parent: buildParent(auth, idpConfigId),
token: customToken
});
- // TODO(sammansi): Write token to the Auth object passed.
+ if (token) {
+ await authInternal._updateTokenResponse({
+ token: token.accessToken,
+ expirationTime: Date.now() + Number(token.expiresInSec) * 1000
+ });
+ }
return token.accessToken;
}
diff --git a/packages/auth/src/model/auth.ts b/packages/auth/src/model/auth.ts
index 60faedef4e6..4928c17ad5a 100644
--- a/packages/auth/src/model/auth.ts
+++ b/packages/auth/src/model/auth.ts
@@ -24,6 +24,7 @@ import {
PasswordValidationStatus,
PopupRedirectResolver,
TenantConfig,
+ TokenResponse,
User
} from './public_types';
import { ErrorFactory } from '@firebase/util';
@@ -66,6 +67,7 @@ export interface ConfigInternal extends Config {
export interface AuthInternal extends Auth {
currentUser: User | null;
emulatorConfig: EmulatorConfig | null;
+ tokenResponse: TokenResponse | null;
_agentRecaptchaConfig: RecaptchaConfig | null;
_tenantRecaptchaConfigs: Record;
_projectPasswordPolicy: PasswordPolicy | null;
@@ -75,6 +77,7 @@ export interface AuthInternal extends Auth {
_initializationPromise: Promise | null;
_persistenceManagerAvailable: Promise;
_updateCurrentUser(user: UserInternal | null): Promise;
+ _updateTokenResponse(tokenResponse: TokenResponse | null): Promise;
_onStorageEvent(): void;
diff --git a/packages/auth/src/model/public_types.ts b/packages/auth/src/model/public_types.ts
index e7275aeee4c..99fec4e9a5e 100644
--- a/packages/auth/src/model/public_types.ts
+++ b/packages/auth/src/model/public_types.ts
@@ -334,6 +334,14 @@ export interface Auth {
* {@link @firebase/app#FirebaseServerApp}.
*/
signOut(): Promise;
+ /**
+ * The token response initialized via {@link exchangeToken} endpoint.
+ *
+ * @remarks
+ * This field is only supported for {@link Auth} instance that have defined
+ * {@link TenantConfig}.
+ */
+ readonly tokenResponse: TokenResponse | null;
}
/**
@@ -966,6 +974,18 @@ export interface ReactNativeAsyncStorage {
removeItem(key: string): Promise;
}
+/**
+ * Interface for TokenRespone returned via {@link exchangeToken} endpoint.
+ * This is expected to be returned only if {@link Auth} object initialized
+ * has defined {@link TenantConfig}.
+ */
+export interface TokenResponse {
+ // The firebase access token (JWT signed by Firebase Auth).
+ readonly token: string;
+ // The time when the access token expires.
+ readonly expirationTime: number;
+}
+
/**
* A user account.
*
diff --git a/packages/auth/test/helpers/api/helper.ts b/packages/auth/test/helpers/api/helper.ts
index 1877371bf2e..00680be3341 100644
--- a/packages/auth/test/helpers/api/helper.ts
+++ b/packages/auth/test/helpers/api/helper.ts
@@ -63,6 +63,5 @@ export function mockRegionalEndpointWithParent(
status = 200
): Route {
const url = `${TEST_SCHEME}://${TEST_HOST}${parent}${endpoint}`;
- console.log('here ', url);
return mock(url, response, status);
}