mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
Add ActionCodeSettings.linkDomain and deprecate ActionCodeSettings.dynamicLinkDomain (#8738)
* Add linkDomain field to ActionCodeSettings (#8428) * Add linkDomain field to ActionCodeSettings * Update API reports * Update error message for ERROR_INVALID_HOSTING_LINK_DOMAIN to include that default hosting domains cannot be used. * Use constants for test values --------- Co-authored-by: NhienLam <[email protected]> * FDL Deprecation & Hosting link Integration Test (#8603) * Implement Integration test for passwordless email sign-in via firebase-hosting links. * Add license to new test file created' did not match any files * Remove unwanted _ * Add support for setting custom hosting link domain in test app (#8614) * Add afterEach method and apply formatting to the hosting links integration test (#8615) * Mark ActionCodeSettings.dynamicLinkDomain as deprecated * Add changeset * Update API reports * Address review comments * Fix changeset * Update refdocs and address review comments * Update deprecated comment * Add FDL deprecation FAQ link * Fix linkDomain link in refdocs --------- Co-authored-by: NhienLam <[email protected]> Co-authored-by: mansisampat <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
NhienLam
mansisampat
parent
7bec6a1f4b
commit
0e520b0401
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'firebase': minor
|
||||
'@firebase/auth-types': minor
|
||||
'@firebase/auth': minor
|
||||
---
|
||||
|
||||
Added `ActionCodeSettings.linkDomain` to customize the Firebase Hosting link domain that is used in mobile out-of-band email action flows. Also, deprecated `ActionCodeSettings.dynamicLinkDomain`.
|
||||
@@ -40,11 +40,13 @@ export interface ActionCodeSettings {
|
||||
minimumVersion?: string;
|
||||
packageName: string;
|
||||
};
|
||||
// @deprecated
|
||||
dynamicLinkDomain?: string;
|
||||
handleCodeInApp?: boolean;
|
||||
iOS?: {
|
||||
bundleId: string;
|
||||
};
|
||||
linkDomain?: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
@@ -236,6 +238,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
|
||||
|
||||
@@ -26,13 +26,14 @@ 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 (<code>web.app</code> or <code>firebaseapp.com</code>). |
|
||||
| [url](./auth.actioncodesettings.md#actioncodesettingsurl) | string | Sets the link continue/state URL. |
|
||||
|
||||
## ActionCodeSettings.android
|
||||
|
||||
Sets the Android package name.
|
||||
|
||||
This will try to open the link in an android app if it is installed. If `installApp` is passed, it specifies whether to install the Android app if the device supports it and the app is not already installed. If this field is provided without a `packageName`<!-- -->, an error is thrown explaining that the `packageName` must be provided in conjunction with this field. If `minimumVersion` is specified, and an older version of the app is installed, the user is taken to the Play Store to upgrade the app.
|
||||
This will try to open the link in an Android app if it is installed.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
@@ -46,6 +47,11 @@ android?: {
|
||||
|
||||
## ActionCodeSettings.dynamicLinkDomain
|
||||
|
||||
> Warning: This API is now obsolete.
|
||||
>
|
||||
> Firebase Dynamic Links is deprecated and will be shut down as early as August 2025. Instead, use [ActionCodeSettings.linkDomain](./auth.actioncodesettings.md#actioncodesettingslinkdomain) to set a custom domain for mobile links. Learn more in the [Dynamic Links deprecation FAQ](https://firebase.google.com/support/dynamic-links-faq)<!-- -->.
|
||||
>
|
||||
|
||||
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, `example.page.link`<!-- -->).
|
||||
|
||||
<b>Signature:</b>
|
||||
@@ -72,8 +78,6 @@ Sets the iOS bundle ID.
|
||||
|
||||
This will try to open the link in an iOS app if it is installed.
|
||||
|
||||
App installation is not supported for iOS.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
@@ -82,11 +86,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>
|
||||
|
||||
|
||||
@@ -1956,6 +1956,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";
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Vendored
+1
@@ -130,6 +130,7 @@ export type ActionCodeSettings = {
|
||||
iOS?: { bundleId: string };
|
||||
url: string;
|
||||
dynamicLinkDomain?: string;
|
||||
linkDomain?: string;
|
||||
};
|
||||
|
||||
export type AdditionalUserInfo = {
|
||||
|
||||
@@ -823,6 +823,11 @@
|
||||
<input type="text" class="form-control" id="ibi" placeholder="iOS Bundle ID"/>
|
||||
</div>
|
||||
</form>
|
||||
<div class="group">Mobile link</div>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" id="hostingLinkDomain"
|
||||
placeholder="Custom Hosting Link Domain"/>
|
||||
</div>
|
||||
<form class="form form-bordered no-submit">
|
||||
<div class="btn-group radio-block" id="handle-in-app-selection"
|
||||
data-toggle="buttons">
|
||||
|
||||
@@ -49,8 +49,8 @@ import {
|
||||
signInWithCredential,
|
||||
signInWithCustomToken,
|
||||
signInWithEmailAndPassword,
|
||||
signInWithEmailLink,
|
||||
TotpMultiFactorGenerator,
|
||||
TotpSecret,
|
||||
unlink,
|
||||
updateEmail,
|
||||
updatePassword,
|
||||
@@ -995,6 +995,7 @@ function getActionCodeSettings() {
|
||||
const installApp = $('input[name=install-app]:checked').val() === 'Yes';
|
||||
const handleCodeInApp =
|
||||
$('input[name=handle-in-app]:checked').val() === 'Yes';
|
||||
const hostingLinkDomain = $('#hostingLinkDomain').val();
|
||||
if (url || apn || ibi) {
|
||||
actionCodeSettings['url'] = url;
|
||||
if (apn) {
|
||||
@@ -1010,6 +1011,9 @@ function getActionCodeSettings() {
|
||||
};
|
||||
}
|
||||
actionCodeSettings['handleCodeInApp'] = handleCodeInApp;
|
||||
if (hostingLinkDomain) {
|
||||
actionCodeSettings['linkDomain'] = hostingLinkDomain;
|
||||
}
|
||||
}
|
||||
return actionCodeSettings;
|
||||
}
|
||||
@@ -1020,6 +1024,7 @@ function onActionCodeSettingsReset() {
|
||||
$('#apn').val('');
|
||||
$('#amv').val('');
|
||||
$('#ibi').val('');
|
||||
$('#hostingLinkDomain').val('');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,7 +41,8 @@ function getTestFiles(argv) {
|
||||
return [
|
||||
'test/integration/flows/totp.test.ts',
|
||||
'test/integration/flows/password_policy.test.ts',
|
||||
'test/integration/flows/recaptcha_enterprise.test.ts'
|
||||
'test/integration/flows/recaptcha_enterprise.test.ts',
|
||||
'test/integration/flows/hosting_link.test.ts'
|
||||
];
|
||||
}
|
||||
return argv.local
|
||||
|
||||
@@ -70,6 +70,7 @@ export interface GetOobCodeRequest {
|
||||
dynamicLinkDomain?: string;
|
||||
tenantId?: string;
|
||||
targetProjectid?: string;
|
||||
linkDomain?: string;
|
||||
}
|
||||
|
||||
export interface VerifyEmailRequest extends GetOobCodeRequest {
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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> {
|
||||
@@ -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`).'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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)');
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
@@ -60,14 +64,27 @@ 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, {
|
||||
@@ -75,8 +92,8 @@ describe('core/strategies/action_code_settings', () => {
|
||||
iOS: {
|
||||
bundleId: ''
|
||||
},
|
||||
url: 'my-url',
|
||||
dynamicLinkDomain: 'fdl-domain'
|
||||
url: TEST_URL,
|
||||
dynamicLinkDomain: TEST_FDL_DOMAIN
|
||||
})
|
||||
).to.throw(FirebaseError, '(auth/missing-ios-bundle-id)');
|
||||
});
|
||||
@@ -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)');
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -162,7 +162,8 @@ 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({
|
||||
@@ -170,6 +171,7 @@ describe('core/strategies/sendEmailVerification', () => {
|
||||
idToken,
|
||||
continueUrl: 'my-url',
|
||||
dynamicLinkDomain: 'fdl-domain',
|
||||
linkDomain: 'hosting-link-domain',
|
||||
canHandleCodeInApp: true,
|
||||
iOSBundleId: 'my-bundle'
|
||||
});
|
||||
@@ -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',
|
||||
@@ -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({
|
||||
@@ -279,6 +284,7 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
|
||||
newEmail,
|
||||
continueUrl: 'my-url',
|
||||
dynamicLinkDomain: 'fdl-domain',
|
||||
linkDomain: 'hosting-link-domain',
|
||||
canHandleCodeInApp: true,
|
||||
iOSBundleId: 'my-bundle'
|
||||
});
|
||||
@@ -299,7 +305,8 @@ 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,
|
||||
@@ -307,6 +314,7 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
|
||||
newEmail,
|
||||
continueUrl: 'my-url',
|
||||
dynamicLinkDomain: 'fdl-domain',
|
||||
linkDomain: 'hosting-link-domain',
|
||||
canHandleCodeInApp: true,
|
||||
androidInstallApp: false,
|
||||
androidMinimumVersionCode: 'my-version',
|
||||
|
||||
@@ -121,7 +121,8 @@ 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({
|
||||
@@ -129,6 +130,7 @@ describe('core/strategies/sendPasswordResetEmail', () => {
|
||||
email,
|
||||
continueUrl: 'my-url',
|
||||
dynamicLinkDomain: 'fdl-domain',
|
||||
linkDomain: 'hosting-link-domain',
|
||||
canHandleCodeInApp: true,
|
||||
iOSBundleId: 'my-bundle',
|
||||
clientType: 'CLIENT_TYPE_WEB'
|
||||
@@ -149,13 +151,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',
|
||||
|
||||
@@ -124,7 +124,8 @@ 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({
|
||||
@@ -132,6 +133,7 @@ describe('core/strategies/sendSignInLinkToEmail', () => {
|
||||
email,
|
||||
continueUrl: 'my-url',
|
||||
dynamicLinkDomain: 'fdl-domain',
|
||||
linkDomain: 'hosting-link-domain',
|
||||
canHandleCodeInApp: true,
|
||||
iOSBundleId: 'my-bundle',
|
||||
clientType: 'CLIENT_TYPE_WEB'
|
||||
@@ -152,13 +154,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',
|
||||
|
||||
@@ -462,12 +462,7 @@ export interface ActionCodeSettings {
|
||||
* Sets the Android package name.
|
||||
*
|
||||
* @remarks
|
||||
* This will try to open the link in an android app if it is
|
||||
* installed. If `installApp` is passed, it specifies whether to install the Android app if the
|
||||
* device supports it and the app is not already installed. If this field is provided without
|
||||
* a `packageName`, an error is thrown explaining that the `packageName` must be provided in
|
||||
* conjunction with this field. If `minimumVersion` is specified, and an older version of the
|
||||
* app is installed, the user is taken to the Play Store to upgrade the app.
|
||||
* This will try to open the link in an Android app if it is installed.
|
||||
*/
|
||||
android?: {
|
||||
installApp?: boolean;
|
||||
@@ -490,8 +485,6 @@ export interface ActionCodeSettings {
|
||||
*
|
||||
* @remarks
|
||||
* This will try to open the link in an iOS app if it is installed.
|
||||
*
|
||||
* App installation is not supported for iOS.
|
||||
*/
|
||||
iOS?: {
|
||||
bundleId: string;
|
||||
@@ -504,7 +497,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;
|
||||
/**
|
||||
@@ -513,8 +506,20 @@ export interface ActionCodeSettings {
|
||||
*
|
||||
*
|
||||
* @defaultValue The first domain is automatically selected.
|
||||
*
|
||||
* @deprecated Firebase Dynamic Links is deprecated and will be shut down as early as August
|
||||
* 2025. Instead, use {@link ActionCodeSettings.linkDomain} to set a custom domain for mobile
|
||||
* links. Learn more in the {@link https://firebase.google.com/support/dynamic-links-faq | Dynamic Links deprecation FAQ}.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import {
|
||||
ActionCodeSettings,
|
||||
Auth,
|
||||
sendSignInLinkToEmail
|
||||
} from '@firebase/auth';
|
||||
import { expect, use } from 'chai';
|
||||
import {
|
||||
cleanUpTestInstance,
|
||||
getTestInstance,
|
||||
randomEmail
|
||||
} from '../../helpers/integration/helpers';
|
||||
import { getEmulatorUrl } from '../../helpers/integration/settings';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
import { FirebaseError } from '@firebase/util';
|
||||
|
||||
use(chaiAsPromised);
|
||||
|
||||
// Assumes mobileLinksConfig.domain is set as "HOSTING_DOMAIN" in the test GCP-project.
|
||||
describe('Integration test: hosting link validation', () => {
|
||||
let auth: Auth;
|
||||
let email: string;
|
||||
|
||||
const AUTHORIZED_CUSTOM_DOMAIN = 'localhost/action_code_return';
|
||||
const ANDROID_PACKAGE_NAME = 'com.google.firebase.test.thin';
|
||||
const BASE_SETTINGS: ActionCodeSettings = {
|
||||
url: 'http://' + AUTHORIZED_CUSTOM_DOMAIN,
|
||||
handleCodeInApp: true,
|
||||
android: { packageName: ANDROID_PACKAGE_NAME }
|
||||
};
|
||||
const VALID_LINK_DOMAIN = 'jscore-sandbox.testdomaindonotuse.com';
|
||||
const INVALID_LINK_DOMAIN = 'invalid.testdomaindonotuse.com';
|
||||
const INVALID_LINK_DOMAIN_ERROR = 'auth/invalid-hosting-link-domain';
|
||||
const TEST_TENANT_ID = 'passpol-tenant-d7hha';
|
||||
|
||||
beforeEach(function () {
|
||||
auth = getTestInstance();
|
||||
email = randomEmail();
|
||||
|
||||
if (getEmulatorUrl()) {
|
||||
this.skip();
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await cleanUpTestInstance(auth);
|
||||
});
|
||||
|
||||
it('allows user to sign in with default firebase hosting link', async () => {
|
||||
// Sends email link to user using default hosting link.
|
||||
await sendSignInLinkToEmail(auth, email, BASE_SETTINGS);
|
||||
});
|
||||
|
||||
it('allows user to sign in to a tenant with default firebase hosting link', async () => {
|
||||
auth.tenantId = TEST_TENANT_ID;
|
||||
// Sends email link to user using default hosting link.
|
||||
await sendSignInLinkToEmail(auth, email, BASE_SETTINGS);
|
||||
});
|
||||
|
||||
it('allows user to sign in with custom firebase hosting link', async () => {
|
||||
// Sends email link to user using custom hosting link.
|
||||
await sendSignInLinkToEmail(auth, email, {
|
||||
...BASE_SETTINGS,
|
||||
linkDomain: VALID_LINK_DOMAIN
|
||||
});
|
||||
});
|
||||
|
||||
it('allows user to sign in to a tenant with custom firebase hosting link', async () => {
|
||||
// Sends email link to user using custom hosting link.
|
||||
auth.tenantId = TEST_TENANT_ID;
|
||||
await sendSignInLinkToEmail(auth, email, {
|
||||
...BASE_SETTINGS,
|
||||
linkDomain: VALID_LINK_DOMAIN
|
||||
});
|
||||
});
|
||||
|
||||
it('sign in with invalid firebase hosting link throws exception', async () => {
|
||||
// Throws an exception while sening email link to user using invalid hosting link.
|
||||
await expect(
|
||||
sendSignInLinkToEmail(auth, email, {
|
||||
...BASE_SETTINGS,
|
||||
linkDomain: INVALID_LINK_DOMAIN
|
||||
})
|
||||
).to.be.rejectedWith(
|
||||
FirebaseError,
|
||||
new RegExp('.*' + INVALID_LINK_DOMAIN_ERROR + '.*')
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user