@atproto/oauth-provider 0.19.6 → 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.
Files changed (165) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/errors/error-parser.js +5 -5
  3. package/dist/errors/error-parser.js.map +1 -1
  4. package/package.json +22 -18
  5. package/src/access-token/access-token-mode.ts +0 -4
  6. package/src/account/account-manager.ts +0 -591
  7. package/src/account/account-store.ts +0 -305
  8. package/src/account/sign-in-data.ts +0 -15
  9. package/src/account/sign-up-input.ts +0 -20
  10. package/src/client/client-auth.ts +0 -64
  11. package/src/client/client-data.ts +0 -9
  12. package/src/client/client-id.ts +0 -4
  13. package/src/client/client-info.ts +0 -13
  14. package/src/client/client-manager.ts +0 -772
  15. package/src/client/client-store.ts +0 -35
  16. package/src/client/client-utils.ts +0 -37
  17. package/src/client/client.ts +0 -394
  18. package/src/constants.ts +0 -80
  19. package/src/customization/branding.ts +0 -12
  20. package/src/customization/build-customization-css.ts +0 -55
  21. package/src/customization/build-customization-data.ts +0 -24
  22. package/src/customization/colors.ts +0 -48
  23. package/src/customization/customization.ts +0 -29
  24. package/src/customization/links.ts +0 -10
  25. package/src/device/device-data.ts +0 -11
  26. package/src/device/device-id.ts +0 -27
  27. package/src/device/device-manager.ts +0 -292
  28. package/src/device/device-store.ts +0 -31
  29. package/src/device/session-id.ts +0 -21
  30. package/src/dpop/dpop-manager.ts +0 -214
  31. package/src/dpop/dpop-nonce.ts +0 -121
  32. package/src/dpop/dpop-proof.ts +0 -6
  33. package/src/errors/access-denied-error.ts +0 -12
  34. package/src/errors/account-selection-required-error.ts +0 -12
  35. package/src/errors/authorization-error.ts +0 -45
  36. package/src/errors/consent-required-error.ts +0 -12
  37. package/src/errors/error-parser.ts +0 -147
  38. package/src/errors/handle-unavailable-error.ts +0 -23
  39. package/src/errors/invalid-authorization-details-error.ts +0 -27
  40. package/src/errors/invalid-client-error.ts +0 -20
  41. package/src/errors/invalid-client-id-error.ts +0 -31
  42. package/src/errors/invalid-client-metadata-error.ts +0 -68
  43. package/src/errors/invalid-credentials-error.ts +0 -29
  44. package/src/errors/invalid-dpop-key-binding-error.ts +0 -21
  45. package/src/errors/invalid-dpop-proof-error.ts +0 -13
  46. package/src/errors/invalid-grant-error.ts +0 -21
  47. package/src/errors/invalid-invite-code-error.ts +0 -10
  48. package/src/errors/invalid-redirect-uri-error.ts +0 -17
  49. package/src/errors/invalid-request-error.ts +0 -32
  50. package/src/errors/invalid-scope-error.ts +0 -15
  51. package/src/errors/invalid-token-error.ts +0 -60
  52. package/src/errors/login-required-error.ts +0 -12
  53. package/src/errors/oauth-error.ts +0 -28
  54. package/src/errors/second-authentication-factor-required-error.ts +0 -25
  55. package/src/errors/unauthorized-client-error.ts +0 -20
  56. package/src/errors/use-dpop-nonce-error.ts +0 -32
  57. package/src/errors/www-authenticate-error.ts +0 -64
  58. package/src/index.ts +0 -16
  59. package/src/lexicon/lexicon-data.ts +0 -11
  60. package/src/lexicon/lexicon-getter.ts +0 -55
  61. package/src/lexicon/lexicon-manager.ts +0 -116
  62. package/src/lexicon/lexicon-store.ts +0 -35
  63. package/src/lib/csp/index.ts +0 -101
  64. package/src/lib/hcaptcha.ts +0 -228
  65. package/src/lib/html/README.md +0 -9
  66. package/src/lib/html/build-document.ts +0 -151
  67. package/src/lib/html/escapers.ts +0 -66
  68. package/src/lib/html/html.ts +0 -56
  69. package/src/lib/html/hydration-data.ts +0 -19
  70. package/src/lib/html/index.ts +0 -5
  71. package/src/lib/html/tags.ts +0 -67
  72. package/src/lib/html/util.ts +0 -17
  73. package/src/lib/http/README.md +0 -11
  74. package/src/lib/http/accept.ts +0 -90
  75. package/src/lib/http/context.ts +0 -42
  76. package/src/lib/http/headers.ts +0 -15
  77. package/src/lib/http/index.ts +0 -10
  78. package/src/lib/http/method.ts +0 -18
  79. package/src/lib/http/middleware.ts +0 -169
  80. package/src/lib/http/parser.ts +0 -101
  81. package/src/lib/http/path.ts +0 -82
  82. package/src/lib/http/request.ts +0 -256
  83. package/src/lib/http/response.ts +0 -122
  84. package/src/lib/http/route.ts +0 -60
  85. package/src/lib/http/router.ts +0 -117
  86. package/src/lib/http/security-headers.ts +0 -100
  87. package/src/lib/http/stream.ts +0 -57
  88. package/src/lib/http/types.ts +0 -16
  89. package/src/lib/http/url.ts +0 -23
  90. package/src/lib/nsid.ts +0 -10
  91. package/src/lib/redis.ts +0 -25
  92. package/src/lib/util/authorization-header.ts +0 -28
  93. package/src/lib/util/cast.ts +0 -18
  94. package/src/lib/util/color.ts +0 -168
  95. package/src/lib/util/crypto.ts +0 -32
  96. package/src/lib/util/date.ts +0 -7
  97. package/src/lib/util/error.ts +0 -7
  98. package/src/lib/util/function.ts +0 -39
  99. package/src/lib/util/locale.ts +0 -12
  100. package/src/lib/util/object.ts +0 -23
  101. package/src/lib/util/redirect-uri.ts +0 -46
  102. package/src/lib/util/time.ts +0 -49
  103. package/src/lib/util/type.ts +0 -182
  104. package/src/lib/util/ui8.ts +0 -14
  105. package/src/lib/util/well-known.ts +0 -8
  106. package/src/lib/util/zod-error.ts +0 -26
  107. package/src/lib/write-form-redirect.ts +0 -58
  108. package/src/lib/write-html.ts +0 -70
  109. package/src/metadata/build-metadata.ts +0 -141
  110. package/src/oauth-client.ts +0 -3
  111. package/src/oauth-dpop.ts +0 -2
  112. package/src/oauth-errors.ts +0 -26
  113. package/src/oauth-hooks.ts +0 -519
  114. package/src/oauth-middleware.ts +0 -53
  115. package/src/oauth-provider.ts +0 -1110
  116. package/src/oauth-store.ts +0 -12
  117. package/src/oauth-verifier.ts +0 -260
  118. package/src/replay/replay-manager.ts +0 -51
  119. package/src/replay/replay-store-memory.ts +0 -36
  120. package/src/replay/replay-store-redis.ts +0 -30
  121. package/src/replay/replay-store.ts +0 -44
  122. package/src/request/code.ts +0 -23
  123. package/src/request/request-data.ts +0 -36
  124. package/src/request/request-id.ts +0 -22
  125. package/src/request/request-manager.ts +0 -521
  126. package/src/request/request-store.ts +0 -60
  127. package/src/request/request-uri.ts +0 -42
  128. package/src/result/authorization-redirect-parameters.ts +0 -24
  129. package/src/result/authorization-result-authorize-page.ts +0 -16
  130. package/src/result/authorization-result-redirect.ts +0 -8
  131. package/src/router/assets/assets-manifest.ts +0 -117
  132. package/src/router/assets/assets.ts +0 -124
  133. package/src/router/assets/csrf.ts +0 -79
  134. package/src/router/assets/send-account-page.ts +0 -23
  135. package/src/router/assets/send-authorization-page.ts +0 -42
  136. package/src/router/assets/send-cookie-error-page.ts +0 -21
  137. package/src/router/assets/send-error-page.ts +0 -25
  138. package/src/router/assets/send-redirect.ts +0 -140
  139. package/src/router/create-account-page-middleware.ts +0 -88
  140. package/src/router/create-api-middleware.ts +0 -1051
  141. package/src/router/create-authorization-page-middleware.ts +0 -281
  142. package/src/router/create-oauth-middleware.ts +0 -257
  143. package/src/router/error-handler.ts +0 -6
  144. package/src/router/middleware-options.ts +0 -9
  145. package/src/signer/access-token-payload.ts +0 -25
  146. package/src/signer/api-token-payload.ts +0 -18
  147. package/src/signer/signer.ts +0 -121
  148. package/src/token/refresh-token.ts +0 -30
  149. package/src/token/token-claims.ts +0 -22
  150. package/src/token/token-data.ts +0 -40
  151. package/src/token/token-id.ts +0 -25
  152. package/src/token/token-manager.ts +0 -427
  153. package/src/token/token-store.ts +0 -88
  154. package/src/types/authorization-response-error.ts +0 -27
  155. package/src/types/color-hue.ts +0 -3
  156. package/src/types/email-otp.ts +0 -3
  157. package/src/types/email.ts +0 -26
  158. package/src/types/handle.ts +0 -23
  159. package/src/types/invite-code.ts +0 -4
  160. package/src/types/par-response-error.ts +0 -25
  161. package/src/types/password.ts +0 -4
  162. package/src/types/rgb-color.ts +0 -21
  163. package/tsconfig.build.json +0 -8
  164. package/tsconfig.build.tsbuildinfo +0 -1
  165. package/tsconfig.json +0 -4
