@atproto/oauth-provider 0.9.2 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/client/client.js +6 -6
- package/dist/client/client.js.map +1 -1
- package/dist/device/device-manager.js +1 -1
- package/dist/device/device-manager.js.map +1 -1
- package/dist/dpop/dpop-manager.js +15 -15
- package/dist/dpop/dpop-manager.js.map +1 -1
- package/dist/errors/access-denied-error.d.ts +4 -7
- package/dist/errors/access-denied-error.d.ts.map +1 -1
- package/dist/errors/access-denied-error.js +4 -13
- package/dist/errors/access-denied-error.js.map +1 -1
- package/dist/errors/account-selection-required-error.d.ts +2 -2
- package/dist/errors/account-selection-required-error.d.ts.map +1 -1
- package/dist/errors/account-selection-required-error.js +2 -2
- package/dist/errors/account-selection-required-error.js.map +1 -1
- package/dist/errors/authorization-error.d.ts +10 -0
- package/dist/errors/authorization-error.d.ts.map +1 -0
- package/dist/errors/authorization-error.js +31 -0
- package/dist/errors/authorization-error.js.map +1 -0
- package/dist/errors/consent-required-error.d.ts +2 -2
- package/dist/errors/consent-required-error.d.ts.map +1 -1
- package/dist/errors/consent-required-error.js +2 -2
- package/dist/errors/consent-required-error.js.map +1 -1
- package/dist/errors/error-parser.d.ts.map +1 -1
- package/dist/errors/error-parser.js +2 -1
- package/dist/errors/error-parser.js.map +1 -1
- package/dist/errors/invalid-authorization-details-error.d.ts +2 -2
- package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -1
- package/dist/errors/invalid-authorization-details-error.js +2 -2
- package/dist/errors/invalid-authorization-details-error.js.map +1 -1
- package/dist/errors/invalid-scope-error.d.ts +2 -2
- package/dist/errors/invalid-scope-error.d.ts.map +1 -1
- package/dist/errors/invalid-scope-error.js +2 -2
- package/dist/errors/invalid-scope-error.js.map +1 -1
- package/dist/errors/login-required-error.d.ts +2 -3
- package/dist/errors/login-required-error.d.ts.map +1 -1
- package/dist/errors/login-required-error.js +2 -7
- package/dist/errors/login-required-error.js.map +1 -1
- package/dist/lib/http/response.d.ts +4 -4
- package/dist/lib/http/response.d.ts.map +1 -1
- package/dist/lib/http/response.js +8 -7
- package/dist/lib/http/response.js.map +1 -1
- package/dist/lib/http/stream.d.ts +1 -0
- package/dist/lib/http/stream.d.ts.map +1 -1
- package/dist/lib/http/stream.js +6 -0
- package/dist/lib/http/stream.js.map +1 -1
- package/dist/lib/util/error.d.ts +2 -0
- package/dist/lib/util/error.d.ts.map +1 -0
- package/dist/lib/util/error.js +11 -0
- package/dist/lib/util/error.js.map +1 -0
- package/dist/lib/util/zod-error.d.ts +3 -1
- package/dist/lib/util/zod-error.d.ts.map +1 -1
- package/dist/lib/util/zod-error.js +20 -10
- package/dist/lib/util/zod-error.js.map +1 -1
- package/dist/oauth-errors.d.ts +1 -1
- package/dist/oauth-errors.d.ts.map +1 -1
- package/dist/oauth-errors.js +1 -1
- package/dist/oauth-errors.js.map +1 -1
- package/dist/oauth-hooks.d.ts +3 -2
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-hooks.js +4 -3
- package/dist/oauth-hooks.js.map +1 -1
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +18 -21
- package/dist/oauth-provider.js.map +1 -1
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +12 -12
- package/dist/request/request-manager.js.map +1 -1
- package/dist/router/create-api-middleware.d.ts.map +1 -1
- package/dist/router/create-api-middleware.js +60 -45
- package/dist/router/create-api-middleware.js.map +1 -1
- package/dist/router/create-authorization-page-middleware.d.ts.map +1 -1
- package/dist/router/create-authorization-page-middleware.js +19 -17
- package/dist/router/create-authorization-page-middleware.js.map +1 -1
- package/dist/router/create-oauth-middleware.d.ts.map +1 -1
- package/dist/router/create-oauth-middleware.js +21 -18
- package/dist/router/create-oauth-middleware.js.map +1 -1
- package/dist/router/send-redirect.js +2 -2
- package/dist/router/send-redirect.js.map +1 -1
- package/dist/token/token-manager.js +1 -1
- package/dist/types/authorization-response-error.d.ts +5 -0
- package/dist/types/authorization-response-error.d.ts.map +1 -0
- package/dist/types/authorization-response-error.js +21 -0
- package/dist/types/authorization-response-error.js.map +1 -0
- package/dist/types/par-response-error.d.ts +5 -0
- package/dist/types/par-response-error.d.ts.map +1 -0
- package/dist/types/par-response-error.js +22 -0
- package/dist/types/par-response-error.js.map +1 -0
- package/package.json +5 -5
- package/src/client/client.ts +6 -6
- package/src/device/device-manager.ts +1 -1
- package/src/dpop/dpop-manager.ts +16 -16
- package/src/errors/access-denied-error.ts +6 -33
- package/src/errors/account-selection-required-error.ts +2 -2
- package/src/errors/authorization-error.ts +45 -0
- package/src/errors/consent-required-error.ts +2 -2
- package/src/errors/error-parser.ts +2 -1
- package/src/errors/invalid-authorization-details-error.ts +2 -2
- package/src/errors/invalid-scope-error.ts +2 -2
- package/src/errors/login-required-error.ts +2 -12
- package/src/lib/http/response.ts +14 -13
- package/src/lib/http/stream.ts +6 -0
- package/src/lib/util/error.ts +7 -0
- package/src/lib/util/zod-error.ts +23 -11
- package/src/oauth-errors.ts +1 -1
- package/src/oauth-hooks.ts +3 -2
- package/src/oauth-provider.ts +18 -28
- package/src/request/request-manager.ts +12 -18
- package/src/router/create-api-middleware.ts +84 -62
- package/src/router/create-authorization-page-middleware.ts +19 -21
- package/src/router/create-oauth-middleware.ts +28 -27
- package/src/router/send-redirect.ts +2 -2
- package/src/token/token-manager.ts +1 -1
- package/src/types/authorization-response-error.ts +27 -0
- package/src/types/par-response-error.ts +25 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/errors/invalid-parameters-error.d.ts +0 -6
- package/dist/errors/invalid-parameters-error.d.ts.map +0 -1
- package/dist/errors/invalid-parameters-error.js +0 -11
- package/dist/errors/invalid-parameters-error.js.map +0 -1
- package/src/errors/invalid-parameters-error.ts +0 -12
@@ -0,0 +1,45 @@
|
|
1
|
+
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
2
|
+
import {
|
3
|
+
AuthorizationResponseError,
|
4
|
+
isAuthorizationResponseError,
|
5
|
+
} from '../types/authorization-response-error.js'
|
6
|
+
import { buildErrorPayload } from './error-parser.js'
|
7
|
+
import { OAuthError } from './oauth-error.js'
|
8
|
+
|
9
|
+
export type { AuthorizationResponseError, OAuthAuthorizationRequestParameters }
|
10
|
+
|
11
|
+
export class AuthorizationError extends OAuthError {
|
12
|
+
constructor(
|
13
|
+
public readonly parameters: OAuthAuthorizationRequestParameters,
|
14
|
+
error_description: string,
|
15
|
+
error: AuthorizationResponseError = 'invalid_request',
|
16
|
+
cause?: unknown,
|
17
|
+
) {
|
18
|
+
super(error, error_description, 400, cause)
|
19
|
+
}
|
20
|
+
|
21
|
+
static from(
|
22
|
+
parameters: OAuthAuthorizationRequestParameters,
|
23
|
+
cause: unknown,
|
24
|
+
): AuthorizationError {
|
25
|
+
if (cause instanceof AuthorizationError) return cause
|
26
|
+
const payload = buildErrorPayload(cause)
|
27
|
+
return new AuthorizationError(
|
28
|
+
parameters,
|
29
|
+
payload.error_description,
|
30
|
+
isAuthorizationResponseError(payload.error)
|
31
|
+
? payload.error // Propagate "error" derived from the cause
|
32
|
+
: rootCause(cause) instanceof OAuthError
|
33
|
+
? 'invalid_request'
|
34
|
+
: 'server_error',
|
35
|
+
cause,
|
36
|
+
)
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
function rootCause(err: unknown): unknown {
|
41
|
+
while (err instanceof Error && err.cause != null) {
|
42
|
+
err = err.cause
|
43
|
+
}
|
44
|
+
return err
|
45
|
+
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
2
|
-
import {
|
2
|
+
import { AuthorizationError } from './authorization-error.js'
|
3
3
|
|
4
|
-
export class ConsentRequiredError extends
|
4
|
+
export class ConsentRequiredError extends AuthorizationError {
|
5
5
|
constructor(
|
6
6
|
parameters: OAuthAuthorizationRequestParameters,
|
7
7
|
error_description = 'User consent required',
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { errors } from 'jose'
|
2
2
|
import { ZodError } from 'zod'
|
3
3
|
import { JwtVerifyError } from '@atproto/jwk'
|
4
|
+
import { formatZodError } from '../lib/util/zod-error.js'
|
4
5
|
import { OAuthError } from './oauth-error.js'
|
5
6
|
|
6
7
|
const { JOSEError } = errors
|
@@ -63,7 +64,7 @@ export function buildErrorPayload(error: unknown): ErrorPayload {
|
|
63
64
|
if (error instanceof ZodError) {
|
64
65
|
return {
|
65
66
|
error: INVALID_REQUEST,
|
66
|
-
error_description: error
|
67
|
+
error_description: formatZodError(error, 'Validation error'),
|
67
68
|
}
|
68
69
|
}
|
69
70
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
2
|
-
import {
|
2
|
+
import { AuthorizationError } from './authorization-error.js'
|
3
3
|
|
4
4
|
/**
|
5
5
|
* @see
|
@@ -16,7 +16,7 @@ import { AccessDeniedError } from './access-denied-error.js'
|
|
16
16
|
* - contains fields with invalid values for the authorization details type, or
|
17
17
|
* - is missing required fields for the authorization details type.
|
18
18
|
*/
|
19
|
-
export class InvalidAuthorizationDetailsError extends
|
19
|
+
export class InvalidAuthorizationDetailsError extends AuthorizationError {
|
20
20
|
constructor(
|
21
21
|
parameters: OAuthAuthorizationRequestParameters,
|
22
22
|
error_description: string,
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
2
|
-
import {
|
2
|
+
import { AuthorizationError } from './authorization-error.js'
|
3
3
|
|
4
4
|
/**
|
5
5
|
* @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1}
|
6
6
|
*/
|
7
|
-
export class InvalidScopeError extends
|
7
|
+
export class InvalidScopeError extends AuthorizationError {
|
8
8
|
constructor(
|
9
9
|
parameters: OAuthAuthorizationRequestParameters,
|
10
10
|
error_description: string,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
2
|
-
import {
|
2
|
+
import { AuthorizationError } from './authorization-error.js'
|
3
3
|
|
4
|
-
export class LoginRequiredError extends
|
4
|
+
export class LoginRequiredError extends AuthorizationError {
|
5
5
|
constructor(
|
6
6
|
parameters: OAuthAuthorizationRequestParameters,
|
7
7
|
error_description = 'Login is required',
|
@@ -9,14 +9,4 @@ export class LoginRequiredError extends AccessDeniedError {
|
|
9
9
|
) {
|
10
10
|
super(parameters, error_description, 'login_required', cause)
|
11
11
|
}
|
12
|
-
|
13
|
-
static from(
|
14
|
-
parameters: OAuthAuthorizationRequestParameters,
|
15
|
-
cause?: unknown,
|
16
|
-
fallbackError?: string,
|
17
|
-
): LoginRequiredError {
|
18
|
-
if (cause instanceof LoginRequiredError) return cause
|
19
|
-
|
20
|
-
return new LoginRequiredError(parameters, fallbackError, cause)
|
21
|
-
}
|
22
12
|
}
|
package/src/lib/http/response.ts
CHANGED
@@ -101,16 +101,16 @@ export function cacheControlMiddleware(maxAge: number): Middleware<void> {
|
|
101
101
|
}
|
102
102
|
}
|
103
103
|
|
104
|
+
export type JsonResponse<P = unknown> = WriteResponseOptions & {
|
105
|
+
json: P
|
106
|
+
}
|
107
|
+
|
104
108
|
export function jsonHandler<
|
105
109
|
T,
|
106
110
|
Req extends IncomingMessage = IncomingMessage,
|
107
111
|
Res extends ServerResponse = ServerResponse,
|
108
112
|
>(
|
109
|
-
buildJson: (
|
110
|
-
this: T,
|
111
|
-
req: Req,
|
112
|
-
res: Res,
|
113
|
-
) => Awaitable<{ payload: unknown; status?: number }>,
|
113
|
+
buildJson: (this: T, req: Req, res: Res) => Awaitable<JsonResponse>,
|
114
114
|
): Middleware<T, Req, Res> {
|
115
115
|
return function (req, res, next) {
|
116
116
|
// Ensure we can agree on a content encoding & type before starting to
|
@@ -120,14 +120,11 @@ export function jsonHandler<
|
|
120
120
|
// promise and return it.
|
121
121
|
void (async () => {
|
122
122
|
try {
|
123
|
-
const
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
? cause
|
129
|
-
: new Error('Failed to build JSON response', { cause })
|
130
|
-
next(error satisfies Error)
|
123
|
+
const jsonResponse = await buildJson.call(this, req, res)
|
124
|
+
const { json, status = 200, ...options } = jsonResponse
|
125
|
+
writeJson(res, json, { ...options, status })
|
126
|
+
} catch (err) {
|
127
|
+
next(asError(err, 'Failed to build JSON response'))
|
131
128
|
}
|
132
129
|
})()
|
133
130
|
} else {
|
@@ -135,3 +132,7 @@ export function jsonHandler<
|
|
135
132
|
}
|
136
133
|
}
|
137
134
|
}
|
135
|
+
|
136
|
+
function asError(cause: unknown, message: string): Error {
|
137
|
+
return cause instanceof Error ? cause : new Error(message, { cause })
|
138
|
+
}
|
package/src/lib/http/stream.ts
CHANGED
@@ -49,3 +49,9 @@ export async function parseHttpRequest<A extends readonly KnownNames[]>(
|
|
49
49
|
Extract<KnownParser, { name: A[number] }>
|
50
50
|
>
|
51
51
|
}
|
52
|
+
|
53
|
+
export async function flushStream(stream: AsyncIterable<any>): Promise<void> {
|
54
|
+
for await (const _ of stream) {
|
55
|
+
// Consume the stream to completion
|
56
|
+
}
|
57
|
+
}
|
@@ -1,14 +1,26 @@
|
|
1
|
-
import { ZodError } from 'zod'
|
2
|
-
|
3
|
-
export function
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
import { ZodError, ZodIssue, ZodIssueCode } from 'zod'
|
2
|
+
|
3
|
+
export function formatZodError(err: ZodError, prefix?: string): string {
|
4
|
+
const message = err.issues.length
|
5
|
+
? err.issues.map(formatZodIssue).join('; ')
|
6
|
+
: err.message // Should never happen (issues should never be empty)
|
7
|
+
return prefix ? `${prefix}: ${message}` : message
|
8
|
+
}
|
9
|
+
|
10
|
+
export function formatZodIssue(issue: ZodIssue): string {
|
11
|
+
if (issue.code === ZodIssueCode.invalid_union) {
|
12
|
+
return issue.unionErrors
|
13
|
+
.map((err) => err.issues.map(formatZodIssue).join('; '))
|
14
|
+
.join(', or ')
|
15
|
+
}
|
16
|
+
|
17
|
+
if (issue.path.length === 1 && typeof issue.path[0] === 'number') {
|
18
|
+
return `${issue.message} at index ${issue.path[0]}`
|
19
|
+
}
|
20
|
+
|
21
|
+
if (issue.path.length) {
|
22
|
+
return `${issue.message} at ${issue.path.join('.')}`
|
11
23
|
}
|
12
24
|
|
13
|
-
return
|
25
|
+
return issue.message
|
14
26
|
}
|
package/src/oauth-errors.ts
CHANGED
@@ -3,6 +3,7 @@ export { OAuthError } from './errors/oauth-error.js'
|
|
3
3
|
|
4
4
|
export * from './errors/access-denied-error.js'
|
5
5
|
export * from './errors/account-selection-required-error.js'
|
6
|
+
export * from './errors/authorization-error.js'
|
6
7
|
export * from './errors/consent-required-error.js'
|
7
8
|
export * from './errors/handle-unavailable-error.js'
|
8
9
|
export * from './errors/invalid-authorization-details-error.js'
|
@@ -13,7 +14,6 @@ export * from './errors/invalid-dpop-key-binding-error.js'
|
|
13
14
|
export * from './errors/invalid-dpop-proof-error.js'
|
14
15
|
export * from './errors/invalid-grant-error.js'
|
15
16
|
export * from './errors/invalid-invite-code-error.js'
|
16
|
-
export * from './errors/invalid-parameters-error.js'
|
17
17
|
export * from './errors/invalid-redirect-uri-error.js'
|
18
18
|
export * from './errors/invalid-request-error.js'
|
19
19
|
export * from './errors/invalid-scope-error.js'
|
package/src/oauth-hooks.ts
CHANGED
@@ -12,7 +12,9 @@ import { ClientAuth } from './client/client-auth.js'
|
|
12
12
|
import { ClientId } from './client/client-id.js'
|
13
13
|
import { ClientInfo } from './client/client-info.js'
|
14
14
|
import { Client } from './client/client.js'
|
15
|
+
import { AccessDeniedError } from './errors/access-denied-error.js'
|
15
16
|
import { InvalidRequestError } from './errors/invalid-request-error.js'
|
17
|
+
import { OAuthError } from './errors/oauth-error.js'
|
16
18
|
import {
|
17
19
|
HcaptchaClientTokens,
|
18
20
|
HcaptchaConfig,
|
@@ -20,7 +22,6 @@ import {
|
|
20
22
|
} from './lib/hcaptcha.js'
|
21
23
|
import { RequestMetadata } from './lib/http/request.js'
|
22
24
|
import { Awaitable } from './lib/util/type.js'
|
23
|
-
import { AccessDeniedError, OAuthError } from './oauth-errors.js'
|
24
25
|
import { DeviceId, SignUpData } from './oauth-store.js'
|
25
26
|
import { RequestId } from './request/request-id.js'
|
26
27
|
|
@@ -118,7 +119,7 @@ export type OAuthHooks = {
|
|
118
119
|
/**
|
119
120
|
* This hook is called when a client is authorized.
|
120
121
|
*
|
121
|
-
* @throws {
|
122
|
+
* @throws {AuthorizationError} to deny the authorization request and redirect
|
122
123
|
* the user to the client with an OAuth error (other errors will result in an
|
123
124
|
* internal server error being displayed to the user)
|
124
125
|
*
|
package/src/oauth-provider.ts
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import { createHash } from 'node:crypto'
|
2
2
|
import type { Redis, RedisOptions } from 'ioredis'
|
3
|
-
import { ZodError } from 'zod'
|
4
3
|
import { Jwks, Keyset } from '@atproto/jwk'
|
5
4
|
import type { Account } from '@atproto/oauth-provider-api'
|
6
5
|
import {
|
@@ -64,8 +63,8 @@ import {
|
|
64
63
|
deviceManagerOptionsSchema,
|
65
64
|
} from './device/device-manager.js'
|
66
65
|
import { DeviceStore, asDeviceStore } from './device/device-store.js'
|
67
|
-
import { AccessDeniedError } from './errors/access-denied-error.js'
|
68
66
|
import { AccountSelectionRequiredError } from './errors/account-selection-required-error.js'
|
67
|
+
import { AuthorizationError } from './errors/authorization-error.js'
|
69
68
|
import { ConsentRequiredError } from './errors/consent-required-error.js'
|
70
69
|
import { InvalidDpopKeyBindingError } from './errors/invalid-dpop-key-binding-error.js'
|
71
70
|
import { InvalidDpopProofError } from './errors/invalid-dpop-proof-error.js'
|
@@ -75,8 +74,8 @@ import { LoginRequiredError } from './errors/login-required-error.js'
|
|
75
74
|
import { HcaptchaConfig } from './lib/hcaptcha.js'
|
76
75
|
import { RequestMetadata } from './lib/http/request.js'
|
77
76
|
import { dateToRelativeSeconds } from './lib/util/date.js'
|
77
|
+
import { formatError } from './lib/util/error.js'
|
78
78
|
import { LocalizedString, MultiLangString } from './lib/util/locale.js'
|
79
|
-
import { extractZodErrorMessage } from './lib/util/zod-error.js'
|
80
79
|
import { CustomMetadata, buildMetadata } from './metadata/build-metadata.js'
|
81
80
|
import { OAuthHooks } from './oauth-hooks.js'
|
82
81
|
import {
|
@@ -104,6 +103,7 @@ import {
|
|
104
103
|
VerifyTokenClaimsOptions,
|
105
104
|
VerifyTokenClaimsResult,
|
106
105
|
} from './token/verify-token-claims.js'
|
106
|
+
import { isPARResponseError } from './types/par-response-error.js'
|
107
107
|
|
108
108
|
export { AccessTokenMode, Keyset }
|
109
109
|
export type {
|
@@ -448,11 +448,8 @@ export class OAuthProvider extends OAuthVerifier {
|
|
448
448
|
const parameters = await oauthAuthorizationRequestParametersSchema
|
449
449
|
.parseAsync(payload)
|
450
450
|
.catch((err) => {
|
451
|
-
const
|
452
|
-
|
453
|
-
? `Invalid request parameters: ${err.message}`
|
454
|
-
: `Invalid "request" object`
|
455
|
-
throw InvalidRequestError.from(err, message)
|
451
|
+
const msg = formatError(err, 'Invalid parameters in JAR')
|
452
|
+
throw new InvalidRequestError(msg, err)
|
456
453
|
})
|
457
454
|
|
458
455
|
return parameters
|
@@ -522,7 +519,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
522
519
|
// > Since initial processing of the pushed authorization request does not
|
523
520
|
// > involve resource owner interaction, error codes related to user
|
524
521
|
// > interaction, such as "access_denied", are never returned.
|
525
|
-
if (err instanceof
|
522
|
+
if (err instanceof AuthorizationError && !isPARResponseError(err.error)) {
|
526
523
|
throw new InvalidRequestError(err.error_description, err)
|
527
524
|
}
|
528
525
|
throw err
|
@@ -539,10 +536,8 @@ export class OAuthProvider extends OAuthVerifier {
|
|
539
536
|
const requestUri = await requestUriSchema
|
540
537
|
.parseAsync(query.request_uri, { path: ['query', 'request_uri'] })
|
541
538
|
.catch((err) => {
|
542
|
-
|
543
|
-
|
544
|
-
err,
|
545
|
-
)
|
539
|
+
const msg = formatError(err, 'Invalid "request_uri" query parameter')
|
540
|
+
throw new InvalidRequestError(msg, err)
|
546
541
|
})
|
547
542
|
|
548
543
|
return this.requestManager.get(requestUri, deviceId, client.id)
|
@@ -592,24 +587,24 @@ export class OAuthProvider extends OAuthVerifier {
|
|
592
587
|
const { issuer } = this
|
593
588
|
|
594
589
|
// If there is a chance to redirect the user to the client, let's do
|
595
|
-
// it by wrapping the error in an
|
596
|
-
const
|
590
|
+
// it by wrapping the error in an AuthorizationError.
|
591
|
+
const throwAuthorizationError =
|
597
592
|
'redirect_uri' in query
|
598
593
|
? (err: unknown): never => {
|
599
594
|
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1
|
600
|
-
throw
|
595
|
+
throw AuthorizationError.from(query, err)
|
601
596
|
}
|
602
597
|
: null
|
603
598
|
|
604
599
|
const client = await this.clientManager
|
605
600
|
.getClient(clientCredentials.client_id)
|
606
|
-
.catch(
|
601
|
+
.catch(throwAuthorizationError)
|
607
602
|
|
608
603
|
const { parameters, uri } = await this.processAuthorizationRequest(
|
609
604
|
client,
|
610
605
|
deviceId,
|
611
606
|
query,
|
612
|
-
).catch(
|
607
|
+
).catch(throwAuthorizationError)
|
613
608
|
|
614
609
|
try {
|
615
610
|
const sessions = await this.getSessions(client.id, deviceId, parameters)
|
@@ -694,9 +689,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
694
689
|
// (allowing to log this error)
|
695
690
|
}
|
696
691
|
|
697
|
-
|
698
|
-
// likely contain the redirect_uri (using the client default).
|
699
|
-
throw AccessDeniedError.from(parameters, err, 'server_error')
|
692
|
+
throw AuthorizationError.from(parameters, err)
|
700
693
|
}
|
701
694
|
}
|
702
695
|
|
@@ -872,12 +865,8 @@ export class OAuthProvider extends OAuthVerifier {
|
|
872
865
|
const code = await codeSchema
|
873
866
|
.parseAsync(input.code, { path: ['code'] })
|
874
867
|
.catch((err) => {
|
875
|
-
|
876
|
-
|
877
|
-
err instanceof ZodError
|
878
|
-
? `Invalid code: ${err.message}`
|
879
|
-
: `Invalid code`,
|
880
|
-
)
|
868
|
+
const msg = formatError(err, 'Invalid code')
|
869
|
+
throw new InvalidGrantError(msg, err)
|
881
870
|
})
|
882
871
|
|
883
872
|
const data = await this.requestManager
|
@@ -999,7 +988,8 @@ export class OAuthProvider extends OAuthVerifier {
|
|
999
988
|
const refreshToken = await refreshTokenSchema
|
1000
989
|
.parseAsync(input.refresh_token, { path: ['refresh_token'] })
|
1001
990
|
.catch((err) => {
|
1002
|
-
|
991
|
+
const msg = formatError(err, 'Invalid refresh token')
|
992
|
+
throw new InvalidGrantError(msg, err)
|
1003
993
|
})
|
1004
994
|
|
1005
995
|
const tokenInfo = await this.tokenManager.consumeRefreshToken(refreshToken)
|
@@ -16,10 +16,10 @@ import {
|
|
16
16
|
} from '../constants.js'
|
17
17
|
import { DeviceId } from '../device/device-id.js'
|
18
18
|
import { AccessDeniedError } from '../errors/access-denied-error.js'
|
19
|
+
import { AuthorizationError } from '../errors/authorization-error.js'
|
19
20
|
import { ConsentRequiredError } from '../errors/consent-required-error.js'
|
20
21
|
import { InvalidAuthorizationDetailsError } from '../errors/invalid-authorization-details-error.js'
|
21
22
|
import { InvalidGrantError } from '../errors/invalid-grant-error.js'
|
22
|
-
import { InvalidParametersError } from '../errors/invalid-parameters-error.js'
|
23
23
|
import { InvalidRequestError } from '../errors/invalid-request-error.js'
|
24
24
|
import { InvalidScopeError } from '../errors/invalid-scope-error.js'
|
25
25
|
import { RequestMetadata } from '../lib/http/request.js'
|
@@ -93,10 +93,7 @@ export class RequestManager {
|
|
93
93
|
'nonce', // note that OIDC "nonce" is redundant with PKCE
|
94
94
|
] as const) {
|
95
95
|
if (parameters[k] !== undefined) {
|
96
|
-
throw new
|
97
|
-
parameters,
|
98
|
-
`Unsupported "${k}" parameter`,
|
99
|
-
)
|
96
|
+
throw new AuthorizationError(parameters, `Unsupported "${k}" parameter`)
|
100
97
|
}
|
101
98
|
}
|
102
99
|
|
@@ -109,7 +106,7 @@ export class RequestManager {
|
|
109
106
|
parameters.response_type,
|
110
107
|
)
|
111
108
|
) {
|
112
|
-
throw new
|
109
|
+
throw new AuthorizationError(
|
113
110
|
parameters,
|
114
111
|
`Unsupported response_type "${parameters.response_type}"`,
|
115
112
|
'unsupported_response_type',
|
@@ -120,7 +117,7 @@ export class RequestManager {
|
|
120
117
|
parameters.response_type === 'code' &&
|
121
118
|
!this.metadata.grant_types_supported?.includes('authorization_code')
|
122
119
|
) {
|
123
|
-
throw new
|
120
|
+
throw new AuthorizationError(
|
124
121
|
parameters,
|
125
122
|
`Unsupported grant_type "authorization_code"`,
|
126
123
|
'invalid_request',
|
@@ -133,7 +130,7 @@ export class RequestManager {
|
|
133
130
|
// defined in the server metadata. In the future, we might add support
|
134
131
|
// for dynamic scopes.
|
135
132
|
if (!this.metadata.scopes_supported?.includes(scope)) {
|
136
|
-
throw new
|
133
|
+
throw new AuthorizationError(
|
137
134
|
parameters,
|
138
135
|
`Scope "${scope}" is not supported by this server`,
|
139
136
|
)
|
@@ -169,7 +166,7 @@ export class RequestManager {
|
|
169
166
|
if (!parameters.redirect_uri) {
|
170
167
|
// Should already be ensured by client.validateRequest(). Adding here for
|
171
168
|
// clarity & extra safety.
|
172
|
-
throw new
|
169
|
+
throw new AuthorizationError(parameters, 'Missing "redirect_uri"')
|
173
170
|
}
|
174
171
|
|
175
172
|
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10#section-1.4.1
|
@@ -195,7 +192,7 @@ export class RequestManager {
|
|
195
192
|
case 'S256':
|
196
193
|
break
|
197
194
|
default: {
|
198
|
-
throw new
|
195
|
+
throw new AuthorizationError(
|
199
196
|
parameters,
|
200
197
|
`Unsupported code_challenge_method "${parameters.code_challenge_method}"`,
|
201
198
|
)
|
@@ -204,7 +201,7 @@ export class RequestManager {
|
|
204
201
|
} else {
|
205
202
|
if (parameters.code_challenge_method) {
|
206
203
|
// https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1
|
207
|
-
throw new
|
204
|
+
throw new AuthorizationError(
|
208
205
|
parameters,
|
209
206
|
'code_challenge is required when code_challenge_method is provided',
|
210
207
|
)
|
@@ -225,7 +222,7 @@ export class RequestManager {
|
|
225
222
|
// atproto does not implement the OpenID Connect nonce mechanism, so we
|
226
223
|
// require the use of PKCE for all clients.
|
227
224
|
|
228
|
-
throw new
|
225
|
+
throw new AuthorizationError(parameters, 'Use of PKCE is required')
|
229
226
|
}
|
230
227
|
|
231
228
|
// -----------------
|
@@ -233,7 +230,7 @@ export class RequestManager {
|
|
233
230
|
// -----------------
|
234
231
|
|
235
232
|
if (parameters.response_type !== 'code') {
|
236
|
-
throw new
|
233
|
+
throw new AuthorizationError(
|
237
234
|
parameters,
|
238
235
|
'atproto only supports the "code" response_type',
|
239
236
|
)
|
@@ -249,7 +246,7 @@ export class RequestManager {
|
|
249
246
|
}
|
250
247
|
|
251
248
|
if (parameters.code_challenge_method !== 'S256') {
|
252
|
-
throw new
|
249
|
+
throw new AuthorizationError(
|
253
250
|
parameters,
|
254
251
|
'atproto requires use of "S256" code_challenge_method',
|
255
252
|
)
|
@@ -280,10 +277,7 @@ export class RequestManager {
|
|
280
277
|
const hint = parameters.login_hint?.toLowerCase()
|
281
278
|
if (hint) {
|
282
279
|
if (!isAtprotoDid(hint) && !isValidHandle(hint)) {
|
283
|
-
throw new
|
284
|
-
parameters,
|
285
|
-
`Invalid login_hint "${hint}"`,
|
286
|
-
)
|
280
|
+
throw new AuthorizationError(parameters, `Invalid login_hint "${hint}"`)
|
287
281
|
}
|
288
282
|
|
289
283
|
// @TODO: ensure that the account actually exists on this server (there is
|