Comprehensive OAuth 2.0/OIDC support with adapters, environment handlers, and security utilities for implementing custom authentication providers.
Adapter class for implementing OAuth providers with configuration-driven setup.
/**
* OAuth adapter for implementing authentication providers
*/
class OAuthAdapter implements AuthProviderRouteHandlers {
/**
* Creates OAuth adapter from configuration
* @param config - Backstage configuration object
* @param providerId - Provider identifier
* @param options - OAuth adapter options
* @returns Configured OAuth adapter instance
*/
static fromConfig(
config: Config,
providerId: string,
options: OAuthAdapterOptions
): OAuthAdapter;
/**
* Initiates OAuth authentication flow
* @param req - Express request object
* @param res - Express response object
*/
start(req: express.Request, res: express.Response): Promise<void>;
/**
* Handles OAuth callback and token exchange
* @param req - Express request object
* @param res - Express response object
*/
frameHandler(req: express.Request, res: express.Response): Promise<void>;
/**
* Refreshes OAuth tokens (if supported)
* @param req - Express request object
* @param res - Express response object
*/
refresh?(req: express.Request, res: express.Response): Promise<void>;
/**
* Logs out from OAuth provider (if supported)
* @param req - Express request object
* @param res - Express response object
*/
logout?(req: express.Request, res: express.Response): Promise<void>;
}
interface OAuthAdapterOptions {
/** OAuth provider implementation */
provider: OAuthHandlers;
/** Callback URL for OAuth flow */
callbackUrl: string;
/** Whether to sign-in existing users */
signInResolverOptions?: SignInResolverOptions;
}Usage Example:
import { OAuthAdapter } from "@backstage/plugin-auth-backend";
// Create custom OAuth provider
const customOAuthHandlers: OAuthHandlers = {
async start(req, options) {
// Implement OAuth start logic
return { url: authorizationUrl, status: 302 };
},
async handler(req, res, options) {
// Implement OAuth callback logic
return { profile, providerInfo };
},
};
// Create adapter from configuration
const adapter = OAuthAdapter.fromConfig(config, "custom", {
provider: customOAuthHandlers,
callbackUrl: "/auth/custom/handler/frame",
});Handler for managing OAuth providers across different environments.
/**
* OAuth environment handler for multi-environment deployments
*/
class OAuthEnvironmentHandler implements AuthProviderRouteHandlers {
/**
* Maps configuration to environment-specific handlers
* @param config - Provider configuration
* @param factoryFunc - Factory function for creating handlers
* @returns Environment handler instance
*/
static mapConfig<T>(
config: Config,
factoryFunc: (envConfig: Config) => T
): OAuthEnvironmentHandler;
/**
* Initiates OAuth authentication flow
* @param req - Express request object
* @param res - Express response object
*/
start(req: express.Request, res: express.Response): Promise<void>;
/**
* Handles OAuth callback
* @param req - Express request object
* @param res - Express response object
*/
frameHandler(req: express.Request, res: express.Response): Promise<void>;
/**
* Refreshes OAuth tokens (if supported)
* @param req - Express request object
* @param res - Express response object
*/
refresh?(req: express.Request, res: express.Response): Promise<void>;
/**
* Logs out from OAuth provider (if supported)
* @param req - Express request object
* @param res - Express response object
*/
logout?(req: express.Request, res: express.Response): Promise<void>;
}Security utilities for OAuth state management and nonce verification.
/**
* Encodes OAuth state parameters into a secure string
* @param state - OAuth state object
* @returns Encoded state string
*/
function encodeState(state: OAuthState): string;
/**
* Verifies OAuth nonce for security
* @param req - Express request object
* @param providerId - Provider identifier
* @throws Error if nonce verification fails
*/
function verifyNonce(req: express.Request, providerId: string): void;
/**
* Decodes OAuth state parameters from string
* @param stateString - Encoded state string
* @returns Decoded state object
*/
function readState(stateString: string): OAuthState;Usage Example:
import { encodeState, verifyNonce, readState } from "@backstage/plugin-auth-backend";
// Encode state for OAuth flow
const state = encodeState({
nonce: "random-nonce-value",
env: "development",
origin: "https://backstage.example.com",
scope: "read:user",
redirectUrl: "/welcome",
});
// In OAuth callback handler
try {
verifyNonce(req, "github");
const decodedState = readState(req.query.state as string);
// Process OAuth callback
} catch (error) {
// Handle nonce verification failure
}/**
* Interface for OAuth provider implementations
*/
interface OAuthHandlers {
/** Initiates OAuth authentication flow */
start(req: OAuthStartRequest, options: OAuthHandlerOptions): Promise<RedirectInfo>;
/** Handles OAuth callback and token exchange */
handler(req: OAuthCallbackRequest, res: express.Response, options: OAuthHandlerOptions): Promise<{
profile: ProfileInfo;
providerInfo: OAuthProviderInfo;
}>;
/** Refreshes OAuth tokens (optional) */
refresh?(req: OAuthRefreshRequest, options: OAuthHandlerOptions): Promise<{
profile: ProfileInfo;
providerInfo: OAuthProviderInfo;
}>;
/** Logs out from OAuth provider (optional) */
logout?(req: express.Request, options: OAuthHandlerOptions): Promise<void>;
}
/**
* OAuth provider information structure
*/
interface OAuthProviderInfo {
/** OAuth access token */
accessToken: string;
/** OAuth refresh token (optional) */
refreshToken?: string;
/** Token scope */
scope: string;
/** Token expiration in seconds (optional) */
expiresInSeconds?: number;
/** Provider-specific additional data */
[key: string]: any;
}
/**
* Common OAuth provider options
*/
interface OAuthProviderOptions {
/** OAuth client ID */
clientId: string;
/** OAuth client secret */
clientSecret: string;
/** OAuth callback URL */
callbackUrl: string;
/** Additional OAuth scopes */
scope?: string;
/** Custom authorization parameters */
authorizationParams?: { [key: string]: string };
}/**
* OAuth authentication start request
*/
interface OAuthStartRequest {
/** Request query parameters */
query: { [key: string]: string };
/** OAuth state parameters */
state: OAuthState;
/** Request scope */
scope: string;
}
/**
* OAuth callback request
*/
interface OAuthCallbackRequest {
/** Authorization code from provider */
code: string;
/** OAuth state string */
state: string;
/** Request query parameters */
query: { [key: string]: string };
}
/**
* OAuth token refresh request
*/
interface OAuthRefreshRequest {
/** Refresh token */
refreshToken: string;
/** Request scope */
scope: string;
}/**
* OAuth state parameter structure
*/
interface OAuthState {
/** Security nonce */
nonce: string;
/** Environment identifier */
env: string;
/** Origin URL (optional) */
origin?: string;
/** Request scope (optional) */
scope?: string;
/** Redirect URL after authentication (optional) */
redirectUrl?: string;
/** Additional state parameters */
[key: string]: any;
}
/**
* OAuth authentication result
*/
interface OAuthResult {
/** Full user profile from provider */
fullProfile: any;
/** OAuth access token */
accessToken: string;
/** OAuth refresh token (optional) */
refreshToken?: string;
/** Additional parameters from provider */
params: any;
}
/**
* OAuth authentication response
*/
interface OAuthResponse {
/** Provider-specific information */
providerInfo: OAuthProviderInfo;
/** User profile information */
profile: ProfileInfo;
}/**
* Options passed to OAuth handlers
*/
interface OAuthHandlerOptions {
/** Provider identifier */
providerId: string;
/** Provider configuration */
config: Config;
/** Logger instance */
logger: Logger;
/** Optional token issuer */
tokenIssuer?: TokenIssuer;
/** Optional catalog API client */
catalogApi?: CatalogApi;
}
/**
* Sign-in resolver options
*/
interface SignInResolverOptions {
/** Resolver function for user identity */
resolver: (info: OAuthResult, ctx: OAuthHandlerOptions) => Promise<SignInInfo>;
}
/**
* Sign-in information
*/
interface SignInInfo {
/** User profile information */
profile: ProfileInfo;
/** Provider-specific information */
providerInfo?: any;
/** Optional Backstage identity token */
token?: string;
}