Skip to content

Commit e3307e5

Browse files
committed
Refactoring OAuthErrors
This makes it possible to parse them from JSON, using OAUTH_ERRORS
1 parent 048bc4f commit e3307e5

File tree

2 files changed

+55
-52
lines changed

2 files changed

+55
-52
lines changed

src/server/auth/errors.ts

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import { OAuthErrorResponse } from "../../shared/auth.js";
44
* Base class for all OAuth errors
55
*/
66
export class OAuthError extends Error {
7+
static errorCode: string;
8+
public errorCode: string;
9+
710
constructor(
8-
public readonly errorCode: string,
911
message: string,
1012
public readonly errorUri?: string
1113
) {
1214
super(message);
15+
this.errorCode = (this.constructor as typeof OAuthError).errorCode
1316
this.name = this.constructor.name;
1417
}
1518

@@ -36,19 +39,15 @@ export class OAuthError extends Error {
3639
* or is otherwise malformed.
3740
*/
3841
export class InvalidRequestError extends OAuthError {
39-
constructor(message: string, errorUri?: string) {
40-
super("invalid_request", message, errorUri);
41-
}
42+
static errorCode = "invalid_request";
4243
}
4344

4445
/**
4546
* Invalid client error - Client authentication failed (e.g., unknown client, no client
4647
* authentication included, or unsupported authentication method).
4748
*/
4849
export class InvalidClientError extends OAuthError {
49-
constructor(message: string, errorUri?: string) {
50-
super("invalid_client", message, errorUri);
51-
}
50+
static errorCode = "invalid_client";
5251
}
5352

5453
/**
@@ -57,135 +56,139 @@ export class InvalidClientError extends OAuthError {
5756
* authorization request, or was issued to another client.
5857
*/
5958
export class InvalidGrantError extends OAuthError {
60-
constructor(message: string, errorUri?: string) {
61-
super("invalid_grant", message, errorUri);
62-
}
59+
static errorCode = "invalid_grant";
6360
}
6461

6562
/**
6663
* Unauthorized client error - The authenticated client is not authorized to use
6764
* this authorization grant type.
6865
*/
6966
export class UnauthorizedClientError extends OAuthError {
70-
constructor(message: string, errorUri?: string) {
71-
super("unauthorized_client", message, errorUri);
72-
}
67+
static errorCode = "unauthorized_client";
7368
}
7469

7570
/**
7671
* Unsupported grant type error - The authorization grant type is not supported
7772
* by the authorization server.
7873
*/
7974
export class UnsupportedGrantTypeError extends OAuthError {
80-
constructor(message: string, errorUri?: string) {
81-
super("unsupported_grant_type", message, errorUri);
82-
}
75+
static errorCode = "unsupported_grant_type";
8376
}
8477

8578
/**
8679
* Invalid scope error - The requested scope is invalid, unknown, malformed, or
8780
* exceeds the scope granted by the resource owner.
8881
*/
8982
export class InvalidScopeError extends OAuthError {
90-
constructor(message: string, errorUri?: string) {
91-
super("invalid_scope", message, errorUri);
92-
}
83+
static errorCode = "invalid_scope";
9384
}
9485

9586
/**
9687
* Access denied error - The resource owner or authorization server denied the request.
9788
*/
9889
export class AccessDeniedError extends OAuthError {
99-
constructor(message: string, errorUri?: string) {
100-
super("access_denied", message, errorUri);
101-
}
90+
static errorCode = "access_denied";
10291
}
10392

10493
/**
10594
* Server error - The authorization server encountered an unexpected condition
10695
* that prevented it from fulfilling the request.
10796
*/
10897
export class ServerError extends OAuthError {
109-
constructor(message: string, errorUri?: string) {
110-
super("server_error", message, errorUri);
111-
}
98+
static errorCode = "server_error";
11299
}
113100

114101
/**
115102
* Temporarily unavailable error - The authorization server is currently unable to
116103
* handle the request due to a temporary overloading or maintenance of the server.
117104
*/
118105
export class TemporarilyUnavailableError extends OAuthError {
119-
constructor(message: string, errorUri?: string) {
120-
super("temporarily_unavailable", message, errorUri);
121-
}
106+
static errorCode = "temporarily_unavailable";
122107
}
123108

124109
/**
125110
* Unsupported response type error - The authorization server does not support
126111
* obtaining an authorization code using this method.
127112
*/
128113
export class UnsupportedResponseTypeError extends OAuthError {
129-
constructor(message: string, errorUri?: string) {
130-
super("unsupported_response_type", message, errorUri);
131-
}
114+
static errorCode = "unsupported_response_type";
132115
}
133116

134117
/**
135118
* Unsupported token type error - The authorization server does not support
136119
* the requested token type.
137120
*/
138121
export class UnsupportedTokenTypeError extends OAuthError {
139-
constructor(message: string, errorUri?: string) {
140-
super("unsupported_token_type", message, errorUri);
141-
}
122+
static errorCode = "unsupported_token_type";
142123
}
143124

144125
/**
145126
* Invalid token error - The access token provided is expired, revoked, malformed,
146127
* or invalid for other reasons.
147128
*/
148129
export class InvalidTokenError extends OAuthError {
149-
constructor(message: string, errorUri?: string) {
150-
super("invalid_token", message, errorUri);
151-
}
130+
static errorCode = "invalid_token";
152131
}
153132

154133
/**
155134
* Method not allowed error - The HTTP method used is not allowed for this endpoint.
156135
* (Custom, non-standard error)
157136
*/
158137
export class MethodNotAllowedError extends OAuthError {
159-
constructor(message: string, errorUri?: string) {
160-
super("method_not_allowed", message, errorUri);
161-
}
138+
static errorCode = "method_not_allowed";
162139
}
163140

164141
/**
165142
* Too many requests error - Rate limit exceeded.
166143
* (Custom, non-standard error based on RFC 6585)
167144
*/
168145
export class TooManyRequestsError extends OAuthError {
169-
constructor(message: string, errorUri?: string) {
170-
super("too_many_requests", message, errorUri);
171-
}
146+
static errorCode = "too_many_requests";
172147
}
173148

174149
/**
175150
* Invalid client metadata error - The client metadata is invalid.
176151
* (Custom error for dynamic client registration - RFC 7591)
177152
*/
178153
export class InvalidClientMetadataError extends OAuthError {
179-
constructor(message: string, errorUri?: string) {
180-
super("invalid_client_metadata", message, errorUri);
181-
}
154+
static errorCode = "invalid_client_metadata";
182155
}
183156

184157
/**
185158
* Insufficient scope error - The request requires higher privileges than provided by the access token.
186159
*/
187160
export class InsufficientScopeError extends OAuthError {
188-
constructor(message: string, errorUri?: string) {
189-
super("insufficient_scope", message, errorUri);
161+
static errorCode = "insufficient_scope";
162+
}
163+
164+
/**
165+
* A utility class for defining one-off error codes
166+
*/
167+
export class CustomOAuthError extends OAuthError {
168+
constructor(errorCode: string, message: string, errorUri?: string) {
169+
super(message, errorUri);
170+
this.errorCode = errorCode
190171
}
191172
}
173+
174+
/**
175+
* A full list of all OAuthErrors, enabling parsing from error responses
176+
*/
177+
export const OAUTH_ERRORS = {
178+
[InvalidRequestError.errorCode]: InvalidRequestError,
179+
[InvalidClientError.errorCode]: InvalidClientError,
180+
[InvalidGrantError.errorCode]: InvalidGrantError,
181+
[UnauthorizedClientError.errorCode]: UnauthorizedClientError,
182+
[UnsupportedGrantTypeError.errorCode]: UnsupportedGrantTypeError,
183+
[InvalidScopeError.errorCode]: InvalidScopeError,
184+
[AccessDeniedError.errorCode]: AccessDeniedError,
185+
[ServerError.errorCode]: ServerError,
186+
[TemporarilyUnavailableError.errorCode]: TemporarilyUnavailableError,
187+
[UnsupportedResponseTypeError.errorCode]: UnsupportedResponseTypeError,
188+
[UnsupportedTokenTypeError.errorCode]: UnsupportedTokenTypeError,
189+
[InvalidTokenError.errorCode]: InvalidTokenError,
190+
[MethodNotAllowedError.errorCode]: MethodNotAllowedError,
191+
[TooManyRequestsError.errorCode]: TooManyRequestsError,
192+
[InvalidClientMetadataError.errorCode]: InvalidClientMetadataError,
193+
[InsufficientScopeError.errorCode]: InsufficientScopeError,
194+
} as const;

src/server/auth/middleware/bearerAuth.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Request, Response } from "express";
22
import { requireBearerAuth } from "./bearerAuth.js";
33
import { AuthInfo } from "../types.js";
4-
import { InsufficientScopeError, InvalidTokenError, OAuthError, ServerError } from "../errors.js";
4+
import { CustomOAuthError, InsufficientScopeError, InvalidTokenError, ServerError } from "../errors.js";
55
import { OAuthServerProvider } from "../provider.js";
66
import { OAuthRegisteredClientsStore } from "../clients.js";
77

@@ -59,7 +59,7 @@ describe("requireBearerAuth middleware", () => {
5959
expect(mockResponse.status).not.toHaveBeenCalled();
6060
expect(mockResponse.json).not.toHaveBeenCalled();
6161
});
62-
62+
6363
it("should reject expired tokens", async () => {
6464
const expiredAuthInfo: AuthInfo = {
6565
token: "expired-token",
@@ -87,7 +87,7 @@ describe("requireBearerAuth middleware", () => {
8787
);
8888
expect(nextFunction).not.toHaveBeenCalled();
8989
});
90-
90+
9191
it("should accept non-expired tokens", async () => {
9292
const nonExpiredAuthInfo: AuthInfo = {
9393
token: "valid-token",
@@ -274,7 +274,7 @@ describe("requireBearerAuth middleware", () => {
274274
authorization: "Bearer valid-token",
275275
};
276276

277-
mockVerifyAccessToken.mockRejectedValue(new OAuthError("custom_error", "Some OAuth error"));
277+
mockVerifyAccessToken.mockRejectedValue(new CustomOAuthError("custom_error", "Some OAuth error"));
278278

279279
const middleware = requireBearerAuth({ provider: mockProvider });
280280
await middleware(mockRequest as Request, mockResponse as Response, nextFunction);

0 commit comments

Comments
 (0)