Skip to content

Write BYO-CIAM token, expiresIn to the Auth Object initialized #9052

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 18 commits into
base: gcip-byociam-web
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a8426a6
GCIP BYO-CIAM Initialize Auth Changes (#8995)
mansisampat May 8, 2025
c02ca13
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013)
mansisampat May 13, 2025
a3dc91a
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013) (#9019)
mansisampat May 13, 2025
54db430
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013) (#9019)
mansisampat May 13, 2025
251353f
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013) (#9019)
mansisampat May 13, 2025
8690a01
Implement exchangeToken public api
mansisampat May 14, 2025
8965130
Using expiry_in returned by backend
mansisampat May 15, 2025
978f78f
Addressing review comments and adding some Unit test
mansisampat May 22, 2025
d9ef125
Docgen
mansisampat May 22, 2025
ff61e6c
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013)
mansisampat May 13, 2025
537bdc6
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013) (#9019)
mansisampat May 13, 2025
c0cc6eb
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013) (#9019)
mansisampat May 13, 2025
b609765
Implement exchangeToken public api
mansisampat May 14, 2025
28ccf1e
Write BYO-CIAM token, expiresIn to the Auth Object initialized
mansisampat May 19, 2025
ccd5174
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013) (#9019)
mansisampat May 13, 2025
a361d46
Throw Operation Not Allowed for Invalid Auth Endpoint (#9013) (#9019)
mansisampat May 13, 2025
7ca7909
yarn docgen:all
mansisampat May 19, 2025
14eb393
Exchange Token interop changes
mansisampat May 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export interface Auth {
setPersistence(persistence: Persistence): Promise<void>;
readonly settings: AuthSettings;
signOut(): Promise<void>;
readonly tenantConfig?: TenantConfig;
tenantId: string | null;
updateCurrentUser(user: User | null): Promise<void>;
useDeviceLanguage(): void;
Expand Down Expand Up @@ -316,6 +317,7 @@ export interface Dependencies {
errorMap?: AuthErrorMap;
persistence?: Persistence | Persistence[];
popupRedirectResolver?: PopupRedirectResolver;
tenantConfig?: TenantConfig;
}

// @public
Expand Down Expand Up @@ -362,6 +364,9 @@ export interface EmulatorConfig {

export { ErrorFn }

// @public (undocumented)
export function exchangeToken(auth: Auth, idpConfigId: string, customToken: string): Promise<string>;

// Warning: (ae-forgotten-export) The symbol "BaseOAuthProvider" needs to be exported by the entry point index.d.ts
//
// @public
Expand Down Expand Up @@ -795,6 +800,12 @@ export function signInWithRedirect(auth: Auth, provider: AuthProvider, resolver?
// @public
export function signOut(auth: Auth): Promise<void>;

// @public
export interface TenantConfig {
location: string;
tenantId: string;
}

// @public
export interface TotpMultiFactorAssertion extends MultiFactorAssertion {
}
Expand Down
4 changes: 4 additions & 0 deletions docs-devsite/_toc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ toc:
path: /docs/reference/js/auth.recaptchaverifier.md
- title: SAMLAuthProvider
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
Expand Down
24 changes: 24 additions & 0 deletions docs-devsite/auth.auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export interface Auth
| [languageCode](./auth.auth.md#authlanguagecode) | string \| null | The [Auth](./auth.auth.md#auth_interface) instance's language code. |
| [name](./auth.auth.md#authname) | string | The name of the app associated with the <code>Auth</code> service instance. |
| [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 <code>DefaultConfig.REGIONAL_API_HOST</code> 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

Expand Down Expand Up @@ -120,6 +122,16 @@ This is used to edit/read configuration related options such as app verification
readonly settings: AuthSettings;
```

## Auth.tenantConfig

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.

<b>Signature:</b>

```typescript
readonly tenantConfig?: TenantConfig;
```

## Auth.tenantId

The [Auth](./auth.auth.md#auth_interface) instance's tenant ID.
Expand All @@ -145,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)<!-- -->.

<b>Signature:</b>

```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.
Expand Down
11 changes: 11 additions & 0 deletions docs-devsite/auth.dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface Dependencies
| [errorMap](./auth.dependencies.md#dependencieserrormap) | [AuthErrorMap](./auth.autherrormap.md#autherrormap_interface) | Which [AuthErrorMap](./auth.autherrormap.md#autherrormap_interface) to use. |
| [persistence](./auth.dependencies.md#dependenciespersistence) | [Persistence](./auth.persistence.md#persistence_interface) \| [Persistence](./auth.persistence.md#persistence_interface)<!-- -->\[\] | Which [Persistence](./auth.persistence.md#persistence_interface) to use. If this is an array, the first <code>Persistence</code> that the device supports is used. The SDK searches for an existing account in order and, if one is found in a secondary <code>Persistence</code>, the account is moved to the primary <code>Persistence</code>.<!-- -->If no persistence is provided, the SDK falls back on [inMemoryPersistence](./auth.md#inmemorypersistence)<!-- -->. |
| [popupRedirectResolver](./auth.dependencies.md#dependenciespopupredirectresolver) | [PopupRedirectResolver](./auth.popupredirectresolver.md#popupredirectresolver_interface) | The [PopupRedirectResolver](./auth.popupredirectresolver.md#popupredirectresolver_interface) to use. This value depends on the platform. Options are [browserPopupRedirectResolver](./auth.md#browserpopupredirectresolver) and [cordovaPopupRedirectResolver](./auth.md#cordovapopupredirectresolver)<!-- -->. This field is optional if neither [signInWithPopup()](./auth.md#signinwithpopup_770f816) or [signInWithRedirect()](./auth.md#signinwithredirect_770f816) are being used. |
| [tenantConfig](./auth.dependencies.md#dependenciestenantconfig) | [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface) | The [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface) to use. This dependency is only required if you want to use regional auth which works with <code>DefaultConfig.REGIONAL_API_HOST</code>\` endpoint. It should not be set otherwise. |

## Dependencies.errorMap

Expand Down Expand Up @@ -61,3 +62,13 @@ The [PopupRedirectResolver](./auth.popupredirectresolver.md#popupredirectresolve
```typescript
popupRedirectResolver?: PopupRedirectResolver;
```

## Dependencies.tenantConfig

The [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface) to use. This dependency is only required if you want to use regional auth which works with `DefaultConfig.REGIONAL_API_HOST` endpoint. It should not be set otherwise.

<b>Signature:</b>

```typescript
tenantConfig?: TenantConfig;
```
31 changes: 31 additions & 0 deletions docs-devsite/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Firebase Authentication
| [confirmPasswordReset(auth, oobCode, newPassword)](./auth.md#confirmpasswordreset_749dad8) | Completes the password reset process, given a confirmation code and new password. |
| [connectAuthEmulator(auth, url, options)](./auth.md#connectauthemulator_657c7e5) | Changes the [Auth](./auth.auth.md#auth_interface) instance to communicate with the Firebase Auth Emulator, instead of production Firebase Auth services. |
| [createUserWithEmailAndPassword(auth, email, password)](./auth.md#createuserwithemailandpassword_21ad33b) | Creates a new user account associated with the specified email address and password. |
| [exchangeToken(auth, idpConfigId, customToken)](./auth.md#exchangetoken_b6b1871) | Asynchronously exchanges an OIDC provider's Authorization code or Id Token for an OidcToken i.e. Outbound Access Token. |
| [fetchSignInMethodsForEmail(auth, email)](./auth.md#fetchsigninmethodsforemail_efb3887) | Gets the list of possible sign in methods for the given email address. This method returns an empty list when [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled, irrespective of the number of authentication methods available for the given email. |
| [getMultiFactorResolver(auth, error)](./auth.md#getmultifactorresolver_201ba61) | Provides a [MultiFactorResolver](./auth.multifactorresolver.md#multifactorresolver_interface) suitable for completion of a multi-factor flow. |
| [getRedirectResult(auth, resolver)](./auth.md#getredirectresult_c35dc1f) | Returns a [UserCredential](./auth.usercredential.md#usercredential_interface) from the redirect-based sign-in flow. |
Expand Down Expand Up @@ -137,6 +138,8 @@ Firebase Authentication
| [PopupRedirectResolver](./auth.popupredirectresolver.md#popupredirectresolver_interface) | A resolver used for handling DOM specific operations like [signInWithPopup()](./auth.md#signinwithpopup_770f816) or [signInWithRedirect()](./auth.md#signinwithredirect_770f816)<!-- -->. |
| [ReactNativeAsyncStorage](./auth.reactnativeasyncstorage.md#reactnativeasyncstorage_interface) | Interface for a supplied <code>AsyncStorage</code>. |
| [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 <code>sitekey</code>: Firebase Auth provisions a reCAPTCHA for each project and will configure the site key upon rendering.<!-- -->For an invisible reCAPTCHA, set the <code>size</code> key to <code>invisible</code>. |
| [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 <code>factorId</code> of this second factor is [FactorId](./auth.md#factorid)<!-- -->.TOTP. |
| [User](./auth.user.md#user_interface) | A user account. |
Expand Down Expand Up @@ -404,6 +407,34 @@ export declare function createUserWithEmailAndPassword(auth: Auth, email: string

Promise&lt;[UserCredential](./auth.usercredential.md#usercredential_interface)<!-- -->&gt;

### exchangeToken(auth, idpConfigId, customToken) {:#exchangetoken_b6b1871}

Asynchronously exchanges an OIDC provider's Authorization code or Id Token for an OidcToken i.e. Outbound Access Token.

This method is implemented only for `DefaultConfig.REGIONAL_API_HOST` and requires [TenantConfig](./auth.tenantconfig.md#tenantconfig_interface) to be configured in the [Auth](./auth.auth.md#auth_interface) instance used.

Fails with an error if the token is invalid, expired, or not accepted by the Firebase Auth service.

<b>Signature:</b>

```typescript
export declare function exchangeToken(auth: Auth, idpConfigId: string, customToken: string): Promise<string>;
```

#### Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| auth | [Auth](./auth.auth.md#auth_interface) | The [Auth](./auth.auth.md#auth_interface) instance. |
| idpConfigId | string | The ExternalUserDirectoryId corresponding to the OIDC custom Token. |
| customToken | string | The OIDC provider's Authorization code or Id Token to exchange. |

<b>Returns:</b>

Promise&lt;string&gt;

The firebase access token (JWT signed by Firebase Auth).

### fetchSignInMethodsForEmail(auth, email) {:#fetchsigninmethodsforemail_efb3887}

Gets the list of possible sign in methods for the given email address. This method returns an empty list when [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled, irrespective of the number of authentication methods available for the given email.
Expand Down
46 changes: 46 additions & 0 deletions docs-devsite/auth.tenantconfig.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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 %}

# TenantConfig interface
The tenant config that can be used to initialize a Regional [Auth](./auth.auth.md#auth_interface) instance.

<b>Signature:</b>

```typescript
export interface TenantConfig
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [location](./auth.tenantconfig.md#tenantconfiglocation) | string | Which location to use. |
| [tenantId](./auth.tenantconfig.md#tenantconfigtenantid) | string | The tenant Id being used. |

## TenantConfig.location

Which location to use.

<b>Signature:</b>

```typescript
location: string;
```

## TenantConfig.tenantId

The tenant Id being used.

<b>Signature:</b>

```typescript
tenantId: string;
```
42 changes: 42 additions & 0 deletions docs-devsite/auth.tokenresponse.md
Original file line number Diff line number Diff line change
@@ -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)<!-- -->.

<b>Signature:</b>

```typescript
export interface TokenResponse
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [expiresIn](./auth.tokenresponse.md#tokenresponseexpiresin) | string | |
| [token](./auth.tokenresponse.md#tokenresponsetoken) | string | |

## TokenResponse.expiresIn

<b>Signature:</b>

```typescript
readonly expiresIn: string;
```

## TokenResponse.token

<b>Signature:</b>

```typescript
readonly token: string;
```
102 changes: 102 additions & 0 deletions packages/auth/src/api/authentication/exchange_token.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* @license
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { expect, use } from 'chai';
import chaiAsPromised from 'chai-as-promised';

import {
regionalTestAuth,
testAuth,
TestAuth
} from '../../../test/helpers/mock_auth';
import * as mockFetch from '../../../test/helpers/mock_fetch';
import { mockRegionalEndpointWithParent } from '../../../test/helpers/api/helper';
import { exchangeToken } from './exchange_token';
import { HttpHeader, RegionalEndpoint } from '..';
import { FirebaseError } from '@firebase/util';
import { ServerError } from '../errors';

use(chaiAsPromised);

describe('api/authentication/exchange_token', () => {
let auth: TestAuth;
let regionalAuth: TestAuth;
const request = {
parent: 'test-parent',
token: 'custom-token'
};

beforeEach(async () => {
auth = await testAuth();
regionalAuth = await regionalTestAuth();
mockFetch.setUp();
});

afterEach(mockFetch.tearDown);

it('returns accesss token for Regional Auth', async () => {
const mock = mockRegionalEndpointWithParent(
RegionalEndpoint.EXCHANGE_TOKEN,
'test-parent',
{ accessToken: 'outbound-token', expiresIn: '1000' }
);

const response = await exchangeToken(regionalAuth, request);
expect(response.accessToken).equal('outbound-token');
expect(response.expiresIn).equal('1000');
expect(mock.calls[0].request).to.eql({
parent: 'test-parent',
token: 'custom-token'
});
expect(mock.calls[0].method).to.eq('POST');
expect(mock.calls[0].headers!.get(HttpHeader.CONTENT_TYPE)).to.eq(
'application/json'
);
});

it('throws exception for default Auth', async () => {
await expect(exchangeToken(auth, request)).to.be.rejectedWith(
FirebaseError,
'Firebase: Operations not allowed for the auth object initialized. (auth/operation-not-allowed).'
);
});

it('should handle errors', async () => {
const mock = mockRegionalEndpointWithParent(
RegionalEndpoint.EXCHANGE_TOKEN,
'test-parent',
{
error: {
code: 400,
message: ServerError.INVALID_CUSTOM_TOKEN,
errors: [
{
message: ServerError.INVALID_CUSTOM_TOKEN
}
]
}
},
400
);

await expect(exchangeToken(regionalAuth, request)).to.be.rejectedWith(
FirebaseError,
'(auth/invalid-custom-token).'
);
expect(mock.calls[0].request).to.eql(request);
});
});
Loading
Loading