diff --git a/src/server/auth/handlers/authorize.ts b/src/server/auth/handlers/authorize.ts index ef15770b9..861662449 100644 --- a/src/server/auth/handlers/authorize.ts +++ b/src/server/auth/handlers/authorize.ts @@ -13,6 +13,11 @@ export type AuthorizationHandlerOptions = { * Set to false to disable rate limiting for this endpoint. */ rateLimit?: Partial | false; + + /** + * Set to true to throw errors to the express error handler + */ + throwErrors?: boolean; }; // Parameters that must be validated in order to issue redirects. @@ -34,7 +39,7 @@ const RequestAuthorizationParamsSchema = z.object({ resource: z.string().url().optional() }); -export function authorizationHandler({ provider, rateLimit: rateLimitConfig }: AuthorizationHandlerOptions): RequestHandler { +export function authorizationHandler({ provider, rateLimit: rateLimitConfig, throwErrors }: AuthorizationHandlerOptions): RequestHandler { // Create a router to apply middleware const router = express.Router(); router.use(allowedMethods(['GET', 'POST'])); @@ -101,6 +106,10 @@ export function authorizationHandler({ provider, rateLimit: rateLimitConfig }: A res.status(500).json(serverError.toResponseObject()); } + if (throwErrors) { + throw error; + } + return; } @@ -142,6 +151,10 @@ export function authorizationHandler({ provider, rateLimit: rateLimitConfig }: A const serverError = new ServerError('Internal Server Error'); res.redirect(302, createErrorRedirect(redirect_uri, serverError, state)); } + + if (throwErrors) { + throw error; + } } }); diff --git a/src/server/auth/handlers/register.ts b/src/server/auth/handlers/register.ts index 1830619b4..5121d0ed2 100644 --- a/src/server/auth/handlers/register.ts +++ b/src/server/auth/handlers/register.ts @@ -33,6 +33,11 @@ export type ClientRegistrationHandlerOptions = { * If not set, defaults to true. */ clientIdGeneration?: boolean; + + /** + * Set to true to throw errors to the express error handler + */ + throwErrors?: boolean; }; const DEFAULT_CLIENT_SECRET_EXPIRY_SECONDS = 30 * 24 * 60 * 60; // 30 days @@ -41,7 +46,8 @@ export function clientRegistrationHandler({ clientsStore, clientSecretExpirySeconds = DEFAULT_CLIENT_SECRET_EXPIRY_SECONDS, rateLimit: rateLimitConfig, - clientIdGeneration = true + clientIdGeneration = true, + throwErrors }: ClientRegistrationHandlerOptions): RequestHandler { if (!clientsStore.registerClient) { throw new Error('Client registration store does not support registering clients'); @@ -112,6 +118,10 @@ export function clientRegistrationHandler({ const serverError = new ServerError('Internal Server Error'); res.status(500).json(serverError.toResponseObject()); } + + if (throwErrors) { + throw error; + } } }); diff --git a/src/server/auth/handlers/revoke.ts b/src/server/auth/handlers/revoke.ts index da7ef04f8..e31e6e737 100644 --- a/src/server/auth/handlers/revoke.ts +++ b/src/server/auth/handlers/revoke.ts @@ -14,9 +14,11 @@ export type RevocationHandlerOptions = { * Set to false to disable rate limiting for this endpoint. */ rateLimit?: Partial | false; + + throwErrors?: boolean; }; -export function revocationHandler({ provider, rateLimit: rateLimitConfig }: RevocationHandlerOptions): RequestHandler { +export function revocationHandler({ provider, rateLimit: rateLimitConfig, throwErrors }: RevocationHandlerOptions): RequestHandler { if (!provider.revokeToken) { throw new Error('Auth provider does not support revoking tokens'); } @@ -72,6 +74,10 @@ export function revocationHandler({ provider, rateLimit: rateLimitConfig }: Revo const serverError = new ServerError('Internal Server Error'); res.status(500).json(serverError.toResponseObject()); } + + if (throwErrors) { + throw error; + } } }); diff --git a/src/server/auth/handlers/token.ts b/src/server/auth/handlers/token.ts index c387ff7bf..7bc610e1e 100644 --- a/src/server/auth/handlers/token.ts +++ b/src/server/auth/handlers/token.ts @@ -22,6 +22,11 @@ export type TokenHandlerOptions = { * Set to false to disable rate limiting for this endpoint. */ rateLimit?: Partial | false; + + /** + * Set to true to throw errors to the express error handler + */ + throwErrors?: boolean; }; const TokenRequestSchema = z.object({ @@ -41,7 +46,7 @@ const RefreshTokenGrantSchema = z.object({ resource: z.string().url().optional() }); -export function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHandlerOptions): RequestHandler { +export function tokenHandler({ provider, rateLimit: rateLimitConfig, throwErrors }: TokenHandlerOptions): RequestHandler { // Nested router so we can configure middleware and restrict HTTP method const router = express.Router(); @@ -66,7 +71,7 @@ export function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHand } // Authenticate and extract client details - router.use(authenticateClient({ clientsStore: provider.clientsStore })); + router.use(authenticateClient({ clientsStore: provider.clientsStore, throwErrors })); router.post('/', async (req, res) => { res.setHeader('Cache-Control', 'no-store'); @@ -140,7 +145,7 @@ export function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHand //case "client_credentials": default: - throw new UnsupportedGrantTypeError('The grant type is not supported by this authorization server.'); + throw new UnsupportedGrantTypeError(`The grant type '${grant_type}' is not supported by this authorization server.`); } } catch (error) { if (error instanceof OAuthError) { @@ -150,6 +155,10 @@ export function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHand const serverError = new ServerError('Internal Server Error'); res.status(500).json(serverError.toResponseObject()); } + + if (throwErrors) { + throw error; + } } }); diff --git a/src/server/auth/middleware/bearerAuth.ts b/src/server/auth/middleware/bearerAuth.ts index 363fd7a42..05be5a5c4 100644 --- a/src/server/auth/middleware/bearerAuth.ts +++ b/src/server/auth/middleware/bearerAuth.ts @@ -18,6 +18,11 @@ export type BearerAuthMiddlewareOptions = { * Optional resource metadata URL to include in WWW-Authenticate header. */ resourceMetadataUrl?: string; + + /** + * Set to true to throw errors to the express error handler + */ + throwErrors?: boolean; }; declare module 'express-serve-static-core' { @@ -37,7 +42,12 @@ declare module 'express-serve-static-core' { * If resourceMetadataUrl is provided, it will be included in the WWW-Authenticate header * for 401 responses as per the OAuth 2.0 Protected Resource Metadata spec. */ -export function requireBearerAuth({ verifier, requiredScopes = [], resourceMetadataUrl }: BearerAuthMiddlewareOptions): RequestHandler { +export function requireBearerAuth({ + verifier, + requiredScopes = [], + resourceMetadataUrl, + throwErrors +}: BearerAuthMiddlewareOptions): RequestHandler { return async (req, res, next) => { try { const authHeader = req.headers.authorization; @@ -91,6 +101,10 @@ export function requireBearerAuth({ verifier, requiredScopes = [], resourceMetad const serverError = new ServerError('Internal Server Error'); res.status(500).json(serverError.toResponseObject()); } + + if (throwErrors) { + throw error; + } } }; } diff --git a/src/server/auth/middleware/clientAuth.ts b/src/server/auth/middleware/clientAuth.ts index 9969b8724..422b1c992 100644 --- a/src/server/auth/middleware/clientAuth.ts +++ b/src/server/auth/middleware/clientAuth.ts @@ -9,6 +9,11 @@ export type ClientAuthenticationMiddlewareOptions = { * A store used to read information about registered OAuth clients. */ clientsStore: OAuthRegisteredClientsStore; + + /** + * Set to true to throw errors to the express error handler + */ + throwErrors?: boolean; }; const ClientAuthenticatedRequestSchema = z.object({ @@ -25,7 +30,7 @@ declare module 'express-serve-static-core' { } } -export function authenticateClient({ clientsStore }: ClientAuthenticationMiddlewareOptions): RequestHandler { +export function authenticateClient({ clientsStore, throwErrors }: ClientAuthenticationMiddlewareOptions): RequestHandler { return async (req, res, next) => { try { const result = ClientAuthenticatedRequestSchema.safeParse(req.body); @@ -67,6 +72,10 @@ export function authenticateClient({ clientsStore }: ClientAuthenticationMiddlew const serverError = new ServerError('Internal Server Error'); res.status(500).json(serverError.toResponseObject()); } + + if (throwErrors) { + throw error; + } } }; }