@atproto/oauth-provider 0.19.7 → 0.19.8
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 +26 -0
- package/dist/errors/error-parser.js +5 -5
- package/dist/errors/error-parser.js.map +1 -1
- package/package.json +22 -18
- package/src/access-token/access-token-mode.ts +0 -4
- package/src/account/account-manager.ts +0 -591
- package/src/account/account-store.ts +0 -305
- package/src/account/sign-in-data.ts +0 -15
- package/src/account/sign-up-input.ts +0 -20
- package/src/client/client-auth.ts +0 -64
- package/src/client/client-data.ts +0 -9
- package/src/client/client-id.ts +0 -4
- package/src/client/client-info.ts +0 -13
- package/src/client/client-manager.ts +0 -772
- package/src/client/client-store.ts +0 -35
- package/src/client/client-utils.ts +0 -37
- package/src/client/client.ts +0 -394
- package/src/constants.ts +0 -80
- package/src/customization/branding.ts +0 -12
- package/src/customization/build-customization-css.ts +0 -55
- package/src/customization/build-customization-data.ts +0 -24
- package/src/customization/colors.ts +0 -48
- package/src/customization/customization.ts +0 -29
- package/src/customization/links.ts +0 -10
- package/src/device/device-data.ts +0 -11
- package/src/device/device-id.ts +0 -27
- package/src/device/device-manager.ts +0 -292
- package/src/device/device-store.ts +0 -31
- package/src/device/session-id.ts +0 -21
- package/src/dpop/dpop-manager.ts +0 -214
- package/src/dpop/dpop-nonce.ts +0 -121
- package/src/dpop/dpop-proof.ts +0 -6
- package/src/errors/access-denied-error.ts +0 -12
- package/src/errors/account-selection-required-error.ts +0 -12
- package/src/errors/authorization-error.ts +0 -45
- package/src/errors/consent-required-error.ts +0 -12
- package/src/errors/error-parser.ts +0 -147
- package/src/errors/handle-unavailable-error.ts +0 -23
- package/src/errors/invalid-authorization-details-error.ts +0 -27
- package/src/errors/invalid-client-error.ts +0 -20
- package/src/errors/invalid-client-id-error.ts +0 -31
- package/src/errors/invalid-client-metadata-error.ts +0 -68
- package/src/errors/invalid-credentials-error.ts +0 -29
- package/src/errors/invalid-dpop-key-binding-error.ts +0 -21
- package/src/errors/invalid-dpop-proof-error.ts +0 -13
- package/src/errors/invalid-grant-error.ts +0 -21
- package/src/errors/invalid-invite-code-error.ts +0 -10
- package/src/errors/invalid-redirect-uri-error.ts +0 -17
- package/src/errors/invalid-request-error.ts +0 -32
- package/src/errors/invalid-scope-error.ts +0 -15
- package/src/errors/invalid-token-error.ts +0 -60
- package/src/errors/login-required-error.ts +0 -12
- package/src/errors/oauth-error.ts +0 -28
- package/src/errors/second-authentication-factor-required-error.ts +0 -25
- package/src/errors/unauthorized-client-error.ts +0 -20
- package/src/errors/use-dpop-nonce-error.ts +0 -32
- package/src/errors/www-authenticate-error.ts +0 -64
- package/src/index.ts +0 -16
- package/src/lexicon/lexicon-data.ts +0 -11
- package/src/lexicon/lexicon-getter.ts +0 -55
- package/src/lexicon/lexicon-manager.ts +0 -116
- package/src/lexicon/lexicon-store.ts +0 -35
- package/src/lib/csp/index.ts +0 -101
- package/src/lib/hcaptcha.ts +0 -228
- package/src/lib/html/README.md +0 -9
- package/src/lib/html/build-document.ts +0 -151
- package/src/lib/html/escapers.ts +0 -66
- package/src/lib/html/html.ts +0 -56
- package/src/lib/html/hydration-data.ts +0 -19
- package/src/lib/html/index.ts +0 -5
- package/src/lib/html/tags.ts +0 -67
- package/src/lib/html/util.ts +0 -17
- package/src/lib/http/README.md +0 -11
- package/src/lib/http/accept.ts +0 -90
- package/src/lib/http/context.ts +0 -42
- package/src/lib/http/headers.ts +0 -15
- package/src/lib/http/index.ts +0 -10
- package/src/lib/http/method.ts +0 -18
- package/src/lib/http/middleware.ts +0 -169
- package/src/lib/http/parser.ts +0 -101
- package/src/lib/http/path.ts +0 -82
- package/src/lib/http/request.ts +0 -256
- package/src/lib/http/response.ts +0 -122
- package/src/lib/http/route.ts +0 -60
- package/src/lib/http/router.ts +0 -117
- package/src/lib/http/security-headers.ts +0 -100
- package/src/lib/http/stream.ts +0 -57
- package/src/lib/http/types.ts +0 -16
- package/src/lib/http/url.ts +0 -23
- package/src/lib/nsid.ts +0 -10
- package/src/lib/redis.ts +0 -25
- package/src/lib/util/authorization-header.ts +0 -28
- package/src/lib/util/cast.ts +0 -18
- package/src/lib/util/color.ts +0 -168
- package/src/lib/util/crypto.ts +0 -32
- package/src/lib/util/date.ts +0 -7
- package/src/lib/util/error.ts +0 -7
- package/src/lib/util/function.ts +0 -39
- package/src/lib/util/locale.ts +0 -12
- package/src/lib/util/object.ts +0 -23
- package/src/lib/util/redirect-uri.ts +0 -46
- package/src/lib/util/time.ts +0 -49
- package/src/lib/util/type.ts +0 -182
- package/src/lib/util/ui8.ts +0 -14
- package/src/lib/util/well-known.ts +0 -8
- package/src/lib/util/zod-error.ts +0 -26
- package/src/lib/write-form-redirect.ts +0 -58
- package/src/lib/write-html.ts +0 -70
- package/src/metadata/build-metadata.ts +0 -141
- package/src/oauth-client.ts +0 -3
- package/src/oauth-dpop.ts +0 -2
- package/src/oauth-errors.ts +0 -26
- package/src/oauth-hooks.ts +0 -519
- package/src/oauth-middleware.ts +0 -53
- package/src/oauth-provider.ts +0 -1110
- package/src/oauth-store.ts +0 -12
- package/src/oauth-verifier.ts +0 -260
- package/src/replay/replay-manager.ts +0 -51
- package/src/replay/replay-store-memory.ts +0 -36
- package/src/replay/replay-store-redis.ts +0 -30
- package/src/replay/replay-store.ts +0 -44
- package/src/request/code.ts +0 -23
- package/src/request/request-data.ts +0 -36
- package/src/request/request-id.ts +0 -22
- package/src/request/request-manager.ts +0 -521
- package/src/request/request-store.ts +0 -60
- package/src/request/request-uri.ts +0 -42
- package/src/result/authorization-redirect-parameters.ts +0 -24
- package/src/result/authorization-result-authorize-page.ts +0 -16
- package/src/result/authorization-result-redirect.ts +0 -8
- package/src/router/assets/assets-manifest.ts +0 -117
- package/src/router/assets/assets.ts +0 -124
- package/src/router/assets/csrf.ts +0 -79
- package/src/router/assets/send-account-page.ts +0 -23
- package/src/router/assets/send-authorization-page.ts +0 -42
- package/src/router/assets/send-cookie-error-page.ts +0 -21
- package/src/router/assets/send-error-page.ts +0 -25
- package/src/router/assets/send-redirect.ts +0 -140
- package/src/router/create-account-page-middleware.ts +0 -88
- package/src/router/create-api-middleware.ts +0 -1051
- package/src/router/create-authorization-page-middleware.ts +0 -281
- package/src/router/create-oauth-middleware.ts +0 -257
- package/src/router/error-handler.ts +0 -6
- package/src/router/middleware-options.ts +0 -9
- package/src/signer/access-token-payload.ts +0 -25
- package/src/signer/api-token-payload.ts +0 -18
- package/src/signer/signer.ts +0 -121
- package/src/token/refresh-token.ts +0 -30
- package/src/token/token-claims.ts +0 -22
- package/src/token/token-data.ts +0 -40
- package/src/token/token-id.ts +0 -25
- package/src/token/token-manager.ts +0 -427
- package/src/token/token-store.ts +0 -88
- package/src/types/authorization-response-error.ts +0 -27
- package/src/types/color-hue.ts +0 -3
- package/src/types/email-otp.ts +0 -3
- package/src/types/email.ts +0 -26
- package/src/types/handle.ts +0 -23
- package/src/types/invite-code.ts +0 -4
- package/src/types/par-response-error.ts +0 -25
- package/src/types/password.ts +0 -4
- package/src/types/rgb-color.ts +0 -21
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -4
package/src/dpop/dpop-nonce.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { createHmac, randomBytes } from 'node:crypto'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
import { DPOP_NONCE_MAX_AGE } from '../constants.js'
|
|
4
|
-
|
|
5
|
-
const MAX_ROTATION_INTERVAL = DPOP_NONCE_MAX_AGE / 3
|
|
6
|
-
const MIN_ROTATION_INTERVAL = Math.min(1000, MAX_ROTATION_INTERVAL)
|
|
7
|
-
|
|
8
|
-
export const rotationIntervalSchema = z
|
|
9
|
-
.number()
|
|
10
|
-
.int()
|
|
11
|
-
.min(MIN_ROTATION_INTERVAL)
|
|
12
|
-
.max(MAX_ROTATION_INTERVAL)
|
|
13
|
-
|
|
14
|
-
const SECRET_BYTE_LENGTH = 32
|
|
15
|
-
|
|
16
|
-
export const secretBytesSchema = z
|
|
17
|
-
.instanceof(Uint8Array<ArrayBufferLike>)
|
|
18
|
-
.refine((secret) => secret.length === SECRET_BYTE_LENGTH, {
|
|
19
|
-
message: `Secret must be exactly ${SECRET_BYTE_LENGTH} bytes long`,
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
export const secretHexSchema = z
|
|
23
|
-
.string()
|
|
24
|
-
.regex(
|
|
25
|
-
/^[0-9a-f]+$/i,
|
|
26
|
-
`Secret must be a ${SECRET_BYTE_LENGTH * 2} chars hex string`,
|
|
27
|
-
)
|
|
28
|
-
.length(SECRET_BYTE_LENGTH * 2)
|
|
29
|
-
.transform((hex): Uint8Array => Buffer.from(hex, 'hex'))
|
|
30
|
-
|
|
31
|
-
export const dpopSecretSchema = z.union([secretBytesSchema, secretHexSchema])
|
|
32
|
-
export type DpopSecret = z.input<typeof dpopSecretSchema>
|
|
33
|
-
|
|
34
|
-
export class DpopNonce {
|
|
35
|
-
readonly #rotationInterval: number
|
|
36
|
-
readonly #secret: Uint8Array
|
|
37
|
-
|
|
38
|
-
// Nonce state
|
|
39
|
-
#counter: number
|
|
40
|
-
#prev: string
|
|
41
|
-
#now: string
|
|
42
|
-
#next: string
|
|
43
|
-
|
|
44
|
-
constructor(
|
|
45
|
-
secret: DpopSecret = randomBytes(SECRET_BYTE_LENGTH),
|
|
46
|
-
rotationInterval = MAX_ROTATION_INTERVAL,
|
|
47
|
-
) {
|
|
48
|
-
this.#rotationInterval = rotationIntervalSchema.parse(rotationInterval)
|
|
49
|
-
this.#secret = Uint8Array.from(dpopSecretSchema.parse(secret))
|
|
50
|
-
|
|
51
|
-
this.#counter = this.currentCounter
|
|
52
|
-
this.#prev = this.compute(this.#counter - 1)
|
|
53
|
-
this.#now = this.compute(this.#counter)
|
|
54
|
-
this.#next = this.compute(this.#counter + 1)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Returns the number of full rotations since the epoch
|
|
59
|
-
*/
|
|
60
|
-
protected get currentCounter() {
|
|
61
|
-
return (Date.now() / this.#rotationInterval) | 0
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
protected rotate() {
|
|
65
|
-
const counter = this.currentCounter
|
|
66
|
-
switch (counter - this.#counter) {
|
|
67
|
-
case 0:
|
|
68
|
-
// counter === this.#counter => nothing to do
|
|
69
|
-
return
|
|
70
|
-
case 1:
|
|
71
|
-
// Optimization: avoid recomputing #prev & #now
|
|
72
|
-
this.#prev = this.#now
|
|
73
|
-
this.#now = this.#next
|
|
74
|
-
this.#next = this.compute(counter + 1)
|
|
75
|
-
break
|
|
76
|
-
case 2:
|
|
77
|
-
// Optimization: avoid recomputing #prev
|
|
78
|
-
this.#prev = this.#next
|
|
79
|
-
this.#now = this.compute(counter)
|
|
80
|
-
this.#next = this.compute(counter + 1)
|
|
81
|
-
break
|
|
82
|
-
default:
|
|
83
|
-
// All nonces are outdated, so we recompute all of them
|
|
84
|
-
this.#prev = this.compute(counter - 1)
|
|
85
|
-
this.#now = this.compute(counter)
|
|
86
|
-
this.#next = this.compute(counter + 1)
|
|
87
|
-
break
|
|
88
|
-
}
|
|
89
|
-
this.#counter = counter
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
protected compute(counter: number) {
|
|
93
|
-
return createHmac('sha256', this.#secret)
|
|
94
|
-
.update(numTo64bits(counter))
|
|
95
|
-
.digest()
|
|
96
|
-
.toString('base64url')
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public next() {
|
|
100
|
-
this.rotate()
|
|
101
|
-
return this.#next
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
public check(nonce: string) {
|
|
105
|
-
return this.#next === nonce || this.#now === nonce || this.#prev === nonce
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function numTo64bits(num: number) {
|
|
110
|
-
const arr = new Uint8Array(8)
|
|
111
|
-
// @NOTE Assigning to an uint8 will only keep the last 8 int bits
|
|
112
|
-
arr[7] = num |= 0
|
|
113
|
-
arr[6] = num >>= 8
|
|
114
|
-
arr[5] = num >>= 8
|
|
115
|
-
arr[4] = num >>= 8
|
|
116
|
-
arr[3] = num >>= 8
|
|
117
|
-
arr[2] = num >>= 8
|
|
118
|
-
arr[1] = num >>= 8
|
|
119
|
-
arr[0] = num >>= 8
|
|
120
|
-
return arr
|
|
121
|
-
}
|
package/src/dpop/dpop-proof.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
|
2
|
-
import { AuthorizationError } from './authorization-error.js'
|
|
3
|
-
|
|
4
|
-
export class AccessDeniedError extends AuthorizationError {
|
|
5
|
-
constructor(
|
|
6
|
-
parameters: OAuthAuthorizationRequestParameters,
|
|
7
|
-
error_description = 'Access denied',
|
|
8
|
-
cause?: unknown,
|
|
9
|
-
) {
|
|
10
|
-
super(parameters, error_description, 'access_denied', cause)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
|
2
|
-
import { AuthorizationError } from './authorization-error.js'
|
|
3
|
-
|
|
4
|
-
export class AccountSelectionRequiredError extends AuthorizationError {
|
|
5
|
-
constructor(
|
|
6
|
-
parameters: OAuthAuthorizationRequestParameters,
|
|
7
|
-
error_description = 'Account selection required',
|
|
8
|
-
cause?: unknown,
|
|
9
|
-
) {
|
|
10
|
-
super(parameters, error_description, 'account_selection_required', cause)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
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,12 +0,0 @@
|
|
|
1
|
-
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
|
2
|
-
import { AuthorizationError } from './authorization-error.js'
|
|
3
|
-
|
|
4
|
-
export class ConsentRequiredError extends AuthorizationError {
|
|
5
|
-
constructor(
|
|
6
|
-
parameters: OAuthAuthorizationRequestParameters,
|
|
7
|
-
error_description = 'User consent required',
|
|
8
|
-
cause?: unknown,
|
|
9
|
-
) {
|
|
10
|
-
super(parameters, error_description, 'consent_required', cause)
|
|
11
|
-
}
|
|
12
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { errors } from 'jose'
|
|
2
|
-
import { ZodError } from 'zod'
|
|
3
|
-
import { JwtVerifyError } from '@atproto/jwk'
|
|
4
|
-
import { formatZodError } from '../lib/util/zod-error.js'
|
|
5
|
-
import { OAuthError } from './oauth-error.js'
|
|
6
|
-
|
|
7
|
-
const { JOSEError } = errors
|
|
8
|
-
|
|
9
|
-
const INVALID_REQUEST = 'invalid_request'
|
|
10
|
-
const SERVER_ERROR = 'server_error'
|
|
11
|
-
|
|
12
|
-
export function buildErrorStatus(error: unknown): number {
|
|
13
|
-
if (error instanceof OAuthError) {
|
|
14
|
-
return error.statusCode
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (error instanceof JwtVerifyError) {
|
|
18
|
-
return 400
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (error instanceof ZodError) {
|
|
22
|
-
return 400
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (error instanceof JOSEError) {
|
|
26
|
-
return 400
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (error instanceof TypeError) {
|
|
30
|
-
return 400
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (isBoom(error)) {
|
|
34
|
-
return error.output.statusCode
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (isXrpcError(error)) {
|
|
38
|
-
return error.type
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const status = (error as any)?.status
|
|
42
|
-
if (
|
|
43
|
-
typeof status === 'number' &&
|
|
44
|
-
status === (status | 0) &&
|
|
45
|
-
status >= 400 &&
|
|
46
|
-
status < 600
|
|
47
|
-
) {
|
|
48
|
-
return status
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return 500
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type ErrorPayload = {
|
|
55
|
-
error: string
|
|
56
|
-
error_description: string
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function buildErrorPayload(error: unknown): ErrorPayload {
|
|
60
|
-
if (error instanceof OAuthError) {
|
|
61
|
-
return error.toJSON()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (error instanceof ZodError) {
|
|
65
|
-
return {
|
|
66
|
-
error: INVALID_REQUEST,
|
|
67
|
-
error_description: formatZodError(error, 'Validation error'),
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (error instanceof JOSEError) {
|
|
72
|
-
return {
|
|
73
|
-
error: INVALID_REQUEST,
|
|
74
|
-
error_description: error.message,
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (error instanceof TypeError) {
|
|
79
|
-
return {
|
|
80
|
-
error: INVALID_REQUEST,
|
|
81
|
-
error_description: error.message,
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (isBoom(error)) {
|
|
86
|
-
return {
|
|
87
|
-
error: error.output.statusCode <= 500 ? INVALID_REQUEST : SERVER_ERROR,
|
|
88
|
-
error_description:
|
|
89
|
-
error.output.statusCode <= 500
|
|
90
|
-
? isPayloadLike(error.output?.payload)
|
|
91
|
-
? error.output.payload.message
|
|
92
|
-
: error.message
|
|
93
|
-
: 'Server error',
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (isXrpcError(error)) {
|
|
98
|
-
return {
|
|
99
|
-
error: error.type <= 500 ? INVALID_REQUEST : SERVER_ERROR,
|
|
100
|
-
error_description: error.payload.message,
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const status = buildErrorStatus(error)
|
|
105
|
-
return {
|
|
106
|
-
error: status < 500 ? INVALID_REQUEST : SERVER_ERROR,
|
|
107
|
-
error_description:
|
|
108
|
-
error instanceof Error && (error as any)?.expose === true
|
|
109
|
-
? error.message
|
|
110
|
-
: 'Server error',
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function isBoom(v: unknown): v is Error & {
|
|
115
|
-
isBoom: true
|
|
116
|
-
output: { statusCode: number; payload: unknown }
|
|
117
|
-
} {
|
|
118
|
-
return (
|
|
119
|
-
v instanceof Error &&
|
|
120
|
-
(v as any).isBoom === true &&
|
|
121
|
-
isHttpErrorCode(v['output']?.['statusCode'])
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function isXrpcError(v: unknown): v is Error & {
|
|
126
|
-
type: number
|
|
127
|
-
payload: { error: string; message: string }
|
|
128
|
-
} {
|
|
129
|
-
return (
|
|
130
|
-
v instanceof Error &&
|
|
131
|
-
isHttpErrorCode(v['type']) &&
|
|
132
|
-
isPayloadLike(v['payload'])
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function isHttpErrorCode(v: unknown): v is number {
|
|
137
|
-
return typeof v === 'number' && v >= 400 && v < 600 && v === (v | 0)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function isPayloadLike(v: unknown): v is { error: string; message: string } {
|
|
141
|
-
return (
|
|
142
|
-
v != null &&
|
|
143
|
-
typeof v === 'object' &&
|
|
144
|
-
typeof v['error'] === 'string' &&
|
|
145
|
-
typeof v['message'] === 'string'
|
|
146
|
-
)
|
|
147
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { HandleUnavailableReason } from '@atproto/oauth-provider-api'
|
|
2
|
-
import { OAuthError } from './oauth-error.js'
|
|
3
|
-
|
|
4
|
-
// @TODO this is *not* and "OAuthError" error but rather an ApiError.
|
|
5
|
-
|
|
6
|
-
export type { HandleUnavailableReason }
|
|
7
|
-
|
|
8
|
-
export class HandleUnavailableError extends OAuthError {
|
|
9
|
-
constructor(
|
|
10
|
-
readonly reason: HandleUnavailableReason,
|
|
11
|
-
details: string = 'That handle is not available',
|
|
12
|
-
cause?: unknown,
|
|
13
|
-
) {
|
|
14
|
-
super('handle_unavailable', details, 400, cause)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
toJSON() {
|
|
18
|
-
return {
|
|
19
|
-
...super.toJSON(),
|
|
20
|
-
reason: this.reason,
|
|
21
|
-
} as const
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
|
2
|
-
import { AuthorizationError } from './authorization-error.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @see
|
|
6
|
-
* {@link https://datatracker.ietf.org/doc/html/rfc9396#section-14.6 | RFC 9396 - OAuth Dynamic Client Registration Metadata Registration Error}
|
|
7
|
-
*
|
|
8
|
-
* The AS MUST refuse to process any unknown authorization details type or
|
|
9
|
-
* authorization details not conforming to the respective type definition. The
|
|
10
|
-
* AS MUST abort processing and respond with an error
|
|
11
|
-
* invalid_authorization_details to the client if any of the following are true
|
|
12
|
-
* of the objects in the authorization_details structure:
|
|
13
|
-
* - contains an unknown authorization details type value,
|
|
14
|
-
* - is an object of known type but containing unknown fields,
|
|
15
|
-
* - contains fields of the wrong type for the authorization details type,
|
|
16
|
-
* - contains fields with invalid values for the authorization details type, or
|
|
17
|
-
* - is missing required fields for the authorization details type.
|
|
18
|
-
*/
|
|
19
|
-
export class InvalidAuthorizationDetailsError extends AuthorizationError {
|
|
20
|
-
constructor(
|
|
21
|
-
parameters: OAuthAuthorizationRequestParameters,
|
|
22
|
-
error_description: string,
|
|
23
|
-
cause?: unknown,
|
|
24
|
-
) {
|
|
25
|
-
super(parameters, error_description, 'invalid_authorization_details', cause)
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { OAuthError } from './oauth-error.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @see
|
|
5
|
-
* {@link https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 | RFC6749 - Issuing an Access Token }
|
|
6
|
-
*
|
|
7
|
-
* Client authentication failed (e.g., unknown client, no client authentication
|
|
8
|
-
* included, or unsupported authentication method). The authorization server MAY
|
|
9
|
-
* return an HTTP 401 (Unauthorized) status code to indicate which HTTP
|
|
10
|
-
* authentication schemes are supported. If the client attempted to
|
|
11
|
-
* authenticate via the "Authorization" request header field, the authorization
|
|
12
|
-
* server MUST respond with an HTTP 401 (Unauthorized) status code and include
|
|
13
|
-
* the "WWW-Authenticate" response header field matching the authentication
|
|
14
|
-
* scheme used by the client.
|
|
15
|
-
*/
|
|
16
|
-
export class InvalidClientError extends OAuthError {
|
|
17
|
-
constructor(error_description: string, cause?: unknown) {
|
|
18
|
-
super('invalid_client', error_description, 400, cause)
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { OAuthError } from './oauth-error.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2 | RFC7591 - Client Registration Error Response}
|
|
5
|
-
*
|
|
6
|
-
* The value of one of the client metadata fields is invalid and the server has
|
|
7
|
-
* rejected this request. Note that an authorization server MAY choose to
|
|
8
|
-
* substitute a valid value for any requested parameter of a client's metadata.
|
|
9
|
-
*/
|
|
10
|
-
export class InvalidClientIdError extends OAuthError {
|
|
11
|
-
constructor(error_description: string, cause?: unknown) {
|
|
12
|
-
super('invalid_client_id', error_description, 400, cause)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
static from(
|
|
16
|
-
cause: unknown,
|
|
17
|
-
fallbackMessage = 'Invalid client identifier',
|
|
18
|
-
): InvalidClientIdError {
|
|
19
|
-
if (cause instanceof InvalidClientIdError) {
|
|
20
|
-
return cause
|
|
21
|
-
}
|
|
22
|
-
if (cause instanceof TypeError) {
|
|
23
|
-
// This method is meant to be used in the context of parsing & validating
|
|
24
|
-
// a client client metadata. In that context, a TypeError would more
|
|
25
|
-
// likely represent a problem with the data (e.g. invalid URL constructor
|
|
26
|
-
// arg) and not a programming error.
|
|
27
|
-
return new InvalidClientIdError(cause.message, cause)
|
|
28
|
-
}
|
|
29
|
-
return new InvalidClientIdError(fallbackMessage, cause)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { ZodError } from 'zod'
|
|
2
|
-
import { FetchError } from '@atproto-labs/fetch'
|
|
3
|
-
import { OAuthError } from './oauth-error.js'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2 | RFC7591 - Client Registration Error Response}
|
|
7
|
-
*
|
|
8
|
-
* The value of one of the client metadata fields is invalid and the server has
|
|
9
|
-
* rejected this request. Note that an authorization server MAY choose to
|
|
10
|
-
* substitute a valid value for any requested parameter of a client's metadata.
|
|
11
|
-
*/
|
|
12
|
-
export class InvalidClientMetadataError extends OAuthError {
|
|
13
|
-
constructor(error_description: string, cause?: unknown) {
|
|
14
|
-
super('invalid_client_metadata', error_description, 400, cause)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static from(cause: unknown, message = 'Invalid client metadata'): OAuthError {
|
|
18
|
-
if (cause instanceof OAuthError) {
|
|
19
|
-
return cause
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (cause instanceof FetchError) {
|
|
23
|
-
throw new InvalidClientMetadataError(
|
|
24
|
-
cause.expose ? `${message}: ${cause.message}` : message,
|
|
25
|
-
cause,
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (cause instanceof ZodError) {
|
|
30
|
-
const causeMessage =
|
|
31
|
-
cause.issues
|
|
32
|
-
.map(
|
|
33
|
-
({ path, message }) =>
|
|
34
|
-
`Validation${path.length ? ` of "${path.join('.')}"` : ''} failed with error: ${message}`,
|
|
35
|
-
)
|
|
36
|
-
.join(' ') || cause.message
|
|
37
|
-
|
|
38
|
-
throw new InvalidClientMetadataError(
|
|
39
|
-
causeMessage ? `${message}: ${causeMessage}` : message,
|
|
40
|
-
cause,
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
cause instanceof Error &&
|
|
46
|
-
'code' in cause &&
|
|
47
|
-
cause.code === 'DEPTH_ZERO_SELF_SIGNED_CERT'
|
|
48
|
-
) {
|
|
49
|
-
throw new InvalidClientMetadataError(
|
|
50
|
-
`${message}: Self-signed certificate`,
|
|
51
|
-
cause,
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (cause instanceof TypeError) {
|
|
56
|
-
// This method is meant to be used in the context of parsing & validating
|
|
57
|
-
// a client client metadata. In that context, a TypeError would more
|
|
58
|
-
// likely represent a problem with the data (e.g. invalid URL constructor
|
|
59
|
-
// arg) and not a programming error.
|
|
60
|
-
return new InvalidClientMetadataError(
|
|
61
|
-
`${message}: ${cause.message}`,
|
|
62
|
-
cause,
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return new InvalidClientMetadataError(message, cause)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Did } from '@atproto/did'
|
|
2
|
-
import { InvalidRequestError } from './invalid-request-error.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Thrown by {@link AccountStore.authenticateAccount} implementations to signal
|
|
6
|
-
* that a sign-in attempt was rejected because the provided credentials did not
|
|
7
|
-
* match a known account.
|
|
8
|
-
*
|
|
9
|
-
* Stores should populate {@link did}. when the identifier resolved to an
|
|
10
|
-
* existing account but e.g. the password or OTP was incorrect. The
|
|
11
|
-
* identifier-unknown case should leave {@link did} unset. This information is
|
|
12
|
-
* surfaced to the `onSignInFailed` hook and never sent back to the client, so
|
|
13
|
-
* populating it does not affect the client-visible response.
|
|
14
|
-
*
|
|
15
|
-
* Only the subject identifier (DID) is carried — not a full `Account` — to
|
|
16
|
-
* avoid embedding PII (email, name, etc.) in an error that may be serialized
|
|
17
|
-
* by loggers or monitoring tools walking the `.cause` chain. Hook consumers
|
|
18
|
-
* that need richer profile info can resolve it from their own account store
|
|
19
|
-
* using {@link did}.
|
|
20
|
-
*/
|
|
21
|
-
export class InvalidCredentialsError extends InvalidRequestError {
|
|
22
|
-
constructor(
|
|
23
|
-
message = 'Invalid identifier or password',
|
|
24
|
-
public readonly did?: Did,
|
|
25
|
-
cause?: unknown,
|
|
26
|
-
) {
|
|
27
|
-
super(message, cause)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { WWWAuthenticateError } from './www-authenticate-error.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @see
|
|
5
|
-
* {@link https://datatracker.ietf.org/doc/html/rfc6750#section-3.1 | RFC6750 - The WWW-Authenticate Response Header Field}
|
|
6
|
-
*
|
|
7
|
-
* @see
|
|
8
|
-
* {@link https://datatracker.ietf.org/doc/html/rfc9449#name-the-dpop-authentication-sch | RFC9449 - The DPoP Authentication Scheme}
|
|
9
|
-
*/
|
|
10
|
-
export class InvalidDpopKeyBindingError extends WWWAuthenticateError {
|
|
11
|
-
constructor(cause?: unknown) {
|
|
12
|
-
const error = 'invalid_token'
|
|
13
|
-
const error_description = 'Invalid DPoP key binding'
|
|
14
|
-
super(
|
|
15
|
-
error,
|
|
16
|
-
error_description,
|
|
17
|
-
{ DPoP: { error, error_description } },
|
|
18
|
-
cause,
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { WWWAuthenticateError } from './www-authenticate-error.js'
|
|
2
|
-
|
|
3
|
-
export class InvalidDpopProofError extends WWWAuthenticateError {
|
|
4
|
-
constructor(error_description: string, cause?: unknown) {
|
|
5
|
-
const error = 'invalid_dpop_proof'
|
|
6
|
-
super(
|
|
7
|
-
error,
|
|
8
|
-
error_description,
|
|
9
|
-
{ DPoP: { error, error_description } },
|
|
10
|
-
cause,
|
|
11
|
-
)
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { OAuthError } from './oauth-error.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @see
|
|
5
|
-
* {@link https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 | RFC6749 - Issuing an Access Token }
|
|
6
|
-
*
|
|
7
|
-
* The provided authorization grant (e.g., authorization code, resource owner
|
|
8
|
-
* credentials) or refresh token is invalid, expired, revoked, does not match
|
|
9
|
-
* the redirection URI used in the authorization request, or was issued to
|
|
10
|
-
* another client.
|
|
11
|
-
*/
|
|
12
|
-
export class InvalidGrantError extends OAuthError {
|
|
13
|
-
constructor(error_description: string, cause?: unknown) {
|
|
14
|
-
super('invalid_grant', error_description, 400, cause)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static from(err: unknown, error_description: string): InvalidGrantError {
|
|
18
|
-
if (err instanceof InvalidGrantError) return err
|
|
19
|
-
return new InvalidGrantError(error_description, err)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { InvalidRequestError } from './invalid-request-error.js'
|
|
2
|
-
|
|
3
|
-
export class InvalidInviteCodeError extends InvalidRequestError {
|
|
4
|
-
constructor(details?: string, cause?: unknown) {
|
|
5
|
-
super(
|
|
6
|
-
'This invite code is invalid.' + (details ? ` ${details}` : ''),
|
|
7
|
-
cause,
|
|
8
|
-
)
|
|
9
|
-
}
|
|
10
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { OAuthError } from './oauth-error.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @see {@link https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2 | RFC7591}
|
|
5
|
-
*
|
|
6
|
-
* The value of one or more redirection URIs is invalid.
|
|
7
|
-
*/
|
|
8
|
-
export class InvalidRedirectUriError extends OAuthError {
|
|
9
|
-
constructor(error_description: string, cause?: unknown) {
|
|
10
|
-
super('invalid_redirect_uri', error_description, 400, cause)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
static from(cause?: unknown): InvalidRedirectUriError {
|
|
14
|
-
if (cause instanceof InvalidRedirectUriError) return cause
|
|
15
|
-
return new InvalidRedirectUriError('Invalid redirect URI', cause)
|
|
16
|
-
}
|
|
17
|
-
}
|