Skip to content

Add linkDomain field to ActionCodeSettings #8428

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

Merged
merged 4 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface ActionCodeSettings {
iOS?: {
bundleId: string;
};
linkDomain?: string;
url: string;
}

Expand Down Expand Up @@ -236,6 +237,7 @@ export const AuthErrorCodes: {
readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version";
readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version";
readonly INVALID_REQ_TYPE: "auth/invalid-req-type";
readonly INVALID_HOSTING_LINK_DOMAIN: "auth/invalid-hosting-link-domain";
};

// @public
Expand Down
13 changes: 12 additions & 1 deletion docs-devsite/auth.actioncodesettings.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface ActionCodeSettings
| [dynamicLinkDomain](./auth.actioncodesettings.md#actioncodesettingsdynamiclinkdomain) | string | When multiple custom dynamic link domains are defined for a project, specify which one to use when the link is to be opened via a specified mobile app (for example, <code>example.page.link</code>). |
| [handleCodeInApp](./auth.actioncodesettings.md#actioncodesettingshandlecodeinapp) | boolean | When set to true, the action code link will be be sent as a Universal Link or Android App Link and will be opened by the app if installed. |
| [iOS](./auth.actioncodesettings.md#actioncodesettingsios) | { bundleId: string; } | Sets the iOS bundle ID. |
| [linkDomain](./auth.actioncodesettings.md#actioncodesettingslinkdomain) | string | The optional custom Firebase Hosting domain to use when the link is to be opened via a specified mobile app. The domain must be configured in Firebase Hosting and owned by the project. This cannot be a default hosting domain (web.app or firebaseapp.com). |
| [url](./auth.actioncodesettings.md#actioncodesettingsurl) | string | Sets the link continue/state URL. |

## ActionCodeSettings.android
Expand Down Expand Up @@ -82,11 +83,21 @@ iOS?: {
};
```

## ActionCodeSettings.linkDomain

The optional custom Firebase Hosting domain to use when the link is to be opened via a specified mobile app. The domain must be configured in Firebase Hosting and owned by the project. This cannot be a default hosting domain (web.app or firebaseapp.com).

<b>Signature:</b>

```typescript
linkDomain?: string;
```

## ActionCodeSettings.url

Sets the link continue/state URL.

This has different meanings in different contexts: - When the link is handled in the web action widgets, this is the deep link in the `continueUrl` query parameter. - When the link is handled in the app directly, this is the `continueUrl` query parameter in the deep link of the Dynamic Link.
This has different meanings in different contexts: - When the link is handled in the web action widgets, this is the deep link in the `continueUrl` query parameter. - When the link is handled in the app directly, this is the `continueUrl` query parameter in the deep link of the Dynamic Link or Hosting Link.

<b>Signature:</b>

Expand Down
1 change: 1 addition & 0 deletions docs-devsite/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -1954,6 +1954,7 @@ AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY: {
readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version";
readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version";
readonly INVALID_REQ_TYPE: "auth/invalid-req-type";
readonly INVALID_HOSTING_LINK_DOMAIN: "auth/invalid-hosting-link-domain";
}
```

Expand Down
1 change: 1 addition & 0 deletions packages/auth-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export type ActionCodeSettings = {
iOS?: { bundleId: string };
url: string;
dynamicLinkDomain?: string;
linkDomain?: string;
};

export type AdditionalUserInfo = {
Expand Down
1 change: 1 addition & 0 deletions packages/auth/src/api/authentication/email_and_password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface GetOobCodeRequest {
dynamicLinkDomain?: string;
tenantId?: string;
targetProjectid?: string;
linkDomain?: string;
}

export interface VerifyEmailRequest extends GetOobCodeRequest {
Expand Down
3 changes: 2 additions & 1 deletion packages/auth/src/api/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ export const enum ServerError {
MISSING_RECAPTCHA_VERSION = 'MISSING_RECAPTCHA_VERSION',
INVALID_RECAPTCHA_VERSION = 'INVALID_RECAPTCHA_VERSION',
INVALID_REQ_TYPE = 'INVALID_REQ_TYPE',
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'PASSWORD_DOES_NOT_MEET_REQUIREMENTS'
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'PASSWORD_DOES_NOT_MEET_REQUIREMENTS',
INVALID_HOSTING_LINK_DOMAIN = 'INVALID_HOSTING_LINK_DOMAIN'
}

/**
Expand Down
11 changes: 8 additions & 3 deletions packages/auth/src/core/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ export const enum AuthErrorCode {
INVALID_RECAPTCHA_VERSION = 'invalid-recaptcha-version',
INVALID_REQ_TYPE = 'invalid-req-type',
UNSUPPORTED_PASSWORD_POLICY_SCHEMA_VERSION = 'unsupported-password-policy-schema-version',
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'password-does-not-meet-requirements'
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'password-does-not-meet-requirements',
INVALID_HOSTING_LINK_DOMAIN = 'invalid-hosting-link-domain'
}

function _debugErrorMap(): ErrorMap<AuthErrorCode> {
Expand Down Expand Up @@ -387,7 +388,10 @@ function _debugErrorMap(): ErrorMap<AuthErrorCode> {
[AuthErrorCode.UNSUPPORTED_PASSWORD_POLICY_SCHEMA_VERSION]:
'The password policy received from the backend uses a schema version that is not supported by this version of the Firebase SDK.',
[AuthErrorCode.PASSWORD_DOES_NOT_MEET_REQUIREMENTS]:
'The password does not meet the requirements.'
'The password does not meet the requirements.',
[AuthErrorCode.INVALID_HOSTING_LINK_DOMAIN]:
'The provided hosting link domain is not configured in Firebase Hosting or is not owned by ' +
'the current project. This cannot be a default hosting domain (web.app or firebaseapp.com).'
};
}

Expand Down Expand Up @@ -598,5 +602,6 @@ export const AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY = {
MISSING_CLIENT_TYPE: 'auth/missing-client-type',
MISSING_RECAPTCHA_VERSION: 'auth/missing-recaptcha-version',
INVALID_RECAPTCHA_VERSION: 'auth/invalid-recaptcha-version',
INVALID_REQ_TYPE: 'auth/invalid-req-type'
INVALID_REQ_TYPE: 'auth/invalid-req-type',
INVALID_HOSTING_LINK_DOMAIN: 'auth/invalid-hosting-link-domain'
} as const;
37 changes: 27 additions & 10 deletions packages/auth/src/core/strategies/action_code_settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ describe('core/strategies/action_code_settings', () => {
let auth: TestAuth;
const request: GetOobCodeRequest = {};

const TEST_BUNDLE_ID = 'my-bundle';
const TEST_FDL_DOMAIN = 'fdl-domain';
const TEST_URL = 'my-url';

beforeEach(async () => {
auth = await testAuth();
});
Expand All @@ -35,10 +39,10 @@ describe('core/strategies/action_code_settings', () => {
_setActionCodeSettingsOnRequest(auth, request, {
handleCodeInApp: true,
iOS: {
bundleId: 'my-bundle'
bundleId: TEST_BUNDLE_ID
},
url: '',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: TEST_FDL_DOMAIN
})
).to.throw(FirebaseError, '(auth/invalid-continue-uri)');
});
Expand All @@ -48,9 +52,9 @@ describe('core/strategies/action_code_settings', () => {
_setActionCodeSettingsOnRequest(auth, request, {
handleCodeInApp: true,
iOS: {
bundleId: 'my-´bundle'
bundleId: TEST_BUNDLE_ID
},
url: 'my-url'
url: TEST_URL
})
).to.not.throw();
});
Expand All @@ -60,23 +64,36 @@ describe('core/strategies/action_code_settings', () => {
_setActionCodeSettingsOnRequest(auth, request, {
handleCodeInApp: true,
iOS: {
bundleId: 'my-´bundle'
bundleId: TEST_BUNDLE_ID
},
url: 'my-url',
url: TEST_URL,
dynamicLinkDomain: ''
})
).to.throw(FirebaseError, '(auth/invalid-dynamic-link-domain)');
});

it('should require a non empty hosting link URL', () => {
expect(() =>
_setActionCodeSettingsOnRequest(auth, request, {
handleCodeInApp: true,
iOS: {
bundleId: TEST_BUNDLE_ID
},
url: TEST_URL,
linkDomain: ''
})
).to.throw(FirebaseError, '(auth/invalid-hosting-link-domain)');
});

it('should require a non-empty bundle ID', () => {
expect(() =>
_setActionCodeSettingsOnRequest(auth, request, {
handleCodeInApp: true,
iOS: {
bundleId: ''
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
url: TEST_URL,
dynamicLinkDomain: TEST_FDL_DOMAIN
})
).to.throw(FirebaseError, '(auth/missing-ios-bundle-id)');
});
Expand All @@ -88,8 +105,8 @@ describe('core/strategies/action_code_settings', () => {
android: {
packageName: ''
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
url: TEST_URL,
dynamicLinkDomain: TEST_FDL_DOMAIN
})
).to.throw(FirebaseError, '(auth/missing-android-pkg-name)');
});
Expand Down
7 changes: 7 additions & 0 deletions packages/auth/src/core/strategies/action_code_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,16 @@ export function _setActionCodeSettingsOnRequest(
auth,
AuthErrorCode.INVALID_DYNAMIC_LINK_DOMAIN
);
_assert(
typeof actionCodeSettings.linkDomain === 'undefined' ||
actionCodeSettings.linkDomain.length > 0,
auth,
AuthErrorCode.INVALID_HOSTING_LINK_DOMAIN
);

request.continueUrl = actionCodeSettings.url;
request.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;
request.linkDomain = actionCodeSettings.linkDomain;
request.canHandleCodeInApp = actionCodeSettings.handleCodeInApp;

if (actionCodeSettings.iOS) {
Expand Down
16 changes: 12 additions & 4 deletions packages/auth/src/core/strategies/email.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,16 @@ describe('core/strategies/sendEmailVerification', () => {
bundleId: 'my-bundle'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});

expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.VERIFY_EMAIL,
idToken,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
iOSBundleId: 'my-bundle'
});
Expand All @@ -190,13 +192,15 @@ describe('core/strategies/sendEmailVerification', () => {
packageName: 'my-package'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});
expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.VERIFY_EMAIL,
idToken,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
androidInstallApp: false,
androidMinimumVersionCode: 'my-version',
Expand Down Expand Up @@ -270,7 +274,8 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
bundleId: 'my-bundle'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});

expect(mock.calls[0].request).to.eql({
Expand All @@ -279,6 +284,7 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
newEmail,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
iOSBundleId: 'my-bundle'
});
Expand All @@ -299,14 +305,16 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
packageName: 'my-package'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});
expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL,
idToken,
newEmail,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
androidInstallApp: false,
androidMinimumVersionCode: 'my-version',
Expand Down
8 changes: 6 additions & 2 deletions packages/auth/src/core/strategies/email_and_password.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,16 @@ describe('core/strategies/sendPasswordResetEmail', () => {
bundleId: 'my-bundle'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});

expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.PASSWORD_RESET,
email,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
iOSBundleId: 'my-bundle',
clientType: 'CLIENT_TYPE_WEB'
Expand All @@ -148,13 +150,15 @@ describe('core/strategies/sendPasswordResetEmail', () => {
packageName: 'my-package'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});
expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.PASSWORD_RESET,
email,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
androidInstallApp: false,
androidMinimumVersionCode: 'my-version',
Expand Down
8 changes: 6 additions & 2 deletions packages/auth/src/core/strategies/email_link.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,16 @@ describe('core/strategies/sendSignInLinkToEmail', () => {
bundleId: 'my-bundle'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});

expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.EMAIL_SIGNIN,
email,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
iOSBundleId: 'my-bundle',
clientType: 'CLIENT_TYPE_WEB'
Expand All @@ -151,13 +153,15 @@ describe('core/strategies/sendSignInLinkToEmail', () => {
packageName: 'my-package'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain'
});
expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.EMAIL_SIGNIN,
email,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
linkDomain: 'hosting-link-domain',
canHandleCodeInApp: true,
androidInstallApp: false,
androidMinimumVersionCode: 'my-version',
Expand Down
9 changes: 8 additions & 1 deletion packages/auth/src/model/public_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ export interface ActionCodeSettings {
* - When the link is handled in the web action widgets, this is the deep link in the
* `continueUrl` query parameter.
* - When the link is handled in the app directly, this is the `continueUrl` query parameter in
* the deep link of the Dynamic Link.
* the deep link of the Dynamic Link or Hosting Link.
*/
url: string;
/**
Expand All @@ -515,6 +515,13 @@ export interface ActionCodeSettings {
* @defaultValue The first domain is automatically selected.
*/
dynamicLinkDomain?: string;
/**
* The optional custom Firebase Hosting domain to use when the link is to be opened via
* a specified mobile app. The domain must be configured in Firebase Hosting and owned
* by the project. This cannot be a default hosting domain (web.app or firebaseapp.com).
* @defaultValue The default hosting domain will be used (for example, `example.firebaseapp.com`)
*/
linkDomain?: string;
}

/**
Expand Down
Loading