Skip to content

Commit 592355d

Browse files
Added mobileLinksConfig field for project and tenant creation and/or updating workflow. (#2659)
This will be later used in Firebase Dynamic links deprecation work.
1 parent 51e8a84 commit 592355d

File tree

8 files changed

+273
-3
lines changed

8 files changed

+273
-3
lines changed

etc/firebase-admin.auth.api.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,14 @@ export interface ListUsersResult {
766766
users: UserRecord[];
767767
}
768768

769+
// @public
770+
export interface MobileLinksConfig {
771+
domain?: MobileLinksDomain;
772+
}
773+
774+
// @public
775+
export type MobileLinksDomain = 'HOSTING_DOMAIN' | 'FIREBASE_DYNAMIC_LINK_DOMAIN';
776+
769777
// @public
770778
export interface MultiFactorConfig {
771779
factorIds?: AuthFactorType[];
@@ -856,6 +864,7 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo {
856864
// @public
857865
export class ProjectConfig {
858866
readonly emailPrivacyConfig?: EmailPrivacyConfig;
867+
readonly mobileLinksConfig?: MobileLinksConfig;
859868
get multiFactorConfig(): MultiFactorConfig | undefined;
860869
readonly passwordPolicyConfig?: PasswordPolicyConfig;
861870
get recaptchaConfig(): RecaptchaConfig | undefined;
@@ -941,6 +950,7 @@ export class Tenant {
941950
readonly displayName?: string;
942951
readonly emailPrivacyConfig?: EmailPrivacyConfig;
943952
get emailSignInConfig(): EmailSignInProviderConfig | undefined;
953+
readonly mobileLinksConfig?: MobileLinksConfig;
944954
get multiFactorConfig(): MultiFactorConfig | undefined;
945955
readonly passwordPolicyConfig?: PasswordPolicyConfig;
946956
get recaptchaConfig(): RecaptchaConfig | undefined;
@@ -995,6 +1005,7 @@ export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactor
9951005
// @public
9961006
export interface UpdateProjectConfigRequest {
9971007
emailPrivacyConfig?: EmailPrivacyConfig;
1008+
mobileLinksConfig?: MobileLinksConfig;
9981009
multiFactorConfig?: MultiFactorConfig;
9991010
passwordPolicyConfig?: PasswordPolicyConfig;
10001011
recaptchaConfig?: RecaptchaConfig;
@@ -1021,6 +1032,7 @@ export interface UpdateTenantRequest {
10211032
displayName?: string;
10221033
emailPrivacyConfig?: EmailPrivacyConfig;
10231034
emailSignInConfig?: EmailSignInProviderConfig;
1035+
mobileLinksConfig?: MobileLinksConfig;
10241036
multiFactorConfig?: MultiFactorConfig;
10251037
passwordPolicyConfig?: PasswordPolicyConfig;
10261038
recaptchaConfig?: RecaptchaConfig;

src/auth/auth-config.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,6 +1965,60 @@ export interface PasswordPolicyConfig {
19651965
constraints?: CustomStrengthOptionsConfig;
19661966
}
19671967

1968+
/**
1969+
* Configuration for settings related to univeral links (iOS)
1970+
* and app links (Android).
1971+
*/
1972+
export interface MobileLinksConfig {
1973+
/**
1974+
* Use firebase Hosting or dynamic link domain as the out-of-band code domain.
1975+
*/
1976+
domain?: MobileLinksDomain;
1977+
}
1978+
1979+
/**
1980+
* Open code in app domain to use for app links and universal links.
1981+
*/
1982+
export type MobileLinksDomain = 'HOSTING_DOMAIN' | 'FIREBASE_DYNAMIC_LINK_DOMAIN';
1983+
1984+
/**
1985+
* Defines the MobileLinksAuthConfig class used for validation.
1986+
*
1987+
* @internal
1988+
*/
1989+
export class MobileLinksAuthConfig {
1990+
public static validate(options: MobileLinksConfig): void {
1991+
if (!validator.isNonNullObject(options)) {
1992+
throw new FirebaseAuthError(
1993+
AuthClientErrorCode.INVALID_CONFIG,
1994+
'"MobileLinksConfig" must be a non-null object.',
1995+
);
1996+
}
1997+
1998+
const validKeys = {
1999+
domain: true,
2000+
};
2001+
2002+
for (const key in options) {
2003+
if (!(key in validKeys)) {
2004+
throw new FirebaseAuthError(
2005+
AuthClientErrorCode.INVALID_CONFIG,
2006+
`"${key}" is not a valid "MobileLinksConfig" parameter.`,
2007+
);
2008+
}
2009+
}
2010+
2011+
if (typeof options.domain !== 'undefined'
2012+
&& options.domain !== 'HOSTING_DOMAIN'
2013+
&& options.domain !== 'FIREBASE_DYNAMIC_LINK_DOMAIN') {
2014+
throw new FirebaseAuthError(
2015+
AuthClientErrorCode.INVALID_CONFIG,
2016+
'"MobileLinksConfig.domain" must be either "HOSTING_DOMAIN" or "FIREBASE_DYNAMIC_LINK_DOMAIN".',
2017+
);
2018+
}
2019+
}
2020+
}
2021+
19682022
/**
19692023
* A password policy's enforcement state.
19702024
*/

src/auth/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ export {
103103
PasswordPolicyEnforcementState,
104104
CustomStrengthOptionsConfig,
105105
EmailPrivacyConfig,
106+
MobileLinksConfig,
107+
MobileLinksDomain,
106108
} from './auth-config';
107109

108110
export {

src/auth/project-config.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
PasswordPolicyConfig,
2929
EmailPrivacyConfig,
3030
EmailPrivacyAuthConfig,
31+
MobileLinksConfig,
32+
MobileLinksAuthConfig,
3133
} from './auth-config';
3234
import { deepCopy } from '../utils/deep-copy';
3335

@@ -59,6 +61,11 @@ export interface UpdateProjectConfigRequest {
5961
* The email privacy configuration to update on the project
6062
*/
6163
emailPrivacyConfig?: EmailPrivacyConfig;
64+
65+
/**
66+
* The mobile links configuration for the project
67+
*/
68+
mobileLinksConfig?: MobileLinksConfig;
6269
}
6370

6471
/**
@@ -70,6 +77,7 @@ export interface ProjectConfigServerResponse {
7077
recaptchaConfig?: RecaptchaConfig;
7178
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
7279
emailPrivacyConfig?: EmailPrivacyConfig;
80+
mobileLinksConfig?: MobileLinksConfig;
7381
}
7482

7583
/**
@@ -81,6 +89,7 @@ export interface ProjectConfigClientRequest {
8189
recaptchaConfig?: RecaptchaConfig;
8290
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
8391
emailPrivacyConfig?: EmailPrivacyConfig;
92+
mobileLinksConfig?: MobileLinksConfig;
8493
}
8594

8695
/**
@@ -122,6 +131,11 @@ export class ProjectConfig {
122131
*/
123132
public readonly emailPrivacyConfig?: EmailPrivacyConfig;
124133

134+
/**
135+
* The mobile links configuration for the project
136+
*/
137+
public readonly mobileLinksConfig?: MobileLinksConfig
138+
125139
/**
126140
* Validates a project config options object. Throws an error on failure.
127141
*
@@ -140,6 +154,7 @@ export class ProjectConfig {
140154
recaptchaConfig: true,
141155
passwordPolicyConfig: true,
142156
emailPrivacyConfig: true,
157+
mobileLinksConfig: true,
143158
}
144159
// Check for unsupported top level attributes.
145160
for (const key in request) {
@@ -173,6 +188,11 @@ export class ProjectConfig {
173188
if (typeof request.emailPrivacyConfig !== 'undefined') {
174189
EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
175190
}
191+
192+
// Validate Mobile Links Config if provided.
193+
if (typeof request.mobileLinksConfig !== 'undefined') {
194+
MobileLinksAuthConfig.validate(request.mobileLinksConfig);
195+
}
176196
}
177197

178198
/**
@@ -200,6 +220,9 @@ export class ProjectConfig {
200220
if (typeof configOptions.emailPrivacyConfig !== 'undefined') {
201221
request.emailPrivacyConfig = configOptions.emailPrivacyConfig;
202222
}
223+
if (typeof configOptions.mobileLinksConfig !== 'undefined') {
224+
request.mobileLinksConfig = configOptions.mobileLinksConfig;
225+
}
203226
return request;
204227
}
205228

@@ -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: this.recaptchaConfig_?.toJSON(),
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
}

src/auth/tenant.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
MultiFactorAuthConfig, SmsRegionConfig, SmsRegionsAuthConfig, RecaptchaAuthConfig, RecaptchaConfig,
2525
PasswordPolicyConfig,
2626
PasswordPolicyAuthConfig, PasswordPolicyAuthServerConfig, EmailPrivacyConfig, EmailPrivacyAuthConfig,
27+
MobileLinksConfig, MobileLinksAuthConfig
2728
} from './auth-config';
2829

2930
/**
@@ -77,6 +78,11 @@ export interface UpdateTenantRequest {
7778
* The email privacy configuration for the tenant
7879
*/
7980
emailPrivacyConfig?: EmailPrivacyConfig;
81+
82+
/**
83+
* The mobile links configuration for the project
84+
*/
85+
mobileLinksConfig?: MobileLinksConfig;
8086
}
8187

8288
/**
@@ -95,6 +101,7 @@ export interface TenantOptionsServerRequest extends EmailSignInConfigServerReque
95101
recaptchaConfig?: RecaptchaConfig;
96102
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
97103
emailPrivacyConfig?: EmailPrivacyConfig;
104+
mobileLinksConfig?: MobileLinksConfig;
98105
}
99106

100107
/** The tenant server response interface. */
@@ -110,6 +117,7 @@ export interface TenantServerResponse {
110117
recaptchaConfig? : RecaptchaConfig;
111118
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
112119
emailPrivacyConfig?: EmailPrivacyConfig;
120+
mobileLinksConfig?: MobileLinksConfig;
113121
}
114122

115123
/**
@@ -175,6 +183,11 @@ export class Tenant {
175183
* The email privacy configuration for the tenant
176184
*/
177185
public readonly emailPrivacyConfig?: EmailPrivacyConfig;
186+
/**
187+
* The mobile links configuration for the tenant
188+
*/
189+
public readonly mobileLinksConfig?: MobileLinksConfig
190+
178191

179192
/**
180193
* Builds the corresponding server request for a TenantOptions object.
@@ -217,6 +230,9 @@ export class Tenant {
217230
if (typeof tenantOptions.emailPrivacyConfig !== 'undefined') {
218231
request.emailPrivacyConfig = tenantOptions.emailPrivacyConfig;
219232
}
233+
if (typeof tenantOptions.mobileLinksConfig !== 'undefined') {
234+
request.mobileLinksConfig = tenantOptions.mobileLinksConfig;
235+
}
220236
return request;
221237
}
222238

@@ -254,6 +270,7 @@ export class Tenant {
254270
recaptchaConfig: true,
255271
passwordPolicyConfig: true,
256272
emailPrivacyConfig: true,
273+
mobileLinksConfig: true,
257274
};
258275
const label = createRequest ? 'CreateTenantRequest' : 'UpdateTenantRequest';
259276
if (!validator.isNonNullObject(request)) {
@@ -317,6 +334,10 @@ export class Tenant {
317334
if (typeof request.emailPrivacyConfig !== 'undefined') {
318335
EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
319336
}
337+
// Validate Mobile Links Config if provided.
338+
if (typeof request.mobileLinksConfig !== 'undefined') {
339+
MobileLinksAuthConfig.validate(request.mobileLinksConfig);
340+
}
320341
}
321342

322343
/**
@@ -363,6 +384,9 @@ export class Tenant {
363384
if (typeof response.emailPrivacyConfig !== 'undefined') {
364385
this.emailPrivacyConfig = deepCopy(response.emailPrivacyConfig);
365386
}
387+
if (typeof response.mobileLinksConfig !== 'undefined') {
388+
this.mobileLinksConfig = deepCopy(response.mobileLinksConfig);
389+
}
366390
}
367391

368392
/**
@@ -403,6 +427,7 @@ export class Tenant {
403427
recaptchaConfig: this.recaptchaConfig_?.toJSON(),
404428
passwordPolicyConfig: deepCopy(this.passwordPolicyConfig),
405429
emailPrivacyConfig: deepCopy(this.emailPrivacyConfig),
430+
mobileLinksConfig: deepCopy(this.mobileLinksConfig),
406431
};
407432
if (typeof json.multiFactorConfig === 'undefined') {
408433
delete json.multiFactorConfig;
@@ -422,6 +447,9 @@ export class Tenant {
422447
if (typeof json.emailPrivacyConfig === 'undefined') {
423448
delete json.emailPrivacyConfig;
424449
}
450+
if (typeof json.mobileLinksConfig === 'undefined') {
451+
delete json.mobileLinksConfig;
452+
}
425453
return json;
426454
}
427455
}

test/unit/auth/auth-config.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
MAXIMUM_TEST_PHONE_NUMBERS,
2929
PasswordPolicyAuthConfig,
3030
CustomStrengthOptionsConfig,
31+
MobileLinksAuthConfig,
32+
MobileLinksConfig,
3133
} from '../../../src/auth/auth-config';
3234
import {
3335
SAMLUpdateAuthProviderRequest, OIDCUpdateAuthProviderRequest,
@@ -1322,3 +1324,30 @@ describe('PasswordPolicyAuthConfig',() => {
13221324
});
13231325
});
13241326
});
1327+
1328+
describe('MobileLinksAuthConfig',() => {
1329+
describe('validate',() => {
1330+
it('should throw an error on invalid MobileLinksConfig key',() => {
1331+
const config: any = {
1332+
link: 'HOSTING_DOMAIN'
1333+
};
1334+
expect(() =>
1335+
MobileLinksAuthConfig.validate(config)
1336+
).to.throw('"link" is not a valid "MobileLinksConfig" parameter.');
1337+
});
1338+
1339+
it('should throw an error on invalid MobileLinksDomain',() => {
1340+
const config: any = {
1341+
domain: 'WRONG_DOMAIN'
1342+
};
1343+
expect(() => MobileLinksAuthConfig.validate(config))
1344+
.to.throw('"MobileLinksConfig.domain" must be either "HOSTING_DOMAIN" or "FIREBASE_DYNAMIC_LINK_DOMAIN".');
1345+
});//
1346+
it('should now throw an error on valid MobileLinksConfig',() => {
1347+
const config: MobileLinksConfig = {
1348+
domain: 'HOSTING_DOMAIN'
1349+
};
1350+
expect(() => MobileLinksAuthConfig.validate(config)).not.to.throw();
1351+
});
1352+
});
1353+
});

0 commit comments

Comments
 (0)