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

feat(auth): Add Email Privacy support in Project and Tenant config #2198

Merged
merged 16 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
9 changes: 9 additions & 0 deletions etc/firebase-admin.auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ export interface EmailIdentifier {
email: string;
}

// @public
export interface EmailPrivacyConfig {
enableImprovedEmailPrivacy?: boolean;
}

// @public
export interface EmailSignInProviderConfig {
enabled: boolean;
Expand Down Expand Up @@ -363,6 +368,7 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo {

// @public
export class ProjectConfig {
readonly emailPrivacyConfig?: EmailPrivacyConfig;
get multiFactorConfig(): MultiFactorConfig | undefined;
readonly passwordPolicyConfig?: PasswordPolicyConfig;
get recaptchaConfig(): RecaptchaConfig | undefined;
Expand Down Expand Up @@ -446,6 +452,7 @@ export class Tenant {
// (undocumented)
readonly anonymousSignInEnabled: boolean;
readonly displayName?: string;
readonly emailPrivacyConfig?: EmailPrivacyConfig;
get emailSignInConfig(): EmailSignInProviderConfig | undefined;
get multiFactorConfig(): MultiFactorConfig | undefined;
readonly passwordPolicyConfig?: PasswordPolicyConfig;
Expand Down Expand Up @@ -500,6 +507,7 @@ export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactor

// @public
export interface UpdateProjectConfigRequest {
emailPrivacyConfig?: EmailPrivacyConfig;
multiFactorConfig?: MultiFactorConfig;
passwordPolicyConfig?: PasswordPolicyConfig;
recaptchaConfig?: RecaptchaConfig;
Expand All @@ -524,6 +532,7 @@ export interface UpdateRequest {
export interface UpdateTenantRequest {
anonymousSignInEnabled?: boolean;
displayName?: string;
emailPrivacyConfig?: EmailPrivacyConfig;
emailSignInConfig?: EmailSignInProviderConfig;
multiFactorConfig?: MultiFactorConfig;
passwordPolicyConfig?: PasswordPolicyConfig;
Expand Down
47 changes: 47 additions & 0 deletions src/auth/auth-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2281,3 +2281,50 @@ export interface CustomStrengthOptionsAuthServerConfig {
minPasswordLength?: number;
maxPasswordLength?: number;
}

/**
* The configuration for the email privacy on the project or tenant.
*/
pragatimodi marked this conversation as resolved.
Show resolved Hide resolved
export interface EmailPrivacyConfig {
/**
* Variable indicating email privacy enabled of not.
pragatimodi marked this conversation as resolved.
Show resolved Hide resolved
*/
enableImprovedEmailPrivacy?: boolean;
}

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

const validKeys = {
enableImprovedEmailPrivacy: true,
};

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

if (typeof options.enableImprovedEmailPrivacy !== 'undefined'
&& !validator.isBoolean(options.enableImprovedEmailPrivacy)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_CONFIG,
'"EmailPrivacyConfig.enableImprovedEmailPrivacy" must be a valid boolean value.',
);
}
}
}
3 changes: 2 additions & 1 deletion src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ export {
UpdateRequest,
TotpMultiFactorProviderConfig,
PasswordPolicyConfig,
PasswordPolicyEnforcementState,
PasswordPolicyEnforcementState,
CustomStrengthOptionsConfig,
EmailPrivacyConfig,
} from './auth-config';

export {
Expand Down
41 changes: 34 additions & 7 deletions src/auth/project-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
PasswordPolicyAuthConfig,
PasswordPolicyAuthServerConfig,
PasswordPolicyConfig,
EmailPrivacyConfig,
EmailPrivacyAuthConfig,
} from './auth-config';
import { deepCopy } from '../utils/deep-copy';

Expand Down Expand Up @@ -53,6 +55,10 @@ export interface UpdateProjectConfigRequest {
* The password policy configuration to update on the project
*/
passwordPolicyConfig?: PasswordPolicyConfig;
/**
* The email privacy configuration to update on the project
*/
emailPrivacyConfig?: EmailPrivacyConfig;
}

/**
Expand All @@ -63,6 +69,7 @@ export interface ProjectConfigServerResponse {
mfa?: MultiFactorAuthServerConfig;
recaptchaConfig?: RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
}

/**
Expand All @@ -73,6 +80,7 @@ export interface ProjectConfigClientRequest {
mfa?: MultiFactorAuthServerConfig;
recaptchaConfig?: RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
}

/**
Expand All @@ -91,7 +99,12 @@ export class ProjectConfig {
* Supports only phone and TOTP.
*/
private readonly multiFactorConfig_?: MultiFactorConfig;

/**
* The multi-factor auth configuration.
*/
get multiFactorConfig(): MultiFactorConfig | undefined {
return this.multiFactorConfig_;
}
/**
* The reCAPTCHA configuration to update on the project.
* By enabling reCAPTCHA Enterprise integration, you are
Expand All @@ -100,16 +113,14 @@ export class ProjectConfig {
*/
private readonly recaptchaConfig_?: RecaptchaAuthConfig;

/**
* The multi-factor auth configuration.
*/
get multiFactorConfig(): MultiFactorConfig | undefined {
return this.multiFactorConfig_;
}
/**
* The password policy configuration for the project
*/
public readonly passwordPolicyConfig?: PasswordPolicyConfig;
/**
* The email privacy configuration for the project
*/
public readonly emailPrivacyConfig?: EmailPrivacyConfig;

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

// Validate Email Privacy Config if provided.
if (typeof request.emailPrivacyConfig !== 'undefined') {
EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
}
}

/**
Expand All @@ -180,6 +197,9 @@ export class ProjectConfig {
if (typeof configOptions.passwordPolicyConfig !== 'undefined') {
request.passwordPolicyConfig = PasswordPolicyAuthConfig.buildServerRequest(configOptions.passwordPolicyConfig);
}
if (typeof configOptions.emailPrivacyConfig !== 'undefined') {
request.emailPrivacyConfig = configOptions.emailPrivacyConfig;
}
return request;
}

Expand Down Expand Up @@ -211,6 +231,9 @@ export class ProjectConfig {
if (typeof response.passwordPolicyConfig !== 'undefined') {
this.passwordPolicyConfig = new PasswordPolicyAuthConfig(response.passwordPolicyConfig);
}
if (typeof response.emailPrivacyConfig !== 'undefined') {
this.emailPrivacyConfig = response.emailPrivacyConfig;
}
}
/**
* Returns a JSON-serializable representation of this object.
Expand All @@ -224,6 +247,7 @@ export class ProjectConfig {
multiFactorConfig: deepCopy(this.multiFactorConfig),
recaptchaConfig: this.recaptchaConfig_?.toJSON(),
passwordPolicyConfig: deepCopy(this.passwordPolicyConfig),
emailPrivacyConfig: deepCopy(this.emailPrivacyConfig),
};
if (typeof json.smsRegionConfig === 'undefined') {
delete json.smsRegionConfig;
Expand All @@ -237,6 +261,9 @@ export class ProjectConfig {
if (typeof json.passwordPolicyConfig === 'undefined') {
delete json.passwordPolicyConfig;
}
if (typeof json.emailPrivacyConfig === 'undefined') {
delete json.emailPrivacyConfig;
}
return json;
}
}
Expand Down
29 changes: 27 additions & 2 deletions src/auth/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {
EmailSignInConfig, EmailSignInConfigServerRequest, MultiFactorAuthServerConfig,
MultiFactorConfig, validateTestPhoneNumbers, EmailSignInProviderConfig,
MultiFactorAuthConfig, SmsRegionConfig, SmsRegionsAuthConfig, RecaptchaAuthConfig, RecaptchaConfig,
PasswordPolicyConfig,
PasswordPolicyAuthConfig, PasswordPolicyAuthServerConfig,
PasswordPolicyConfig,
PasswordPolicyAuthConfig, PasswordPolicyAuthServerConfig, EmailPrivacyConfig, EmailPrivacyAuthConfig,
} from './auth-config';

/**
Expand Down Expand Up @@ -73,6 +73,10 @@ export interface UpdateTenantRequest {
* The password policy configuration for the tenant
*/
passwordPolicyConfig?: PasswordPolicyConfig;
/**
* The email privacy configuration for the tenant
*/
emailPrivacyConfig?: EmailPrivacyConfig;
}

/**
Expand All @@ -90,6 +94,7 @@ export interface TenantOptionsServerRequest extends EmailSignInConfigServerReque
smsRegionConfig?: SmsRegionConfig;
recaptchaConfig?: RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
}

/** The tenant server response interface. */
Expand All @@ -104,6 +109,7 @@ export interface TenantServerResponse {
smsRegionConfig?: SmsRegionConfig;
recaptchaConfig? : RecaptchaConfig;
passwordPolicyConfig?: PasswordPolicyAuthServerConfig;
emailPrivacyConfig?: EmailPrivacyConfig;
}

/**
Expand Down Expand Up @@ -165,6 +171,10 @@ export class Tenant {
* The password policy configuration for the tenant
*/
public readonly passwordPolicyConfig?: PasswordPolicyConfig;
/**
* The email privacy configuration for the tenant
*/
public readonly emailPrivacyConfig?: EmailPrivacyConfig;

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

Expand Down Expand Up @@ -240,6 +253,7 @@ export class Tenant {
smsRegionConfig: true,
recaptchaConfig: true,
passwordPolicyConfig: true,
emailPrivacyConfig: true,
};
const label = createRequest ? 'CreateTenantRequest' : 'UpdateTenantRequest';
if (!validator.isNonNullObject(request)) {
Expand Down Expand Up @@ -299,6 +313,10 @@ export class Tenant {
// This will throw an error if invalid.
PasswordPolicyAuthConfig.buildServerRequest(request.passwordPolicyConfig);
}
// Validate Email Privacy Config if provided.
if (typeof request.emailPrivacyConfig !== 'undefined') {
EmailPrivacyAuthConfig.validate(request.emailPrivacyConfig);
}
}

/**
Expand Down Expand Up @@ -342,6 +360,9 @@ export class Tenant {
if (typeof response.passwordPolicyConfig !== 'undefined') {
this.passwordPolicyConfig = new PasswordPolicyAuthConfig(response.passwordPolicyConfig);
}
if (typeof response.emailPrivacyConfig !== 'undefined') {
this.emailPrivacyConfig = deepCopy(response.emailPrivacyConfig);
}
}

/**
Expand Down Expand Up @@ -381,6 +402,7 @@ export class Tenant {
smsRegionConfig: deepCopy(this.smsRegionConfig),
recaptchaConfig: this.recaptchaConfig_?.toJSON(),
passwordPolicyConfig: deepCopy(this.passwordPolicyConfig),
emailPrivacyConfig: deepCopy(this.emailPrivacyConfig),
};
if (typeof json.multiFactorConfig === 'undefined') {
delete json.multiFactorConfig;
Expand All @@ -397,6 +419,9 @@ export class Tenant {
if (typeof json.passwordPolicyConfig === 'undefined') {
delete json.passwordPolicyConfig;
}
if (typeof json.emailPrivacyConfig === 'undefined') {
delete json.emailPrivacyConfig;
}
return json;
}
}
Expand Down
Loading