Skip to content

Commit 85ebbce

Browse files
authored
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]>
1 parent f7c6dc4 commit 85ebbce

File tree

13 files changed

+93
-24
lines changed

13 files changed

+93
-24
lines changed

common/api-review/auth.api.md

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export interface ActionCodeSettings {
4545
iOS?: {
4646
bundleId: string;
4747
};
48+
linkDomain?: string;
4849
url: string;
4950
}
5051

@@ -236,6 +237,7 @@ export const AuthErrorCodes: {
236237
readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version";
237238
readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version";
238239
readonly INVALID_REQ_TYPE: "auth/invalid-req-type";
240+
readonly INVALID_HOSTING_LINK_DOMAIN: "auth/invalid-hosting-link-domain";
239241
};
240242

241243
// @public

docs-devsite/auth.actioncodesettings.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface ActionCodeSettings
2626
| [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>). |
2727
| [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. |
2828
| [iOS](./auth.actioncodesettings.md#actioncodesettingsios) | { bundleId: string; } | Sets the iOS bundle ID. |
29+
| [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). |
2930
| [url](./auth.actioncodesettings.md#actioncodesettingsurl) | string | Sets the link continue/state URL. |
3031

3132
## ActionCodeSettings.android
@@ -82,11 +83,21 @@ iOS?: {
8283
};
8384
```
8485

86+
## ActionCodeSettings.linkDomain
87+
88+
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).
89+
90+
<b>Signature:</b>
91+
92+
```typescript
93+
linkDomain?: string;
94+
```
95+
8596
## ActionCodeSettings.url
8697

8798
Sets the link continue/state URL.
8899

89-
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.
100+
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.
90101

91102
<b>Signature:</b>
92103

docs-devsite/auth.md

+1
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,7 @@ AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY: {
19541954
readonly MISSING_RECAPTCHA_VERSION: "auth/missing-recaptcha-version";
19551955
readonly INVALID_RECAPTCHA_VERSION: "auth/invalid-recaptcha-version";
19561956
readonly INVALID_REQ_TYPE: "auth/invalid-req-type";
1957+
readonly INVALID_HOSTING_LINK_DOMAIN: "auth/invalid-hosting-link-domain";
19571958
}
19581959
```
19591960

packages/auth-types/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ export type ActionCodeSettings = {
130130
iOS?: { bundleId: string };
131131
url: string;
132132
dynamicLinkDomain?: string;
133+
linkDomain?: string;
133134
};
134135

135136
export type AdditionalUserInfo = {

packages/auth/src/api/authentication/email_and_password.ts

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export interface GetOobCodeRequest {
7070
dynamicLinkDomain?: string;
7171
tenantId?: string;
7272
targetProjectid?: string;
73+
linkDomain?: string;
7374
}
7475

7576
export interface VerifyEmailRequest extends GetOobCodeRequest {

packages/auth/src/api/errors.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ export const enum ServerError {
100100
MISSING_RECAPTCHA_VERSION = 'MISSING_RECAPTCHA_VERSION',
101101
INVALID_RECAPTCHA_VERSION = 'INVALID_RECAPTCHA_VERSION',
102102
INVALID_REQ_TYPE = 'INVALID_REQ_TYPE',
103-
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'PASSWORD_DOES_NOT_MEET_REQUIREMENTS'
103+
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'PASSWORD_DOES_NOT_MEET_REQUIREMENTS',
104+
INVALID_HOSTING_LINK_DOMAIN = 'INVALID_HOSTING_LINK_DOMAIN'
104105
}
105106

106107
/**

packages/auth/src/core/errors.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ export const enum AuthErrorCode {
134134
INVALID_RECAPTCHA_VERSION = 'invalid-recaptcha-version',
135135
INVALID_REQ_TYPE = 'invalid-req-type',
136136
UNSUPPORTED_PASSWORD_POLICY_SCHEMA_VERSION = 'unsupported-password-policy-schema-version',
137-
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'password-does-not-meet-requirements'
137+
PASSWORD_DOES_NOT_MEET_REQUIREMENTS = 'password-does-not-meet-requirements',
138+
INVALID_HOSTING_LINK_DOMAIN = 'invalid-hosting-link-domain'
138139
}
139140

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

@@ -598,5 +602,6 @@ export const AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY = {
598602
MISSING_CLIENT_TYPE: 'auth/missing-client-type',
599603
MISSING_RECAPTCHA_VERSION: 'auth/missing-recaptcha-version',
600604
INVALID_RECAPTCHA_VERSION: 'auth/invalid-recaptcha-version',
601-
INVALID_REQ_TYPE: 'auth/invalid-req-type'
605+
INVALID_REQ_TYPE: 'auth/invalid-req-type',
606+
INVALID_HOSTING_LINK_DOMAIN: 'auth/invalid-hosting-link-domain'
602607
} as const;

packages/auth/src/core/strategies/action_code_settings.test.ts

+27-10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ describe('core/strategies/action_code_settings', () => {
2626
let auth: TestAuth;
2727
const request: GetOobCodeRequest = {};
2828

29+
const TEST_BUNDLE_ID = 'my-bundle';
30+
const TEST_FDL_DOMAIN = 'fdl-domain';
31+
const TEST_URL = 'my-url';
32+
2933
beforeEach(async () => {
3034
auth = await testAuth();
3135
});
@@ -35,10 +39,10 @@ describe('core/strategies/action_code_settings', () => {
3539
_setActionCodeSettingsOnRequest(auth, request, {
3640
handleCodeInApp: true,
3741
iOS: {
38-
bundleId: 'my-bundle'
42+
bundleId: TEST_BUNDLE_ID
3943
},
4044
url: '',
41-
dynamicLinkDomain: 'fdl-domain'
45+
dynamicLinkDomain: TEST_FDL_DOMAIN
4246
})
4347
).to.throw(FirebaseError, '(auth/invalid-continue-uri)');
4448
});
@@ -48,9 +52,9 @@ describe('core/strategies/action_code_settings', () => {
4852
_setActionCodeSettingsOnRequest(auth, request, {
4953
handleCodeInApp: true,
5054
iOS: {
51-
bundleId: 'my-´bundle'
55+
bundleId: TEST_BUNDLE_ID
5256
},
53-
url: 'my-url'
57+
url: TEST_URL
5458
})
5559
).to.not.throw();
5660
});
@@ -60,23 +64,36 @@ describe('core/strategies/action_code_settings', () => {
6064
_setActionCodeSettingsOnRequest(auth, request, {
6165
handleCodeInApp: true,
6266
iOS: {
63-
bundleId: 'my-´bundle'
67+
bundleId: TEST_BUNDLE_ID
6468
},
65-
url: 'my-url',
69+
url: TEST_URL,
6670
dynamicLinkDomain: ''
6771
})
6872
).to.throw(FirebaseError, '(auth/invalid-dynamic-link-domain)');
6973
});
7074

75+
it('should require a non empty hosting link URL', () => {
76+
expect(() =>
77+
_setActionCodeSettingsOnRequest(auth, request, {
78+
handleCodeInApp: true,
79+
iOS: {
80+
bundleId: TEST_BUNDLE_ID
81+
},
82+
url: TEST_URL,
83+
linkDomain: ''
84+
})
85+
).to.throw(FirebaseError, '(auth/invalid-hosting-link-domain)');
86+
});
87+
7188
it('should require a non-empty bundle ID', () => {
7289
expect(() =>
7390
_setActionCodeSettingsOnRequest(auth, request, {
7491
handleCodeInApp: true,
7592
iOS: {
7693
bundleId: ''
7794
},
78-
url: 'my-url',
79-
dynamicLinkDomain: 'fdl-domain'
95+
url: TEST_URL,
96+
dynamicLinkDomain: TEST_FDL_DOMAIN
8097
})
8198
).to.throw(FirebaseError, '(auth/missing-ios-bundle-id)');
8299
});
@@ -88,8 +105,8 @@ describe('core/strategies/action_code_settings', () => {
88105
android: {
89106
packageName: ''
90107
},
91-
url: 'my-url',
92-
dynamicLinkDomain: 'fdl-domain'
108+
url: TEST_URL,
109+
dynamicLinkDomain: TEST_FDL_DOMAIN
93110
})
94111
).to.throw(FirebaseError, '(auth/missing-android-pkg-name)');
95112
});

packages/auth/src/core/strategies/action_code_settings.ts

+7
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,16 @@ export function _setActionCodeSettingsOnRequest(
3737
auth,
3838
AuthErrorCode.INVALID_DYNAMIC_LINK_DOMAIN
3939
);
40+
_assert(
41+
typeof actionCodeSettings.linkDomain === 'undefined' ||
42+
actionCodeSettings.linkDomain.length > 0,
43+
auth,
44+
AuthErrorCode.INVALID_HOSTING_LINK_DOMAIN
45+
);
4046

4147
request.continueUrl = actionCodeSettings.url;
4248
request.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;
49+
request.linkDomain = actionCodeSettings.linkDomain;
4350
request.canHandleCodeInApp = actionCodeSettings.handleCodeInApp;
4451

4552
if (actionCodeSettings.iOS) {

packages/auth/src/core/strategies/email.test.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,16 @@ describe('core/strategies/sendEmailVerification', () => {
162162
bundleId: 'my-bundle'
163163
},
164164
url: 'my-url',
165-
dynamicLinkDomain: 'fdl-domain'
165+
dynamicLinkDomain: 'fdl-domain',
166+
linkDomain: 'hosting-link-domain'
166167
});
167168

168169
expect(mock.calls[0].request).to.eql({
169170
requestType: ActionCodeOperation.VERIFY_EMAIL,
170171
idToken,
171172
continueUrl: 'my-url',
172173
dynamicLinkDomain: 'fdl-domain',
174+
linkDomain: 'hosting-link-domain',
173175
canHandleCodeInApp: true,
174176
iOSBundleId: 'my-bundle'
175177
});
@@ -190,13 +192,15 @@ describe('core/strategies/sendEmailVerification', () => {
190192
packageName: 'my-package'
191193
},
192194
url: 'my-url',
193-
dynamicLinkDomain: 'fdl-domain'
195+
dynamicLinkDomain: 'fdl-domain',
196+
linkDomain: 'hosting-link-domain'
194197
});
195198
expect(mock.calls[0].request).to.eql({
196199
requestType: ActionCodeOperation.VERIFY_EMAIL,
197200
idToken,
198201
continueUrl: 'my-url',
199202
dynamicLinkDomain: 'fdl-domain',
203+
linkDomain: 'hosting-link-domain',
200204
canHandleCodeInApp: true,
201205
androidInstallApp: false,
202206
androidMinimumVersionCode: 'my-version',
@@ -270,7 +274,8 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
270274
bundleId: 'my-bundle'
271275
},
272276
url: 'my-url',
273-
dynamicLinkDomain: 'fdl-domain'
277+
dynamicLinkDomain: 'fdl-domain',
278+
linkDomain: 'hosting-link-domain'
274279
});
275280

276281
expect(mock.calls[0].request).to.eql({
@@ -279,6 +284,7 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
279284
newEmail,
280285
continueUrl: 'my-url',
281286
dynamicLinkDomain: 'fdl-domain',
287+
linkDomain: 'hosting-link-domain',
282288
canHandleCodeInApp: true,
283289
iOSBundleId: 'my-bundle'
284290
});
@@ -299,14 +305,16 @@ describe('core/strategies/verifyBeforeUpdateEmail', () => {
299305
packageName: 'my-package'
300306
},
301307
url: 'my-url',
302-
dynamicLinkDomain: 'fdl-domain'
308+
dynamicLinkDomain: 'fdl-domain',
309+
linkDomain: 'hosting-link-domain'
303310
});
304311
expect(mock.calls[0].request).to.eql({
305312
requestType: ActionCodeOperation.VERIFY_AND_CHANGE_EMAIL,
306313
idToken,
307314
newEmail,
308315
continueUrl: 'my-url',
309316
dynamicLinkDomain: 'fdl-domain',
317+
linkDomain: 'hosting-link-domain',
310318
canHandleCodeInApp: true,
311319
androidInstallApp: false,
312320
androidMinimumVersionCode: 'my-version',

packages/auth/src/core/strategies/email_and_password.test.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,16 @@ describe('core/strategies/sendPasswordResetEmail', () => {
120120
bundleId: 'my-bundle'
121121
},
122122
url: 'my-url',
123-
dynamicLinkDomain: 'fdl-domain'
123+
dynamicLinkDomain: 'fdl-domain',
124+
linkDomain: 'hosting-link-domain'
124125
});
125126

126127
expect(mock.calls[0].request).to.eql({
127128
requestType: ActionCodeOperation.PASSWORD_RESET,
128129
email,
129130
continueUrl: 'my-url',
130131
dynamicLinkDomain: 'fdl-domain',
132+
linkDomain: 'hosting-link-domain',
131133
canHandleCodeInApp: true,
132134
iOSBundleId: 'my-bundle',
133135
clientType: 'CLIENT_TYPE_WEB'
@@ -148,13 +150,15 @@ describe('core/strategies/sendPasswordResetEmail', () => {
148150
packageName: 'my-package'
149151
},
150152
url: 'my-url',
151-
dynamicLinkDomain: 'fdl-domain'
153+
dynamicLinkDomain: 'fdl-domain',
154+
linkDomain: 'hosting-link-domain'
152155
});
153156
expect(mock.calls[0].request).to.eql({
154157
requestType: ActionCodeOperation.PASSWORD_RESET,
155158
email,
156159
continueUrl: 'my-url',
157160
dynamicLinkDomain: 'fdl-domain',
161+
linkDomain: 'hosting-link-domain',
158162
canHandleCodeInApp: true,
159163
androidInstallApp: false,
160164
androidMinimumVersionCode: 'my-version',

packages/auth/src/core/strategies/email_link.test.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,16 @@ describe('core/strategies/sendSignInLinkToEmail', () => {
123123
bundleId: 'my-bundle'
124124
},
125125
url: 'my-url',
126-
dynamicLinkDomain: 'fdl-domain'
126+
dynamicLinkDomain: 'fdl-domain',
127+
linkDomain: 'hosting-link-domain'
127128
});
128129

129130
expect(mock.calls[0].request).to.eql({
130131
requestType: ActionCodeOperation.EMAIL_SIGNIN,
131132
email,
132133
continueUrl: 'my-url',
133134
dynamicLinkDomain: 'fdl-domain',
135+
linkDomain: 'hosting-link-domain',
134136
canHandleCodeInApp: true,
135137
iOSBundleId: 'my-bundle',
136138
clientType: 'CLIENT_TYPE_WEB'
@@ -151,13 +153,15 @@ describe('core/strategies/sendSignInLinkToEmail', () => {
151153
packageName: 'my-package'
152154
},
153155
url: 'my-url',
154-
dynamicLinkDomain: 'fdl-domain'
156+
dynamicLinkDomain: 'fdl-domain',
157+
linkDomain: 'hosting-link-domain'
155158
});
156159
expect(mock.calls[0].request).to.eql({
157160
requestType: ActionCodeOperation.EMAIL_SIGNIN,
158161
email,
159162
continueUrl: 'my-url',
160163
dynamicLinkDomain: 'fdl-domain',
164+
linkDomain: 'hosting-link-domain',
161165
canHandleCodeInApp: true,
162166
androidInstallApp: false,
163167
androidMinimumVersionCode: 'my-version',

packages/auth/src/model/public_types.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ export interface ActionCodeSettings {
504504
* - When the link is handled in the web action widgets, this is the deep link in the
505505
* `continueUrl` query parameter.
506506
* - When the link is handled in the app directly, this is the `continueUrl` query parameter in
507-
* the deep link of the Dynamic Link.
507+
* the deep link of the Dynamic Link or Hosting Link.
508508
*/
509509
url: string;
510510
/**
@@ -515,6 +515,13 @@ export interface ActionCodeSettings {
515515
* @defaultValue The first domain is automatically selected.
516516
*/
517517
dynamicLinkDomain?: string;
518+
/**
519+
* The optional custom Firebase Hosting domain to use when the link is to be opened via
520+
* a specified mobile app. The domain must be configured in Firebase Hosting and owned
521+
* by the project. This cannot be a default hosting domain (web.app or firebaseapp.com).
522+
* @defaultValue The default hosting domain will be used (for example, `example.firebaseapp.com`)
523+
*/
524+
linkDomain?: string;
518525
}
519526

520527
/**

0 commit comments

Comments
 (0)