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

Project config - Recaptcha config #1595

Merged
merged 8 commits into from
Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
18 changes: 18 additions & 0 deletions etc/firebase-admin.auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface ActionCodeSettings {
export class Auth extends BaseAuth {
// Warning: (ae-forgotten-export) The symbol "App" needs to be exported by the entry point index.d.ts
get app(): App;
projectConfigManager(): ProjectConfigManager;
tenantManager(): TenantManager;
}

Expand Down Expand Up @@ -256,6 +257,18 @@ export class PhoneMultiFactorInfo extends MultiFactorInfo {
toJSON(): object;
}

// @public
export class ProjectConfig {
get recaptchaConfig(): RecaptchaConfig | undefined;
toJSON(): object;
}

// @public
export class ProjectConfigManager {
getProjectConfig(): Promise<ProjectConfig>;
updateProjectConfig(projectConfigOptions: UpdateProjectConfigRequest): Promise<ProjectConfig>;
}

// @public
export interface ProviderIdentifier {
// (undocumented)
Expand Down Expand Up @@ -367,6 +380,11 @@ export interface UpdatePhoneMultiFactorInfoRequest extends BaseUpdateMultiFactor
phoneNumber: string;
}

// @public
export interface UpdateProjectConfigRequest {
recaptchaConfig?: RecaptchaConfig;
}

// @public
export interface UpdateRequest {
disabled?: boolean;
Expand Down
72 changes: 62 additions & 10 deletions src/auth/auth-api-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
OIDCAuthProviderConfig, SAMLAuthProviderConfig, OIDCUpdateAuthProviderRequest,
SAMLUpdateAuthProviderRequest
} from './auth-config';
import { ProjectConfig, ProjectConfigServerResponse, UpdateProjectConfigRequest } from './project-config';

/** Firebase Auth request header. */
const FIREBASE_AUTH_HEADER = {
Expand Down Expand Up @@ -102,7 +103,6 @@ const FIREBASE_AUTH_TENANT_URL_FORMAT = FIREBASE_AUTH_BASE_URL_FORMAT.replace(
const FIREBASE_AUTH_EMULATOR_TENANT_URL_FORMAT = FIREBASE_AUTH_EMULATOR_BASE_URL_FORMAT.replace(
'projects/{projectId}', 'projects/{projectId}/tenants/{tenantId}');


/** Maximum allowed number of tenants to download at one time. */
const MAX_LIST_TENANT_PAGE_SIZE = 1000;

Expand Down Expand Up @@ -1961,6 +1961,29 @@ export abstract class AbstractAuthRequestHandler {
}
}

/** Instantiates the getConfig endpoint settings. */
const GET_PROJECT_CONFIG = new ApiSettings('/config', 'GET')
.setResponseValidator((response: any) => {
// Response should always contain at least the config name.
if (!validator.isNonEmptyString(response.name)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Unable to get project config',
);
}
});

/** Instantiates the updateConfig endpoint settings. */
const UPDATE_PROJECT_CONFIG = new ApiSettings('/config?updateMask={updateMask}', 'PATCH')
.setResponseValidator((response: any) => {
// Response should always contain at least the config name.
if (!validator.isNonEmptyString(response.name)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INTERNAL_ERROR,
'INTERNAL ASSERT FAILED: Unable to update project config',
);
}
});

/** Instantiates the getTenant endpoint settings. */
const GET_TENANT = new ApiSettings('/tenants/{tenantId}', 'GET')
Expand Down Expand Up @@ -2029,13 +2052,13 @@ const CREATE_TENANT = new ApiSettings('/tenants', 'POST')


