Skip to content
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

[FDL deprecation #1] Added mobileLinksConfig field for project and tenant configs #2659

Merged
merged 1 commit into from
Oct 15, 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
12 changes: 12 additions & 0 deletions etc/firebase-admin.auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,14 @@ export interface ListUsersResult {
users: UserRecord[];
}

// @public
export interface MobileLinksConfig {
domain?: MobileLinksDomain;
}

// @public
export type MobileLinksDomain = 'HOSTING_DOMAIN' | 'FIREBASE_DYNAMIC_LINK_DOMAIN';

// @public
export interface MultiFactorConfig {
factorIds?: AuthFactorType[];
Expand Down Expand Up @@ -849,6 +857,7 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo {
// @public
export class ProjectConfig {
readonly emailPrivacyConfig?: EmailPrivacyConfig;
readonly mobileLinksConfig?: MobileLinksConfig;
get multiFactorConfig(): MultiFactorConfig | undefined;
readonly passwordPolicyConfig?: PasswordPolicyConfig;
get recaptchaConfig(): RecaptchaConfig | undefined;
Expand Down Expand Up @@ -934,6 +943,7 @@ export class Tenant {
readonly displayName?: string;
readonly emailPrivacyConfig?: EmailPrivacyConfig;
get emailSignInConfig(): EmailSignInProviderConfig | undefined;
readonly mobileLinksConfig?: MobileLinksConfig;
get multiFactorConfig(): MultiFactorConfig | undefined;
readonly passwordPolicyConfig?: PasswordPolicyConfig;
get recaptchaConfig(): RecaptchaConfig | undefined;
Expand Down Expand Up @@ -988,6 +998,7 @@ export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactor
// @public
export interface UpdateProjectConfigRequest {
emailPrivacyConfig?: EmailPrivacyConfig;
mobileLinksConfig?: MobileLinksConfig;
multiFactorConfig?: MultiFactorConfig;
passwordPolicyConfig?: PasswordPolicyConfig;
recaptchaConfig?: RecaptchaConfig;
Expand All @@ -1014,6 +1025,7 @@ export interface UpdateTenantRequest {
displayName?: string;
emailPrivacyConfig?: EmailPrivacyConfig;
emailSignInConfig?: EmailSignInProviderConfig;
mobileLinksConfig?: MobileLinksConfig;
multiFactorConfig?: MultiFactorConfig;
passwordPolicyConfig?: PasswordPolicyConfig;
recaptchaConfig?: RecaptchaConfig;
Expand Down
54 changes: 54 additions & 0 deletions src/auth/auth-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,60 @@ export interface PasswordPolicyConfig {
constraints?: CustomStrengthOptionsConfig;
}

/**
* Configuration for settings related to univeral links (iOS)
* and app links (Android).
*/
export interface MobileLinksConfig {
/**
* Use firebase Hosting or dynamic link domain as the out-of-band code domain.
*/
domain?: MobileLinksDomain;
}

/**
* Open code in app domain to use for app links and universal links.
*/
export type MobileLinksDomain = 'HOSTING_DOMAIN' | 'FIREBASE_DYNAMIC_LINK_DOMAIN';

/**
* Defines the MobileLinksAuthConfig class used for validation.
*
* @internal
*/
export class MobileLinksAuthConfig {
public static validate(options: MobileLinksConfig): void {
if (!validator.isNonNullObject(options)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_CONFIG,
'"MobileLinksConfig" must be a non-null object.',
);
}

const validKeys = {
domain: true,
};

for (const key in options) {
if (!(key in validKeys)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_CONFIG,
`"${key}" is not a valid "MobileLinksConfig" parameter.`,
);
}
}

if (typeof options.domain !== 'undefined'
&& options.domain !== 'HOSTING_DOMAIN'
&& options.domain !== 'FIREBASE_DYNAMIC_LINK_DOMAIN') {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_CONFIG,
'"MobileLinksConfig.domain" must be either "HOSTING_DOMAIN" or "FIREBASE_DYNAMIC_LINK_DOMAIN".',
);
}
}
}

/**
* A password policy's enforcement state.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ export {
PasswordPolicyEnforcementState,
CustomStrengthOptionsConfig,
EmailPrivacyConfig,
MobileLinksConfig,
MobileLinksDomain,
} from './auth-config';

export {
Expand Down
30 changes: 30 additions & 0 deletions src/auth/project-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
PasswordPolicyConfig,
EmailPrivacyConfig,
EmailPrivacyAuthConfig,
MobileLinksConfig,
MobileLinksAuthConfig,
} from './auth-config';
import { deepCopy } from '../utils/deep-copy';

Expand Down Expand Up @@ -59,6 +61,11 @@ export interface UpdateProjectConfigRequest {
* The email privacy configuration to update on the project
*/
emailPrivacyConfig?: EmailPrivacyConfig;

/**
* The mobile links configuration for the project
*/
mobileLinksConfig?: MobileLinksConfig;
}

/**
Expand All @@ -70,6 +77,7 @@ export interface ProjectConfigServerResponse {
recaptchaConfig?: RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
mobileLinksConfig?: MobileLinksConfig;
}

/**
Expand All @@ -81,6 +89,7 @@ export interface ProjectConfigClientRequest {
recaptchaConfig?: RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
mobileLinksConfig?: MobileLinksConfig;
}

/**
Expand Down Expand Up @@ -122,6 +131,11 @@ export class ProjectConfig {
*/
public readonly emailPrivacyConfig?: EmailPrivacyConfig;

/**
* The mobile links configuration for the project
*/
public readonly mobileLinksConfig?: MobileLinksConfig

/**
* Validates a project config options object. Throws an error on failure.
*
Expand All @@ -140,6 +154,7 @@ export class ProjectConfig {
recaptchaConfig: true,
passwordPolicyConfig: true,
emailPrivacyConfig: true,
mobileLinksConfig: true,
}
// Check for unsupported top level attributes.
for (const key in request) {
Expand Down Expand Up @@ -173,6 +188,11 @@ export class ProjectConfig {
if (typeof request.emailPrivacyConfig !== 'undefined') {
EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
}

// Validate Mobile Links Config if provided.
if (typeof request.mobileLinksConfig !== 'undefined') {
MobileLinksAuthConfig.validate(request.mobileLinksConfig);
}
}

/**
Expand Down Expand Up @@ -200,6 +220,9 @@ export class ProjectConfig {
if (typeof configOptions.emailPrivacyConfig !== 'undefined') {
request.emailPrivacyConfig = configOptions.emailPrivacyConfig;
}
if (typeof configOptions.mobileLinksConfig !== 'undefined') {
request.mobileLinksConfig = configOptions.mobileLinksConfig;
}
return request;
}

Expand Down Expand Up @@ -234,6 +257,9 @@ export class ProjectConfig {
if (typeof response.emailPrivacyConfig !== 'undefined') {
this.emailPrivacyConfig = response.emailPrivacyConfig;
}
if (typeof response.mobileLinksConfig !== 'undefined') {
this.mobileLinksConfig = response.mobileLinksConfig;
}
}
/**
* Returns a JSON-serializable representation of this object.
Expand All @@ -248,6 +274,7 @@ export class ProjectConfig {
recaptchaConfig: this.recaptchaConfig_?.toJSON(),
passwordPolicyConfig: deepCopy(this.passwordPolicyConfig),
emailPrivacyConfig: deepCopy(this.emailPrivacyConfig),
mobileLinksConfig: deepCopy(this.mobileLinksConfig),
};
if (typeof json.smsRegionConfig === 'undefined') {
delete json.smsRegionConfig;
Expand All @@ -264,6 +291,9 @@ export class ProjectConfig {
if (typeof json.emailPrivacyConfig === 'undefined') {
delete json.emailPrivacyConfig;
}
if (typeof json.mobileLinksConfig === 'undefined') {
delete json.mobileLinksConfig;
}
return json;
}
}
Expand Down
28 changes: 28 additions & 0 deletions src/auth/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
MultiFactorAuthConfig, SmsRegionConfig, SmsRegionsAuthConfig, RecaptchaAuthConfig, RecaptchaConfig,
PasswordPolicyConfig,
PasswordPolicyAuthConfig, PasswordPolicyAuthServerConfig, EmailPrivacyConfig, EmailPrivacyAuthConfig,
MobileLinksConfig, MobileLinksAuthConfig
} from './auth-config';

/**
Expand Down Expand Up @@ -77,6 +78,11 @@ export interface UpdateTenantRequest {
* The email privacy configuration for the tenant
*/
emailPrivacyConfig?: EmailPrivacyConfig;

/**
* The mobile links configuration for the project
*/
mobileLinksConfig?: MobileLinksConfig;
}

/**
Expand All @@ -95,6 +101,7 @@ export interface TenantOptionsServerRequest extends EmailSignInConfigServerReque
recaptchaConfig?: RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
mobileLinksConfig?: MobileLinksConfig;
}

/** The tenant server response interface. */
Expand All @@ -110,6 +117,7 @@ export interface TenantServerResponse {
recaptchaConfig? : RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
mobileLinksConfig?: MobileLinksConfig;
}

/**
Expand Down Expand Up @@ -175,6 +183,11 @@ export class Tenant {
* The email privacy configuration for the tenant
*/
public readonly emailPrivacyConfig?: EmailPrivacyConfig;
/**
* The mobile links configuration for the tenant
*/
public readonly mobileLinksConfig?: MobileLinksConfig


/**
* Builds the corresponding server request for a TenantOptions object.
Expand Down Expand Up @@ -217,6 +230,9 @@ export class Tenant {
if (typeof tenantOptions.emailPrivacyConfig !== 'undefined') {
request.emailPrivacyConfig = tenantOptions.emailPrivacyConfig;
}
if (typeof tenantOptions.mobileLinksConfig !== 'undefined') {
request.mobileLinksConfig = tenantOptions.mobileLinksConfig;
}
return request;
}

Expand Down Expand Up @@ -254,6 +270,7 @@ export class Tenant {
recaptchaConfig: true,
passwordPolicyConfig: true,
emailPrivacyConfig: true,
mobileLinksConfig: true,
};
const label = createRequest ? 'CreateTenantRequest' : 'UpdateTenantRequest';
if (!validator.isNonNullObject(request)) {
Expand Down Expand Up @@ -317,6 +334,10 @@ export class Tenant {
if (typeof request.emailPrivacyConfig !== 'undefined') {
EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
}
// Validate Mobile Links Config if provided.
if (typeof request.mobileLinksConfig !== 'undefined') {
MobileLinksAuthConfig.validate(request.mobileLinksConfig);
}
}

/**
Expand Down Expand Up @@ -363,6 +384,9 @@ export class Tenant {
if (typeof response.emailPrivacyConfig !== 'undefined') {
this.emailPrivacyConfig = deepCopy(response.emailPrivacyConfig);
}
if (typeof response.mobileLinksConfig !== 'undefined') {
this.mobileLinksConfig = deepCopy(response.mobileLinksConfig);
}
}

/**
Expand Down Expand Up @@ -403,6 +427,7 @@ export class Tenant {
recaptchaConfig: this.recaptchaConfig_?.toJSON(),
passwordPolicyConfig: deepCopy(this.passwordPolicyConfig),
emailPrivacyConfig: deepCopy(this.emailPrivacyConfig),
mobileLinksConfig: deepCopy(this.mobileLinksConfig),
};
if (typeof json.multiFactorConfig === 'undefined') {
delete json.multiFactorConfig;
Expand All @@ -422,6 +447,9 @@ export class Tenant {
if (typeof json.emailPrivacyConfig === 'undefined') {
delete json.emailPrivacyConfig;
}
if (typeof json.mobileLinksConfig === 'undefined') {
delete json.mobileLinksConfig;
}
return json;
}
}
Expand Down
29 changes: 29 additions & 0 deletions test/unit/auth/auth-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
MAXIMUM_TEST_PHONE_NUMBERS,
PasswordPolicyAuthConfig,
CustomStrengthOptionsConfig,
MobileLinksAuthConfig,
MobileLinksConfig,
} from '../../../src/auth/auth-config';
import {
SAMLUpdateAuthProviderRequest, OIDCUpdateAuthProviderRequest,
Expand Down Expand Up @@ -1322,3 +1324,30 @@ describe('PasswordPolicyAuthConfig',() => {
});
});
});

describe('MobileLinksAuthConfig',() => {
describe('validate',() => {
it('should throw an error on invalid MobileLinksConfig key',() => {
const config: any = {
link: 'HOSTING_DOMAIN'
};
expect(() =>
MobileLinksAuthConfig.validate(config)
).to.throw('"link" is not a valid "MobileLinksConfig" parameter.');
});

it('should throw an error on invalid MobileLinksDomain',() => {
const config: any = {
domain: 'WRONG_DOMAIN'
};
expect(() => MobileLinksAuthConfig.validate(config))
.to.throw('"MobileLinksConfig.domain" must be either "HOSTING_DOMAIN" or "FIREBASE_DYNAMIC_LINK_DOMAIN".');
});//
it('should now throw an error on valid MobileLinksConfig',() => {
const config: MobileLinksConfig = {
domain: 'HOSTING_DOMAIN'
};
expect(() => MobileLinksAuthConfig.validate(config)).not.to.throw();
});
});
});
Loading