Skip to content

Throw Operation Not Allowed for Invalid Auth Endpoint #9013

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

Merged
merged 6 commits into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Throw not supported exception in _performApiRequest
  • Loading branch information
mansisampat committed May 12, 2025
commit e543087b421689a15f33cd8e41e6574b932b356e
1 change: 1 addition & 0 deletions common/api-review/auth.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export interface Auth {
setPersistence(persistence: Persistence): Promise<void>;
readonly settings: AuthSettings;
signOut(): Promise<void>;
readonly tenantConfig?: TenantConfig;
tenantId: string | null;
updateCurrentUser(user: User | null): Promise<void>;
useDeviceLanguage(): void;
Expand Down
23 changes: 21 additions & 2 deletions packages/auth/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { IdTokenMfaResponse } from './authentication/mfa';
import { SERVER_ERROR_MAP, ServerError, ServerErrorMap } from './errors';
import { PersistenceType } from '../core/persistence';
import { CookiePersistence } from '../platform_browser/persistence/cookie_storage';
import {_operationNotSupportedForInitializedAuthInstance} from '../core/util/assert';

export const enum HttpMethod {
POST = 'POST',
Expand All @@ -53,7 +54,7 @@ export const enum HttpHeader {
X_FIREBASE_APP_CHECK = 'X-Firebase-AppCheck'
}

export const enum Endpoint {
export enum Endpoint {
CREATE_AUTH_URI = '/v1/accounts:createAuthUri',
DELETE_ACCOUNT = '/v1/accounts:delete',
RESET_PASSWORD = '/v1/accounts:resetPassword',
Expand All @@ -80,6 +81,10 @@ export const enum Endpoint {
REVOKE_TOKEN = '/v2/accounts:revokeToken'
}

export enum Regional_Endpoint {
EXCHANGE_TOKEN = 'v2/${body.parent}:exchangeOidcToken'
}

const CookieAuthProxiedEndpoints: string[] = [
Endpoint.SIGN_IN_WITH_CUSTOM_TOKEN,
Endpoint.SIGN_IN_WITH_EMAIL_LINK,
Expand Down Expand Up @@ -139,10 +144,11 @@ export function _addTidIfNecessary<T extends { tenantId?: string }>(
export async function _performApiRequest<T, V>(
auth: Auth,
method: HttpMethod,
path: Endpoint,
path: Endpoint | Regional_Endpoint,
request?: T,
customErrorMap: Partial<ServerErrorMap<ServerError>> = {}
): Promise<V> {
_assertValidEndpointForAuth(auth, path);
return _performFetchWithErrorHandling(auth, customErrorMap, async () => {
let body = {};
let params = {};
Expand Down Expand Up @@ -322,6 +328,19 @@ export function _parseEnforcementState(
}
}

function _assertValidEndpointForAuth(
auth: Auth,
path: Endpoint | Regional_Endpoint
): void {
if (!auth.tenantConfig && Object.values(Regional_Endpoint).includes(path as Regional_Endpoint)) {
throw _operationNotSupportedForInitializedAuthInstance(auth);
}

if (auth.tenantConfig && Object.values(Endpoint).includes(path as Endpoint)) {
throw _operationNotSupportedForInitializedAuthInstance(auth);
}
}

class NetworkTimeout<T> {
// Node timers and browser timers are fundamentally incompatible, but we
// don't care about the value here
Expand Down
8 changes: 6 additions & 2 deletions packages/auth/src/core/auth/auth_impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ import {
ErrorFn,
NextFn,
Unsubscribe,
PasswordValidationStatus
PasswordValidationStatus,
TenantConfig,
} from '../../model/public_types';
import {
createSubscribe,
Expand Down Expand Up @@ -126,6 +127,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
| undefined = undefined;
_persistenceManagerAvailable: Promise<void>;
readonly name: string;
readonly tenantConfig?: TenantConfig;

// Tracks the last notified UID for state change listeners to prevent
// repeated calls to the callbacks. Undefined means it's never been
Expand All @@ -140,7 +142,8 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
public readonly app: FirebaseApp,
private readonly heartbeatServiceProvider: Provider<'heartbeat'>,
private readonly appCheckServiceProvider: Provider<AppCheckInternalComponentName>,
public readonly config: ConfigInternal
public readonly config: ConfigInternal,
tenantConfig?: TenantConfig
) {
this.name = app.name;
this.clientVersion = config.sdkClientVersion;
Expand All @@ -149,6 +152,7 @@ export class AuthImpl implements AuthInternal, _FirebaseService {
this._persistenceManagerAvailable = new Promise<void>(
resolve => (this._resolvePersistenceManagerAvailable = resolve)
);
this.tenantConfig = tenantConfig;
}

_initializeWithPersistence(
Expand Down
8 changes: 4 additions & 4 deletions packages/auth/src/core/auth/emulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Auth } from '../../model/public_types';
import { AuthErrorCode } from '../errors';
import { _assert } from '../util/assert';
import { _castAuth } from './auth_impl';
import { deepEqual, isCloudWorkstation, pingServer } from '@firebase/util';
import { deepEqual, pingServer } from '@firebase/util';

/**
* Changes the {@link Auth} instance to communicate with the Firebase Auth Emulator, instead of production
Expand Down Expand Up @@ -102,9 +102,9 @@ export function connectAuthEmulator(
}

// Workaround to get cookies in Firebase Studio
if (isCloudWorkstation(host)) {
void pingServer(`${protocol}//${host}:${port}`);
}
// if (isCloudWorkstation(host)) {
// void pingServer(`${protocol}//${host}:${port}`);
// }
}

function extractProtocol(url: string): string {
Expand Down
3 changes: 2 additions & 1 deletion packages/auth/src/core/auth/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ export function registerAuth(clientPlatform: ClientPlatform): void {
app,
heartbeatServiceProvider,
appCheckServiceProvider,
config
config,
tenantConfig
);
_initializeAuthInstance(authInstance, deps);

Expand Down
3 changes: 2 additions & 1 deletion packages/auth/src/core/strategies/email_and_password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
} from '../util/assert';
import { _setActionCodeSettingsOnRequest } from './action_code_settings';
import { signInWithCredential } from './credential';
import { _castAuth } from '../auth/auth_impl';
import { _castAuth, AuthImpl } from '../auth/auth_impl';
import { AuthErrorCode } from '../errors';
import { getModularInstance } from '@firebase/util';
import { OperationType } from '../../model/enums';
Expand All @@ -47,6 +47,7 @@ import {
RecaptchaAuthProvider
} from '../../api';
import { _isFirebaseServerApp } from '@firebase/app';
import {_isFirebaseRegionalAuthInitialized} from '../util/validate_origin'

/**
* Updates the password policy cached in the {@link Auth} instance if a policy is already
Expand Down
8 changes: 8 additions & 0 deletions packages/auth/src/core/util/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ export function _serverAppCurrentUserOperationNotSupportedError(
);
}

export function _operationNotSupportedForInitializedAuthInstance(auth: Auth) : FirebaseError {
return _errorWithCustomMessage(
auth,
AuthErrorCode.OPERATION_NOT_ALLOWED,
'Operations not allowed for the auth object initialized.'
);
}

export function _assertInstanceOf(
auth: Auth,
object: object,
Expand Down
2 changes: 2 additions & 0 deletions packages/auth/src/model/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
PasswordPolicy,
PasswordValidationStatus,
PopupRedirectResolver,
TenantConfig,
User
} from './public_types';
import { ErrorFactory } from '@firebase/util';
Expand Down Expand Up @@ -100,6 +101,7 @@ export interface AuthInternal extends Auth {

readonly name: AppName;
readonly config: ConfigInternal;
readonly tenantConfig?: TenantConfig;
languageCode: string | null;
tenantId: string | null;
readonly settings: AuthSettings;
Expand Down
6 changes: 6 additions & 0 deletions packages/auth/src/model/public_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ export interface Auth {
readonly name: string;
/** The {@link Config} used to initialize this instance. */
readonly config: Config;
/**
* The {@link TenantConfig} used to initialize a Regional Auth. This is only present
* if regional auth is initialized and {@link DefaultConfig.REGIONAL_API_HOST}
* backend endpoint is used.
*/
readonly tenantConfig?: TenantConfig;
/**
* Changes the type of persistence on the `Auth` instance.
*
Expand Down
Loading