/**
* Utility for sending requests to Auth server that are Auth instance related. This includes user and
* tenant management related APIs. This extends the BaseFirebaseAuthRequestHandler class and defines
* Utility for sending requests to Auth server that are Auth instance related. This includes user, tenant,
* and project config management related APIs. This extends the BaseFirebaseAuthRequestHandler class and defines
* additional tenant management related APIs.
*/
export class AuthRequestHandler extends AbstractAuthRequestHandler {

protected readonly tenantMgmtResourceBuilder: AuthResourceUrlBuilder;
protected readonly authResourceUrlBuilder: AuthResourceUrlBuilder;

/**
* The FirebaseAuthRequestHandler constructor used to initialize an instance using a FirebaseApp.
Expand All @@ -2045,7 +2068,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler {
*/
constructor(app: App) {
super(app);
this.tenantMgmtResourceBuilder = new AuthResourceUrlBuilder(app, 'v2');
this.authResourceUrlBuilder = new AuthResourceUrlBuilder(app, 'v2');
}

/**
Expand All @@ -2062,6 +2085,35 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler {
return new AuthResourceUrlBuilder(this.app, 'v2');
}

/**
* Get the current project's config
* @returns A promise that resolves with the project config information.
*/
public getProjectConfig(): Promise<ProjectConfigServerResponse> {
return this.invokeRequestHandler(this.authResourceUrlBuilder, GET_PROJECT_CONFIG, {}, {})
.then((response: any) => {
return response as ProjectConfigServerResponse;
});
}

/**
* Update the current project's config.
* @returns A promise that resolves with the project config information.
*/
public updateProjectConfig(recaptchaOptions: UpdateProjectConfigRequest): Promise<ProjectConfigServerResponse> {
try {
const request = ProjectConfig.buildServerRequest(recaptchaOptions);
const updateMask = utils.generateUpdateMask(request);
return this.invokeRequestHandler(
this.authResourceUrlBuilder, UPDATE_PROJECT_CONFIG, request, { updateMask: updateMask.join(',') })
.then((response: any) => {
return response as ProjectConfigServerResponse;
});
} catch (e) {
return Promise.reject(e);
}
}

/**
* Looks up a tenant by tenant ID.
*
Expand All @@ -2072,7 +2124,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler {
if (!validator.isNonEmptyString(tenantId)) {
return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID));
}
return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, GET_TENANT, {}, { tenantId })
return this.invokeRequestHandler(this.authResourceUrlBuilder, GET_TENANT, {}, { tenantId })
.then((response: any) => {
return response as TenantServerResponse;
});
Expand Down Expand Up @@ -2102,7 +2154,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler {
if (typeof request.pageToken === 'undefined') {
delete request.pageToken;
}
return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, LIST_TENANTS, request)
return this.invokeRequestHandler(this.authResourceUrlBuilder, LIST_TENANTS, request)
.then((response: any) => {
if (!response.tenants) {
response.tenants = [];
Expand All @@ -2122,7 +2174,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler {
if (!validator.isNonEmptyString(tenantId)) {
return Promise.reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_TENANT_ID));
}
return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, DELETE_TENANT, undefined, { tenantId })
return this.invokeRequestHandler(this.authResourceUrlBuilder, DELETE_TENANT, undefined, { tenantId })
.then(() => {
// Return nothing.
});
Expand All @@ -2138,7 +2190,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler {
try {
// Construct backend request.
const request = Tenant.buildServerRequest(tenantOptions, true);
return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, CREATE_TENANT, request)
return this.invokeRequestHandler(this.authResourceUrlBuilder, CREATE_TENANT, request)
.then((response: any) => {
return response as TenantServerResponse;
});
Expand All @@ -2164,7 +2216,7 @@ export class AuthRequestHandler extends AbstractAuthRequestHandler {
// Do not traverse deep into testPhoneNumbers. The entire content should be replaced
// and not just specific phone numbers.
const updateMask = utils.generateUpdateMask(request, ['testPhoneNumbers']);
return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, UPDATE_TENANT, request,
return this.invokeRequestHandler(this.authResourceUrlBuilder, UPDATE_TENANT, request,
{ tenantId, updateMask: updateMask.join(',') })
.then((response: any) => {
return response as TenantServerResponse;
Expand Down
1 change: 1 addition & 0 deletions src/auth/auth-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,7 @@ export interface RecaptchaKey {

/**
* The request interface for updating a reCAPTCHA Config.
* By enabling reCAPTCHA Enterprise Integration you are agreeing to reCAPTCHA Enterprise Term of Service.
*/
export interface RecaptchaConfig {
/**
Expand Down
12 changes: 12 additions & 0 deletions src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { App } from '../app/index';
import { AuthRequestHandler } from './auth-api-request';
import { TenantManager } from './tenant-manager';
import { BaseAuth } from './base-auth';
import { ProjectConfigManager } from './project-config-manager';

/**
* Auth service bound to the provided app.
Expand All @@ -27,6 +28,7 @@ import { BaseAuth } from './base-auth';
export class Auth extends BaseAuth {

private readonly tenantManager_: TenantManager;
private readonly projectConfigManager_: ProjectConfigManager;
private readonly app_: App;

/**
Expand All @@ -38,6 +40,7 @@ export class Auth extends BaseAuth {
super(app, new AuthRequestHandler(app));
this.app_ = app;
this.tenantManager_ = new TenantManager(app);
this.projectConfigManager_ = new ProjectConfigManager(app);
}

/**
Expand All @@ -57,4 +60,13 @@ export class Auth extends BaseAuth {
public tenantManager(): TenantManager {
return this.tenantManager_;
}

/**
* Returns the project config manager instance associated with the current project.
*
* @returns The project config manager instance associated with the current project.
*/
public projectConfigManager(): ProjectConfigManager {
return this.projectConfigManager_;
}
}
9 changes: 9 additions & 0 deletions src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ export {
TenantManager,
} from './tenant-manager';

export {
UpdateProjectConfigRequest,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose GetProjectConfigRequest here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a getProjectConfigRequest. It takes an empty object.

ProjectConfig,
} from './project-config';

export {
ProjectConfigManager,
} from './project-config-manager';

export { DecodedIdToken } from './token-verifier';

export {
Expand Down
67 changes: 67 additions & 0 deletions src/auth/project-config-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*!
* Copyright 2022 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { App } from '../app';
import { ProjectConfig, ProjectConfigServerResponse, UpdateProjectConfigRequest } from './project-config';
import {
AuthRequestHandler,
} from './auth-api-request';

/**
* Defines the project config manager used to help manage project config related operations.
* This includes:
* <ul>
* <li>The ability to update and get project config.</li>
*/
export class ProjectConfigManager {
private readonly authRequestHandler: AuthRequestHandler;

/**
* Initializes a ProjectConfigManager instance for a specified FirebaseApp.
*
* @param app - The app for this ProjectConfigManager instance.
*
* @constructor
* @internal
*/
constructor(app: App) {
this.authRequestHandler = new AuthRequestHandler(app);
}

/**
* Get the project configuration.
*
* @returns A promise fulfilled with the project configuration.
*/
public getProjectConfig(): Promise<ProjectConfig> {
return this.authRequestHandler.getProjectConfig()
.then((response: ProjectConfigServerResponse) => {
return new ProjectConfig(response);
})
}
/**
* Updates an existing project configuration.
*
* @param projectConfigOptions - The properties to update on the project.
*
* @returns A promise fulfilled with the update project config.
*/
public updateProjectConfig(projectConfigOptions: UpdateProjectConfigRequest): Promise<ProjectConfig> {
return this.authRequestHandler.updateProjectConfig(projectConfigOptions)
.then((response: ProjectConfigServerResponse) => {
return new ProjectConfig(response);
})
}
}
Loading