Skip to content

Commit

Permalink
feat(auth): Add Email Privacy support in Project and Tenant config (#…
Browse files Browse the repository at this point in the history
…2198)

* EmailPrivacy Config definition

* Adding unit tests

* Adding integration tests

* api-extractor changes

* trim whitespace changes

* Fix typo : Update auth-config.ts

* Addressing feedback

* Apply suggestions from code review

Co-authored-by: Kevin Cheung <kevinthecheung@users.noreply.github.com>

---------

Co-authored-by: Kevin Cheung <kevinthecheung@users.noreply.github.com>
  • Loading branch information
pragatimodi and kevinthecheung authored Sep 7, 2023
1 parent a4ce27d commit a4019e4
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 11 deletions.
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 @@ -2279,3 +2279,50 @@ export interface CustomStrengthOptionsAuthServerConfig {
minPasswordLength?: number;
maxPasswordLength?: number;
}

/**
* The email privacy configuration of a project or tenant.
*/
export interface EmailPrivacyConfig {
/**
* Whether enhanced email privacy is enabled.
*/
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

0 comments on commit a4019e4

Please sign in to comment.