Skip to content

Commit 0ffa5fc

Browse files
feat(auth): Added FDL deprecation support for Firebase Auth (#2752)
Added linkDomain support for ActionCodeSettings deprecating dynamicLinkDomain. Added mobileLinksConfig support for updating project configs --------- Co-authored-by: Pavan Shankar <[email protected]>
1 parent 379bcff commit 0ffa5fc

13 files changed

+484
-6
lines changed

Diff for: etc/firebase-admin.auth.api.md

+17
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ export interface ActionCodeSettings {
1313
installApp?: boolean;
1414
minimumVersion?: string;
1515
};
16+
// @deprecated
1617
dynamicLinkDomain?: string;
1718
handleCodeInApp?: boolean;
1819
iOS?: {
1920
bundleId: string;
2021
};
22+
linkDomain?: string;
2123
url: string;
2224
}
2325

@@ -221,6 +223,11 @@ export class AuthClientErrorCode {
221223
message: string;
222224
};
223225
// (undocumented)
226+
static INVALID_HOSTING_LINK_DOMAIN: {
227+
code: string;
228+
message: string;
229+
};
230+
// (undocumented)
224231
static INVALID_ID_TOKEN: {
225232
code: string;
226233
message: string;
@@ -757,6 +764,14 @@ export interface ListUsersResult {
757764
users: UserRecord[];
758765
}
759766

767+
// @public
768+
export interface MobileLinksConfig {
769+
domain?: MobileLinksDomain;
770+
}
771+
772+
// @public
773+
export type MobileLinksDomain = 'HOSTING_DOMAIN' | 'FIREBASE_DYNAMIC_LINK_DOMAIN';
774+
760775
// @public
761776
export interface MultiFactorConfig {
762777
factorIds?: AuthFactorType[];
@@ -847,6 +862,7 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo {
847862
// @public
848863
export class ProjectConfig {
849864
readonly emailPrivacyConfig?: EmailPrivacyConfig;
865+
readonly mobileLinksConfig?: MobileLinksConfig;
850866
get multiFactorConfig(): MultiFactorConfig | undefined;
851867
readonly passwordPolicyConfig?: PasswordPolicyConfig;
852868
get recaptchaConfig(): RecaptchaConfig | undefined;
@@ -996,6 +1012,7 @@ export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactor
9961012
// @public
9971013
export interface UpdateProjectConfigRequest {
9981014
emailPrivacyConfig?: EmailPrivacyConfig;
1015+
mobileLinksConfig?: MobileLinksConfig;
9991016
multiFactorConfig?: MultiFactorConfig;
10001017
passwordPolicyConfig?: PasswordPolicyConfig;
10011018
recaptchaConfig?: RecaptchaConfig;

Diff for: src/auth/action-code-settings-builder.ts

+22
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,27 @@ export interface ActionCodeSettings {
9494
* configured per project. This field provides the ability to explicitly choose
9595
* configured per project. This fields provides the ability explicitly choose
9696
* one. If none is provided, the oldest domain is used by default.
97+
* @deprecated use `linkDomain` instead
9798
*/
9899
dynamicLinkDomain?: string;
100+
101+
/**
102+
* Defines the custom Firebase Hosting domain to use when the link is to be opened
103+
* via a specified mobile app,
104+
* This is a replacement of Firebase Dynamic Link.
105+
* If none is provided,
106+
* a default hosting domain will be used (for example, `example.firebaseapp.com`)
107+
*/
108+
109+
linkDomain?: string;
99110
}
100111

101112
/** Defines the email action code server request. */
102113
interface EmailActionCodeRequest {
103114
continueUrl?: string;
104115
canHandleCodeInApp?: boolean;
105116
dynamicLinkDomain?: string;
117+
linkDomain?: string;
106118
androidPackageName?: string;
107119
androidMinimumVersion: string;
108120
androidInstallApp?: boolean;
@@ -123,6 +135,7 @@ export class ActionCodeSettingsBuilder {
123135
private ibi?: string;
124136
private canHandleCodeInApp?: boolean;
125137
private dynamicLinkDomain?: string;
138+
private linkDomain?: string;
126139

127140
/**
128141
* ActionCodeSettingsBuilder constructor.
@@ -166,6 +179,14 @@ export class ActionCodeSettingsBuilder {
166179
}
167180
this.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;
168181

182+
if (typeof actionCodeSettings.linkDomain !== 'undefined' &&
183+
!validator.isNonEmptyString(actionCodeSettings.linkDomain)) {
184+
throw new FirebaseAuthError(
185+
AuthClientErrorCode.INVALID_HOSTING_LINK_DOMAIN,
186+
);
187+
}
188+
this.linkDomain = actionCodeSettings.linkDomain;
189+
169190
if (typeof actionCodeSettings.iOS !== 'undefined') {
170191
if (!validator.isNonNullObject(actionCodeSettings.iOS)) {
171192
throw new FirebaseAuthError(
@@ -230,6 +251,7 @@ export class ActionCodeSettingsBuilder {
230251
continueUrl: this.continueUrl,
231252
canHandleCodeInApp: this.canHandleCodeInApp,
232253
dynamicLinkDomain: this.dynamicLinkDomain,
254+
linkDomain: this.linkDomain,
233255
androidPackageName: this.apn,
234256
androidMinimumVersion: this.amv,
235257
androidInstallApp: this.installApp,

Diff for: src/auth/auth-config.ts

+54
Original file line numberDiff line numberDiff line change
@@ -2137,6 +2137,60 @@ export interface PasswordPolicyConfig {
21372137
constraints?: CustomStrengthOptionsConfig;
21382138
}
21392139

2140+
/**
2141+
* Configuration for settings related to univeral links (iOS)
2142+
* and app links (Android).
2143+
*/
2144+
export interface MobileLinksConfig {
2145+
/**
2146+
* Use Firebase Hosting or dynamic link domain as the out-of-band code domain.
2147+
*/
2148+
domain?: MobileLinksDomain;
2149+
}
2150+
2151+
/**
2152+
* Open code in app domain to use for app links and universal links.
2153+
*/
2154+
export type MobileLinksDomain = 'HOSTING_DOMAIN' | 'FIREBASE_DYNAMIC_LINK_DOMAIN';
2155+
2156+
/**
2157+
* Defines the MobileLinksAuthConfig class used for validation.
2158+
*
2159+
* @internal
2160+
*/
2161+
export class MobileLinksAuthConfig {
2162+
public static validate(options: MobileLinksConfig): void {
2163+
if (!validator.isNonNullObject(options)) {
2164+
throw new FirebaseAuthError(
2165+
AuthClientErrorCode.INVALID_CONFIG,
2166+
'"MobileLinksConfig" must be a non-null object.',
2167+
);
2168+
}
2169+
2170+
const validKeys = {
2171+
domain: true,
2172+
};
2173+
2174+
for (const key in options) {
2175+
if (!(key in validKeys)) {
2176+
throw new FirebaseAuthError(
2177+
AuthClientErrorCode.INVALID_CONFIG,
2178+
`"${key}" is not a valid "MobileLinksConfig" parameter.`,
2179+
);
2180+
}
2181+
}
2182+
2183+
if (typeof options.domain !== 'undefined'
2184+
&& options.domain !== 'HOSTING_DOMAIN'
2185+
&& options.domain !== 'FIREBASE_DYNAMIC_LINK_DOMAIN') {
2186+
throw new FirebaseAuthError(
2187+
AuthClientErrorCode.INVALID_CONFIG,
2188+
'"MobileLinksConfig.domain" must be either "HOSTING_DOMAIN" or "FIREBASE_DYNAMIC_LINK_DOMAIN".',
2189+
);
2190+
}
2191+
}
2192+
}
2193+
21402194
/**
21412195
* A password policy's enforcement state.
21422196
*/

Diff for: src/auth/base-auth.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ export abstract class BaseAuth {
749749
* minimumVersion: '12'
750750
* },
751751
* handleCodeInApp: true,
752-
* dynamicLinkDomain: 'custom.page.link'
752+
* linkDomain: 'project-id.firebaseapp.com'
753753
* };
754754
* admin.auth()
755755
* .generatePasswordResetLink('[email protected]', actionCodeSettings)
@@ -802,7 +802,7 @@ export abstract class BaseAuth {
802802
* minimumVersion: '12'
803803
* },
804804
* handleCodeInApp: true,
805-
* dynamicLinkDomain: 'custom.page.link'
805+
* linkDomain: 'project-id.firebaseapp.com'
806806
* };
807807
* admin.auth()
808808
* .generateEmailVerificationLink('[email protected]', actionCodeSettings)
@@ -883,7 +883,7 @@ export abstract class BaseAuth {
883883
* minimumVersion: '12'
884884
* },
885885
* handleCodeInApp: true,
886-
* dynamicLinkDomain: 'custom.page.link'
886+
* linkDomain: 'project-id.firebaseapp.com'
887887
* };
888888
* admin.auth()
889889
* .generateEmailVerificationLink('[email protected]', actionCodeSettings)

Diff for: src/auth/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ export {
104104
PasswordPolicyEnforcementState,
105105
CustomStrengthOptionsConfig,
106106
EmailPrivacyConfig,
107+
MobileLinksConfig,
108+
MobileLinksDomain,
107109
} from './auth-config';
108110

109111
export {

Diff for: src/auth/project-config.ts

+30
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import {
2929
PasswordPolicyConfig,
3030
EmailPrivacyConfig,
3131
EmailPrivacyAuthConfig,
32+
MobileLinksConfig,
33+
MobileLinksAuthConfig,
3234
} from './auth-config';
3335
import { deepCopy } from '../utils/deep-copy';
3436

@@ -60,6 +62,11 @@ export interface UpdateProjectConfigRequest {
6062
* The email privacy configuration to update on the project
6163
*/
6264
emailPrivacyConfig?: EmailPrivacyConfig;
65+
66+
/**
67+
* The mobile links configuration for the project
68+
*/
69+
mobileLinksConfig?: MobileLinksConfig;
6370
}
6471

6572
/**
@@ -71,6 +78,7 @@ export interface ProjectConfigServerResponse {
7178
recaptchaConfig?: RecaptchaAuthServerConfig;
7279
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
7380
emailPrivacyConfig?: EmailPrivacyConfig;
81+
mobileLinksConfig?: MobileLinksConfig;
7482
}
7583

7684
/**
@@ -82,6 +90,7 @@ export interface ProjectConfigClientRequest {
8290
recaptchaConfig?: RecaptchaAuthServerConfig;
8391
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
8492
emailPrivacyConfig?: EmailPrivacyConfig;
93+
mobileLinksConfig?: MobileLinksConfig;
8594
}
8695

8796
/**
@@ -128,6 +137,11 @@ export class ProjectConfig {
128137
*/
129138
public readonly emailPrivacyConfig?: EmailPrivacyConfig;
130139

140+
/**
141+
* The mobile links configuration for the project
142+
*/
143+
public readonly mobileLinksConfig?: MobileLinksConfig
144+
131145
/**
132146
* Validates a project config options object. Throws an error on failure.
133147
*
@@ -146,6 +160,7 @@ export class ProjectConfig {
146160
recaptchaConfig: true,
147161
passwordPolicyConfig: true,
148162
emailPrivacyConfig: true,
163+
mobileLinksConfig: true,
149164
}
150165
// Check for unsupported top level attributes.
151166
for (const key in request) {
@@ -179,6 +194,11 @@ export class ProjectConfig {
179194
if (typeof request.emailPrivacyConfig !== 'undefined') {
180195
EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
181196
}
197+
198+
// Validate Mobile Links Config if provided.
199+
if (typeof request.mobileLinksConfig !== 'undefined') {
200+
MobileLinksAuthConfig.validate(request.mobileLinksConfig);
201+
}
182202
}
183203

184204
/**
@@ -206,6 +226,9 @@ export class ProjectConfig {
206226
if (typeof configOptions.emailPrivacyConfig !== 'undefined') {
207227
request.emailPrivacyConfig = configOptions.emailPrivacyConfig;
208228
}
229+
if (typeof configOptions.mobileLinksConfig !== 'undefined') {
230+
request.mobileLinksConfig = configOptions.mobileLinksConfig;
231+
}
209232
return request;
210233
}
211234

@@ -234,6 +257,9 @@ export class ProjectConfig {
234257
if (typeof response.emailPrivacyConfig !== 'undefined') {
235258
this.emailPrivacyConfig = response.emailPrivacyConfig;
236259
}
260+
if (typeof response.mobileLinksConfig !== 'undefined') {
261+
this.mobileLinksConfig = response.mobileLinksConfig;
262+
}
237263
}
238264
/**
239265
* Returns a JSON-serializable representation of this object.
@@ -248,6 +274,7 @@ export class ProjectConfig {
248274
recaptchaConfig: deepCopy(this.recaptchaConfig),
249275
passwordPolicyConfig: deepCopy(this.passwordPolicyConfig),
250276
emailPrivacyConfig: deepCopy(this.emailPrivacyConfig),
277+
mobileLinksConfig: deepCopy(this.mobileLinksConfig),
251278
};
252279
if (typeof json.smsRegionConfig === 'undefined') {
253280
delete json.smsRegionConfig;
@@ -264,6 +291,9 @@ export class ProjectConfig {
264291
if (typeof json.emailPrivacyConfig === 'undefined') {
265292
delete json.emailPrivacyConfig;
266293
}
294+
if (typeof json.mobileLinksConfig === 'undefined') {
295+
delete json.mobileLinksConfig;
296+
}
267297
return json;
268298
}
269299
}

Diff for: src/utils/error.ts

+7
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,11 @@ export class AuthClientErrorCode {
471471
message: 'The provided dynamic link domain is not configured or authorized ' +
472472
'for the current project.',
473473
};
474+
public static INVALID_HOSTING_LINK_DOMAIN = {
475+
code: 'invalid-hosting-link-domain',
476+
message: 'The provided hosting link domain is not configured in Firebase ' +
477+
'Hosting or is not owned by the current project.',
478+
};
474479
public static INVALID_EMAIL_VERIFIED = {
475480
code: 'invalid-email-verified',
476481
message: 'The emailVerified field must be a boolean.',
@@ -933,6 +938,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = {
933938
INVALID_CONTINUE_URI: 'INVALID_CONTINUE_URI',
934939
// Dynamic link domain in provided ActionCodeSettings is not authorized.
935940
INVALID_DYNAMIC_LINK_DOMAIN: 'INVALID_DYNAMIC_LINK_DOMAIN',
941+
// Hosting link domain in provided ActionCodeSettings is not owned by the current project.
942+
INVALID_HOSTING_LINK_DOMAIN: 'INVALID_HOSTING_LINK_DOMAIN',
936943
// uploadAccount provides an email that already exists.
937944
DUPLICATE_EMAIL: 'EMAIL_ALREADY_EXISTS',
938945
// uploadAccount provides a localId that already exists.

0 commit comments

Comments
 (0)