@@ -1,12 +0,0 @@
1
- /**
2
- * Every store file exports all the types needed to implement that store. This
3
- * files re-exports all the types from the x-store files.
4
- */
5
-
6
- export * from './account/account-store.js'
7
- export * from './client/client-store.js'
8
- export * from './device/device-store.js'
9
- export * from './lexicon/lexicon-store.js'
10
- export * from './replay/replay-store.js'
11
- export * from './request/request-store.js'
12
- export * from './token/token-store.js'
@@ -1,260 +0,0 @@
1
- import type { Redis, RedisOptions } from 'ioredis'
2
- import { Key, Keyset, isSignedJwt } from '@atproto/jwk'
3
- import {
4
- OAuthAccessToken,
5
- OAuthIssuerIdentifier,
6
- OAuthTokenType,
7
- oauthIssuerIdentifierSchema,
8
- } from '@atproto/oauth-types'
9
- import { DpopManager, DpopManagerOptions } from './dpop/dpop-manager.js'
10
- import { DpopNonce } from './dpop/dpop-nonce.js'
11
- import { DpopProof } from './dpop/dpop-proof.js'
12
- import { InvalidDpopKeyBindingError } from './errors/invalid-dpop-key-binding-error.js'
13
- import { InvalidDpopProofError } from './errors/invalid-dpop-proof-error.js'
14
- import { InvalidTokenError } from './errors/invalid-token-error.js'
15
- import { UseDpopNonceError } from './errors/use-dpop-nonce-error.js'
16
- import { WWWAuthenticateError } from './errors/www-authenticate-error.js'
17
- import { parseAuthorizationHeader } from './lib/util/authorization-header.js'
18
- import { includedIn } from './lib/util/function.js'
19
- import { OAuthHooks } from './oauth-hooks.js'
20
- import { ReplayManager } from './replay/replay-manager.js'
21
- import { ReplayStoreMemory } from './replay/replay-store-memory.js'
22
- import { ReplayStoreRedis } from './replay/replay-store-redis.js'
23
- import { ReplayStore } from './replay/replay-store.js'
24
- import { AccessTokenPayload } from './signer/access-token-payload.js'
25
- import { Signer } from './signer/signer.js'
26
-
27
- export type DecodeTokenHook = OAuthHooks['onDecodeToken']
28
-
29
- export type OAuthVerifierOptions = DpopManagerOptions & {
30
- /**
31
- * The "issuer" identifier of the OAuth provider, this is the base URL of the
32
- * OAuth provider.
33
- */
34
- issuer: URL | string
35
-
36
- /**
37
- * The keyset used to sign access tokens.
38
- */
39
- keyset: Keyset | Iterable<Key | undefined | null | false>
40
-
41
- /**
42
- * A redis instance to use for replay protection. If not provided, replay
43
- * protection will use memory storage.
44
- */
45
- redis?: Redis | RedisOptions | string
46
-
47
- replayStore?: ReplayStore
48
-
49
- onDecodeToken?: DecodeTokenHook
50
- }
51
-
52
- export type VerifyTokenPayloadOptions = {
53
- /** One of these audience must be included in the token audience(s) */
54
- audience?: [string, ...string[]]
55
- /** One of these scope must be included in the token scope(s) */
56
- scope?: [string, ...string[]]
57
- }
58
-
59
- export { DpopNonce, Key, Keyset }
60
- export type {
61
- AccessTokenPayload,
62
- DpopProof,
63
- OAuthTokenType,
64
- RedisOptions,
65
- ReplayStore,
66
- }
67
-
68
- export class OAuthVerifier {
69
- private readonly onDecodeToken?: DecodeTokenHook
70
-
71
- public readonly issuer: OAuthIssuerIdentifier
72
- public readonly keyset: Keyset
73
-
74
- public readonly dpopManager: DpopManager
75
- public readonly replayManager: ReplayManager
76
- public readonly signer: Signer
77
-
78
- constructor({
79
- redis,
80
- issuer,
81
- keyset,
82
- replayStore = redis != null
83
- ? new ReplayStoreRedis({ redis })
84
- : new ReplayStoreMemory(),
85
- onDecodeToken,
86
-
87
- ...rest
88
- }: OAuthVerifierOptions) {
89
- const dpopMgrOptions: DpopManagerOptions = rest
90
-
91
- const issuerParsed = oauthIssuerIdentifierSchema.parse(issuer)
92
- const issuerUrl = new URL(issuerParsed)
93
-
94
- // @TODO (?) support issuer with path
95
- if (issuerUrl.pathname !== '/') {
96
- throw new TypeError(
97
- `"issuer" must be an URL with no path, search or hash (${issuerUrl})`,
98
- )
99
- }
100
-
101
- this.issuer = issuerParsed
102
- this.keyset = keyset instanceof Keyset ? keyset : new Keyset(keyset)
103
-
104
- this.dpopManager = new DpopManager(dpopMgrOptions)
105
- this.replayManager = new ReplayManager(replayStore)
106
- this.signer = new Signer(this.issuer, this.keyset)
107
-
108
- this.onDecodeToken = onDecodeToken
109
- }
110
-
111
- public nextDpopNonce() {
112
- return this.dpopManager.nextNonce()
113
- }
114
-
115
- public async checkDpopProof(
116
- httpMethod: string,
117
- httpUrl: Readonly<URL>,
118
- httpHeaders: Record<string, undefined | string | string[]>,
119
- accessToken?: string,
120
- ): Promise<null | DpopProof> {
121
- const dpopProof = await this.dpopManager.checkProof(
122
- httpMethod,
123
- httpUrl,
124
- httpHeaders,
125
- accessToken,
126
- )
127
-
128
- if (dpopProof) {
129
- const unique = await this.replayManager.uniqueDpop(dpopProof.jti)
130
- if (!unique) throw new InvalidDpopProofError('DPoP proof replayed')
131
- }
132
-
133
- return dpopProof
134
- }
135
-
136
- protected async decodeToken(
137
- tokenType: OAuthTokenType,
138
- token: OAuthAccessToken,
139
- dpopProof: null | DpopProof,
140
- ): Promise<AccessTokenPayload> {
141
- if (!isSignedJwt(token)) {
142
- throw new InvalidTokenError(tokenType, `Malformed token`)
143
- }
144
-
145
- const { payload } = await this.signer
146
- .verifyAccessToken(token)
147
- .catch((err) => {
148
- throw InvalidTokenError.from(err, tokenType)
149
- })
150
-
151
- if (payload.cnf?.jkt) {
152
- // An access token with a cnf.jkt claim must be a DPoP token
153
- if (tokenType !== 'DPoP') {
154
- throw new InvalidTokenError(
155
- 'DPoP',
156
- `Access token is bound to a DPoP proof, but token type is ${tokenType}`,
157
- )
158
- }
159
-
160
- // DPoP token type must be used with a DPoP proof
161
- if (!dpopProof) {
162
- throw new InvalidDpopProofError(`DPoP proof required`)
163
- }
164
-
165
- // DPoP proof must be signed with the key that matches the "cnf" claim
166
- if (payload.cnf.jkt !== dpopProof.jkt) {
167
- throw new InvalidDpopKeyBindingError()
168
- }
169
- } else {
170
- // An access token without a cnf.jkt claim must be a Bearer token
171
- if (tokenType !== 'Bearer') {
172
- throw new InvalidTokenError(
173
- 'Bearer',
174
- `Bearer token type must be used without a DPoP proof`,
175
- )
176
- }
177
-
178
- // @NOTE We ignore (but allow) DPoP proofs for Bearer tokens
179
- }
180
-
181
- const payloadOverride = await this.onDecodeToken?.call(null, {
182
- tokenType,
183
- token,
184
- payload,
185
- dpopProof,
186
- })
187
-
188
- return payloadOverride ?? payload
189
- }
190
-
191
- /**
192
- * @throws {WWWAuthenticateError}
193
- * @throws {InvalidTokenError}
194
- */
195
- public async authenticateRequest(
196
- httpMethod: string,
197
- httpUrl: Readonly<URL>,
198
- httpHeaders: Record<string, undefined | string | string[]>,
199
- verifyOptions?: VerifyTokenPayloadOptions,
200
- ): Promise<AccessTokenPayload> {
201
- const [tokenType, token] = parseAuthorizationHeader(
202
- httpHeaders['authorization'],
203
- )
204
- try {
205
- const dpopProof = await this.checkDpopProof(
206
- httpMethod,
207
- httpUrl,
208
- httpHeaders,
209
- token,
210
- )
211
-
212
- const tokenPayload = await this.decodeToken(tokenType, token, dpopProof)
213
-
214
- this.verifyTokenPayload(tokenType, tokenPayload, verifyOptions)
215
-
216
- return tokenPayload
217
- } catch (err) {
218
- if (err instanceof UseDpopNonceError) throw err.toWwwAuthenticateError()
219
- if (err instanceof WWWAuthenticateError) throw err
220
-
221
- throw InvalidTokenError.from(err, tokenType)
222
- }
223
- }
224
-
225
- protected verifyTokenPayload(
226
- tokenType: OAuthTokenType,
227
- tokenPayload: AccessTokenPayload,
228
- options?: VerifyTokenPayloadOptions,
229
- ): void {
230
- if (options?.audience) {
231
- const { aud } = tokenPayload
232
- const hasMatch =
233
- aud != null &&
234
- (Array.isArray(aud)
235
- ? options.audience.some(includedIn, aud)
236
- : options.audience.includes(aud))
237
- if (!hasMatch) {
238
- const details = `(got: ${aud}, expected one of: ${options.audience})`
239
- throw new InvalidTokenError(tokenType, `Invalid audience ${details}`)
240
- }
241
- }
242
-
243
- if (options?.scope) {
244
- const { scope } = tokenPayload
245
- const scopes = scope?.split(' ')
246
- if (!scopes || !options.scope.some(includedIn, scopes)) {
247
- const details = `(got: ${scope}, expected one of: ${options.scope})`
248
- throw new InvalidTokenError(tokenType, `Invalid scope ${details}`)
249
- }
250
- }
251
-
252
- if (tokenPayload.exp != null && tokenPayload.exp * 1000 <= Date.now()) {
253
- const expirationDate = new Date(tokenPayload.exp * 1000).toISOString()
254
- throw new InvalidTokenError(
255
- tokenType,
256
- `Token expired at ${expirationDate}`,
257
- )
258
- }
259
- }
260
- }
@@ -1,51 +0,0 @@
1
- import { ClientId } from '../client/client-id.js'
2
- import {
3
- CLIENT_ASSERTION_MAX_AGE,
4
- CODE_CHALLENGE_REPLAY_TIMEFRAME,
5
- DPOP_NONCE_MAX_AGE,
6
- JAR_MAX_AGE,
7
- } from '../constants.js'
8
- import { ReplayStore } from './replay-store.js'
9
-
10
- const SECURITY_RATIO = 1.1 // 10% extra time for security
11
- const asTimeFrame = (timeFrame: number) => Math.ceil(timeFrame * SECURITY_RATIO)
12
-
13
- export class ReplayManager {
14
- constructor(protected readonly replayStore: ReplayStore) {}
15
-
16
- async uniqueAuth(
17
- jti: string,
18
- clientId: ClientId,
19
- exp?: number,
20
- ): Promise<boolean> {
21
- const timeFrame =
22
- exp == null
23
- ? asTimeFrame(CLIENT_ASSERTION_MAX_AGE)
24
- : exp * 1000 - Date.now()
25
- return this.replayStore.unique(`Auth@${clientId}`, jti, timeFrame)
26
- }
27
-
28
- async uniqueJar(jti: string, clientId: ClientId): Promise<boolean> {
29
- return this.replayStore.unique(
30
- `JAR@${clientId}`,
31
- jti,
32
- asTimeFrame(JAR_MAX_AGE),
33
- )
34
- }
35
-
36
- async uniqueDpop(jti: string, clientId?: ClientId): Promise<boolean> {
37
- return this.replayStore.unique(
38
- clientId ? `DPoP@${clientId}` : `DPoP`,
39
- jti,
40
- asTimeFrame(DPOP_NONCE_MAX_AGE),
41
- )
42
- }
43
-
44
- async uniqueCodeChallenge(challenge: string): Promise<boolean> {
45
- return this.replayStore.unique(
46
- 'CodeChallenge',
47
- challenge,
48
- asTimeFrame(CODE_CHALLENGE_REPLAY_TIMEFRAME),
49
- )
50
- }
51
- }
@@ -1,36 +0,0 @@
1
- import type { ReplayStore } from './replay-store.js'
2
-
3
- export class ReplayStoreMemory implements ReplayStore {
4
- private lastCleanup = Date.now()
5
- private nonces = new Map<string, number>()
6
-
7
- /**
8
- * Returns true if the nonce is unique within the given time frame.
9
- */
10
- async unique(
11
- namespace: string,
12
- nonce: string,
13
- timeFrame: number,
14
- ): Promise<boolean> {
15
- this.cleanup()
16
- const key = `${namespace}:${nonce}`
17
-
18
- const now = Date.now()
19
-
20
- const exp = this.nonces.get(key)
21
- this.nonces.set(key, now + timeFrame)
22
-
23
- return exp == null || exp < now
24
- }
25
-
26
- private cleanup() {
27
- const now = Date.now()
28
-
29
- if (this.lastCleanup < now - 60_000) {
30
- for (const [key, expires] of this.nonces) {
31
- if (expires < now) this.nonces.delete(key)
32
- }
33
- this.lastCleanup = now
34
- }
35
- }
36
- }
@@ -1,30 +0,0 @@
1
- import type { Redis } from 'ioredis'
2
- import { CreateRedisOptions, createRedis } from '../lib/redis.js'
3
- import type { ReplayStore } from './replay-store.js'
4
-
5
- export type { CreateRedisOptions, Redis }
6
-
7
- export type ReplayStoreRedisOptions = {
8
- redis: CreateRedisOptions
9
- }
10
-
11
- export class ReplayStoreRedis implements ReplayStore {
12
- private readonly redis: Redis
13
-
14
- constructor(options: ReplayStoreRedisOptions) {
15
- this.redis = createRedis(options.redis)
16
- }
17
-
18
- /**
19
- * Returns true if the nonce is unique within the given time frame.
20
- */
21
- async unique(
22
- namespace: string,
23
- nonce: string,
24
- timeFrame: number,
25
- ): Promise<boolean> {
26
- const key = `nonces:${namespace}:${nonce}`
27
- const prev = await this.redis.set(key, '1', 'PX', timeFrame, 'GET')
28
- return prev == null
29
- }
30
- }
@@ -1,44 +0,0 @@
1
- import { Awaitable } from '../lib/util/type.js'
2
-
3
- // Export all types needed to implement the ReplayStore interface
4
- export type { Awaitable }
5
-
6
- export interface ReplayStore {
7
- /**
8
- * Returns true if the nonce is unique within the given time frame. While not
9
- * strictly necessary for security purposes, the namespace should be used to
10
- * mitigate denial of service attacks from one client to the other.
11
- *
12
- * @param timeFrame expressed in milliseconds.
13
- */
14
- unique(
15
- namespace: string,
16
- nonce: string,
17
- timeFrame: number,
18
- ): Awaitable<boolean>
19
- }
20
-
21
- export function isReplayStore(
22
- implementation: Record<string, unknown> & Partial<ReplayStore>,
23
- ): implementation is Record<string, unknown> & ReplayStore {
24
- return typeof implementation.unique === 'function'
25
- }
26
-
27
- export function ifReplayStore(
28
- implementation?: Record<string, unknown> & Partial<ReplayStore>,
29
- ): ReplayStore | undefined {
30
- if (implementation && isReplayStore(implementation)) {
31
- return implementation
32
- }
33
-
34
- return undefined
35
- }
36
-
37
- export function asReplayStore(
38
- implementation?: Record<string, unknown> & Partial<ReplayStore>,
39
- ): ReplayStore {
40
- const store = ifReplayStore(implementation)
41
- if (store) return store
42
-
43
- throw new Error('Invalid ReplayStore implementation')
44
- }
@@ -1,23 +0,0 @@
1
- import { z } from 'zod'
2
- import { CODE_BYTES_LENGTH, CODE_PREFIX } from '../constants.js'
3
- import { randomHexId } from '../lib/util/crypto.js'
4
-
5
- export const CODE_LENGTH = CODE_PREFIX.length + CODE_BYTES_LENGTH * 2 // hex encoding
6
-
7
- export const codeSchema = z
8
- .string()
9
- .length(CODE_LENGTH) // hex encoding
10
- .refine(
11
- (v): v is `${typeof CODE_PREFIX}${string}` => v.startsWith(CODE_PREFIX),
12
- {
13
- message: `Invalid code format`,
14
- },
15
- )
16
-
17
- export const isCode = (data: unknown): data is Code =>
18
- codeSchema.safeParse(data).success
19
-
20
- export type Code = z.infer<typeof codeSchema>
21
- export const generateCode = async (): Promise<Code> => {
22
- return `${CODE_PREFIX}${await randomHexId(CODE_BYTES_LENGTH)}`
23
- }
@@ -1,36 +0,0 @@
1
- import { Did } from '@atproto/did'
2
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
3
- import { ClientAuth, ClientAuthLegacy } from '../client/client-auth.js'
4
- import { ClientId } from '../client/client-id.js'
5
- import { DeviceId } from '../device/device-id.js'
6
- import { NonNullableKeys } from '../lib/util/type.js'
7
- import { Code } from './code.js'
8
-
9
- export type {
10
- ClientAuth,
11
- ClientAuthLegacy,
12
- ClientId,
13
- Code,
14
- DeviceId,
15
- Did,
16
- OAuthAuthorizationRequestParameters,
17
- }
18
-
19
- export type RequestData = {
20
- clientId: ClientId
21
- clientAuth: null | ClientAuth | ClientAuthLegacy
22
- parameters: Readonly<OAuthAuthorizationRequestParameters>
23
- expiresAt: Date
24
- deviceId: DeviceId | null
25
- did: Did | null
26
- code: Code | null
27
- }
28
-
29
- export type RequestDataAuthorized = NonNullableKeys<
30
- RequestData,
31
- 'did' | 'deviceId'
32
- >
33
-
34
- export const isRequestDataAuthorized = (
35
- data: RequestData,
36
- ): data is RequestDataAuthorized => data.did !== null && data.deviceId !== null
@@ -1,22 +0,0 @@
1
- import { z } from 'zod'
2
- import { REQUEST_ID_BYTES_LENGTH, REQUEST_ID_PREFIX } from '../constants.js'
3
- import { randomHexId } from '../lib/util/crypto.js'
4
-
5
- export const REQUEST_ID_LENGTH =
6
- REQUEST_ID_PREFIX.length + REQUEST_ID_BYTES_LENGTH * 2 // hex encoding
7
-
8
- export const requestIdSchema = z
9
- .string()
10
- .length(REQUEST_ID_LENGTH)
11
- .refine(
12
- (v): v is `${typeof REQUEST_ID_PREFIX}${string}` =>
13
- v.startsWith(REQUEST_ID_PREFIX),
14
- {
15
- message: `Invalid request ID format`,
16
- },
17
- )
18
-
19
- export type RequestId = z.infer<typeof requestIdSchema>
20
- export const generateRequestId = async (): Promise<RequestId> => {
21
- return `${REQUEST_ID_PREFIX}${await randomHexId(REQUEST_ID_BYTES_LENGTH)}`
22
- }