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

Added FDL deprecation support for Firebase Auth #2752

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
17 changes: 17 additions & 0 deletions etc/firebase-admin.auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ export interface ActionCodeSettings {
installApp?: boolean;
minimumVersion?: string;
};
// @deprecated
dynamicLinkDomain?: string;
handleCodeInApp?: boolean;
iOS?: {
bundleId: string;
};
linkDomain?: string;
url: string;
}

Expand Down Expand Up @@ -223,6 +225,11 @@ export class AuthClientErrorCode {
message: string;
};
// (undocumented)
static INVALID_HOSTING_LINK_DOMAIN: {
code: string;
message: string;
};
// (undocumented)
static INVALID_ID_TOKEN: {
code: string;
message: string;
Expand Down Expand Up @@ -759,6 +766,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 +864,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 @@ -998,6 +1014,7 @@ export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactor
// @public
export interface UpdateProjectConfigRequest {
emailPrivacyConfig?: EmailPrivacyConfig;
mobileLinksConfig?: MobileLinksConfig;
multiFactorConfig?: MultiFactorConfig;
passwordPolicyConfig?: PasswordPolicyConfig;
recaptchaConfig?: RecaptchaConfig;
Expand Down
22 changes: 22 additions & 0 deletions src/auth/action-code-settings-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,27 @@ export interface ActionCodeSettings {
* configured per project. This field provides the ability to explicitly choose
* configured per project. This fields provides the ability explicitly choose
* one. If none is provided, the oldest domain is used by default.
* @deprecated use linkDomain instead
*/
dynamicLinkDomain?: string;

/**
* Defines the custom Firebase Hosting domain to use when the link is to be opened
* via a specified mobile app,
* This is a replacement of Firebase Dynamic Link.
* If none is provided,
* a default hosting domain will be used (for example, `example.firebaseapp.com`)
*/

linkDomain?: string;
}

/** Defines the email action code server request. */
interface EmailActionCodeRequest {
continueUrl?: string;
canHandleCodeInApp?: boolean;
dynamicLinkDomain?: string;
linkDomain?: string;
androidPackageName?: string;
androidMinimumVersion: string;
androidInstallApp?: boolean;
Expand All @@ -123,6 +135,7 @@ export class ActionCodeSettingsBuilder {
private ibi?: string;
private canHandleCodeInApp?: boolean;
private dynamicLinkDomain?: string;
private linkDomain?: string;

/**
* ActionCodeSettingsBuilder constructor.
Expand Down Expand Up @@ -166,6 +179,14 @@ export class ActionCodeSettingsBuilder {
}
this.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;

if (typeof actionCodeSettings.linkDomain !== 'undefined' &&
!validator.isNonEmptyString(actionCodeSettings.linkDomain)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HOSTING_LINK_DOMAIN,
);
}
this.linkDomain = actionCodeSettings.linkDomain;

if (typeof actionCodeSettings.iOS !== 'undefined') {
if (!validator.isNonNullObject(actionCodeSettings.iOS)) {
throw new FirebaseAuthError(
Expand Down Expand Up @@ -230,6 +251,7 @@ export class ActionCodeSettingsBuilder {
continueUrl: this.continueUrl,
canHandleCodeInApp: this.canHandleCodeInApp,
dynamicLinkDomain: this.dynamicLinkDomain,
linkDomain: this.linkDomain,
androidPackageName: this.apn,
androidMinimumVersion: this.amv,
androidInstallApp: this.installApp,
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 @@ -2137,6 +2137,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
6 changes: 3 additions & 3 deletions src/auth/base-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ export abstract class BaseAuth {
* minimumVersion: '12'
* },
* handleCodeInApp: true,
* dynamicLinkDomain: 'custom.page.link'
* linkDomain: 'project-id.firebaseapp.com'
* };
* admin.auth()
* .generatePasswordResetLink('user@example.com', actionCodeSettings)
Expand Down Expand Up @@ -802,7 +802,7 @@ export abstract class BaseAuth {
* minimumVersion: '12'
* },
* handleCodeInApp: true,
* dynamicLinkDomain: 'custom.page.link'
* linkDomain: 'project-id.firebaseapp.com'
* };
* admin.auth()
* .generateEmailVerificationLink('user@example.com', actionCodeSettings)
Expand Down Expand Up @@ -883,7 +883,7 @@ export abstract class BaseAuth {
* minimumVersion: '12'
* },
* handleCodeInApp: true,
* dynamicLinkDomain: 'custom.page.link'
* linkDomain: 'project-id.firebaseapp.com'
* };
* admin.auth()
* .generateEmailVerificationLink('user@example.com', actionCodeSettings)
Expand Down
2 changes: 2 additions & 0 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,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 @@ -29,6 +29,8 @@ import {
PasswordPolicyConfig,
EmailPrivacyConfig,
EmailPrivacyAuthConfig,
MobileLinksConfig,
MobileLinksAuthConfig,
} from './auth-config';
import { deepCopy } from '../utils/deep-copy';

Expand Down Expand Up @@ -60,6 +62,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 @@ -71,6 +78,7 @@ export interface ProjectConfigServerResponse {
recaptchaConfig?: RecaptchaAuthServerConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
mobileLinksConfig?: MobileLinksConfig;
}

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

/**
Expand Down Expand Up @@ -128,6 +137,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 @@ -146,6 +160,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 @@ -179,6 +194,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 @@ -206,6 +226,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: deepCopy(this.recaptchaConfig),
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
7 changes: 7 additions & 0 deletions src/utils/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,11 @@ export class AuthClientErrorCode {
message: 'The provided dynamic link domain is not configured or authorized ' +
'for the current project.',
};
public static INVALID_HOSTING_LINK_DOMAIN = {
code: 'invalid-hosting-link-domain',
message: 'The provided hosting link domain is not configured in Firebase ' +
'Hosting or is not owned by the current project.',
};
public static INVALID_EMAIL_VERIFIED = {
code: 'invalid-email-verified',
message: 'The emailVerified field must be a boolean.',
Expand Down Expand Up @@ -933,6 +938,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = {
INVALID_CONTINUE_URI: 'INVALID_CONTINUE_URI',
// Dynamic link domain in provided ActionCodeSettings is not authorized.
INVALID_DYNAMIC_LINK_DOMAIN: 'INVALID_DYNAMIC_LINK_DOMAIN',
// Hosting link domain in provided ActionCodeSettings is not owned by the current project.
INVALID_HOSTING_LINK_DOMAIN: 'INVALID_HOSTING_LINK_DOMAIN',
// uploadAccount provides an email that already exists.
DUPLICATE_EMAIL: 'EMAIL_ALREADY_EXISTS',
// uploadAccount provides a localId that already exists.
Expand Down
Loading