@atproto/oauth-provider 0.16.0 → 0.16.2

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 (37) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/account/account-manager.d.ts +2 -1
  3. package/dist/account/account-manager.d.ts.map +1 -1
  4. package/dist/account/account-manager.js +45 -4
  5. package/dist/account/account-manager.js.map +1 -1
  6. package/dist/account/account-store.d.ts +9 -3
  7. package/dist/account/account-store.d.ts.map +1 -1
  8. package/dist/account/account-store.js +2 -1
  9. package/dist/account/account-store.js.map +1 -1
  10. package/dist/errors/invalid-credentials-error.d.ts +24 -0
  11. package/dist/errors/invalid-credentials-error.d.ts.map +1 -0
  12. package/dist/errors/invalid-credentials-error.js +30 -0
  13. package/dist/errors/invalid-credentials-error.js.map +1 -0
  14. package/dist/oauth-errors.d.ts +1 -0
  15. package/dist/oauth-errors.d.ts.map +1 -1
  16. package/dist/oauth-errors.js +1 -0
  17. package/dist/oauth-errors.js.map +1 -1
  18. package/dist/oauth-hooks.d.ts +40 -1
  19. package/dist/oauth-hooks.d.ts.map +1 -1
  20. package/dist/oauth-hooks.js +3 -1
  21. package/dist/oauth-hooks.js.map +1 -1
  22. package/dist/request/request-manager.d.ts +7 -0
  23. package/dist/request/request-manager.d.ts.map +1 -1
  24. package/dist/request/request-manager.js +11 -0
  25. package/dist/request/request-manager.js.map +1 -1
  26. package/dist/router/create-api-middleware.d.ts.map +1 -1
  27. package/dist/router/create-api-middleware.js +6 -1
  28. package/dist/router/create-api-middleware.js.map +1 -1
  29. package/package.json +3 -3
  30. package/src/account/account-manager.ts +49 -6
  31. package/src/account/account-store.ts +9 -1
  32. package/src/errors/invalid-credentials-error.ts +29 -0
  33. package/src/oauth-errors.ts +1 -0
  34. package/src/oauth-hooks.ts +42 -0
  35. package/src/request/request-manager.ts +12 -0
  36. package/src/router/create-api-middleware.ts +7 -0
  37. package/tsconfig.build.tsbuildinfo +1 -1
@@ -23,6 +23,7 @@ import { DeviceId } from './device/device-id.js'
23
23
  import { DpopProof } from './dpop/dpop-proof.js'
24
24
  import { AccessDeniedError } from './errors/access-denied-error.js'
25
25
  import { AuthorizationError } from './errors/authorization-error.js'
26
+ import { InvalidCredentialsError } from './errors/invalid-credentials-error.js'
26
27
  import { InvalidRequestError } from './errors/invalid-request-error.js'
27
28
  import { OAuthError } from './errors/oauth-error.js'
28
29
  import {
@@ -32,6 +33,7 @@ import {
32
33
  } from './lib/hcaptcha.js'
33
34
  import { RequestMetadata } from './lib/http/request.js'
34
35
  import { Awaitable, OmitKey } from './lib/util/type.js'
36
+ import { Sub } from './oidc/sub.js'
35
37
  import { RequestId } from './request/request-id.js'
36
38
  import { AccessTokenPayload } from './signer/access-token-payload.js'
37
39
  import { TokenClaims } from './token/token-claims.js'
@@ -52,6 +54,7 @@ export {
52
54
  type HcaptchaClientTokens,
53
55
  type HcaptchaConfig,
54
56
  type HcaptchaVerifyResult,
57
+ InvalidCredentialsError,
55
58
  InvalidRequestError,
56
59
  type Jwks,
57
60
  type OAuthAccessToken,
@@ -67,6 +70,7 @@ export {
67
70
  type SignInData,
68
71
  type SignUpData,
69
72
  type SignUpInput,
73
+ type Sub,
70
74
  type TokenClaims,
71
75
  }
72
76
 
@@ -159,15 +163,25 @@ export type OAuthHooks = {
159
163
  deviceMetadata: RequestMetadata
160
164
  }) => Awaitable<void>
161
165
 
166
+ /**
167
+ * `clientId` is populated when the sign-in is submitted in the context of
168
+ * an OAuth authorization request (i.e. the user is logging in to approve a
169
+ * client); it is omitted for first-party sign-ins that happen outside any
170
+ * authorization flow.
171
+ */
162
172
  onSignInAttempt?: (data: {
163
173
  data: SignInData
164
174
  deviceId: DeviceId
165
175
  deviceMetadata: RequestMetadata
176
+ clientId?: ClientId
166
177
  }) => Awaitable<void>
167
178
 
168
179
  /**
169
180
  * This hook is called when a user successfully signs in.
170
181
  *
182
+ * `clientId` is populated when the sign-in is submitted in the context of
183
+ * an OAuth authorization request; see {@link OAuthHooks.onSignInAttempt}.
184
+ *
171
185
  * @throws {InvalidRequestError} when the sing-in should be denied
172
186
  */
173
187
  onSignedIn?: (data: {
@@ -175,6 +189,34 @@ export type OAuthHooks = {
175
189
  account: Account
176
190
  deviceId: DeviceId
177
191
  deviceMetadata: RequestMetadata
192
+ clientId?: ClientId
193
+ }) => Awaitable<void>
194
+
195
+ /**
196
+ * This hook is called when a sign-in attempt is rejected by the account
197
+ * store due to invalid credentials (e.g. unknown identifier, wrong
198
+ * password). It is *not* called for unexpected server errors, nor for flows
199
+ * that require an additional authentication factor.
200
+ *
201
+ * `sub` is populated when the store throws an
202
+ * {@link InvalidCredentialsError} that carries the matched subject
203
+ * identifier (i.e. identifier known, credentials wrong). It is `null` when
204
+ * the identifier was unknown or when the store threw a plain
205
+ * {@link InvalidRequestError} without distinguishing the two cases.
206
+ *
207
+ * `clientId` is populated when the sign-in is submitted in the context of
208
+ * an OAuth authorization request; see {@link OAuthHooks.onSignInAttempt}.
209
+ *
210
+ * Errors thrown from this hook are caught and ignored so that they do not
211
+ * mask the original authentication failure.
212
+ */
213
+ onSignInFailed?: (data: {
214
+ data: SignInData
215
+ error: InvalidRequestError
216
+ sub: Sub | null
217
+ deviceId: DeviceId
218
+ deviceMetadata: RequestMetadata
219
+ clientId?: ClientId
178
220
  }) => Awaitable<void>
179
221
 
180
222
  /**
@@ -316,6 +316,18 @@ export class RequestManager {
316
316
  return parameters
317
317
  }
318
318
 
319
+ /**
320
+ * Reads the {@link ClientId} associated with a request URI without any of
321
+ * the validation or side-effects performed by {@link RequestManager.get}
322
+ *
323
+ * Returns `undefined` when no such request exists.
324
+ */
325
+ async peekClientId(requestUri: RequestUri): Promise<ClientId | undefined> {
326
+ const requestId = decodeRequestUri(requestUri)
327
+ const data = await this.store.readRequest(requestId)
328
+ return data?.clientId
329
+ }
330
+
319
331
  async get(requestUri: RequestUri, deviceId?: DeviceId, clientId?: ClientId) {
320
332
  const requestId = decodeRequestUri(requestUri)
321
333
 
@@ -145,10 +145,17 @@ export function createApiMiddleware<
145
145
  // Remember when not in the context of a request by default
146
146
  const { remember = requestUri == null, ...input } = this.input
147
147
 
148
+ // Look up the client identifier associated with the pending OAuth
149
+ // request, if any, so it can be surfaced to the sign-in hooks.
150
+ const clientId = requestUri
151
+ ? await server.requestManager.peekClientId(requestUri)
152
+ : undefined
153
+
148
154
  const account = await server.accountManager.authenticateAccount(
149
155
  deviceId,
150
156
  deviceMetadata,
151
157
  input,
158
+ clientId,
152
159
  )
153
160
 
154
161
  if (remember) {
@@ -1 +1 @@
1
- {"root":["./src/constants.ts","./src/index.ts","./src/oauth-client.ts","./src/oauth-dpop.ts","./src/oauth-errors.ts","./src/oauth-hooks.ts","./src/oauth-middleware.ts","./src/oauth-provider.ts","./src/oauth-store.ts","./src/oauth-verifier.ts","./src/access-token/access-token-mode.ts","./src/account/account-manager.ts","./src/account/account-store.ts","./src/account/sign-in-data.ts","./src/account/sign-up-input.ts","./src/client/client-auth.ts","./src/client/client-data.ts","./src/client/client-id.ts","./src/client/client-info.ts","./src/client/client-manager.ts","./src/client/client-store.ts","./src/client/client-utils.ts","./src/client/client.ts","./src/customization/branding.ts","./src/customization/build-customization-css.ts","./src/customization/build-customization-data.ts","./src/customization/colors.ts","./src/customization/customization.ts","./src/customization/links.ts","./src/device/device-data.ts","./src/device/device-id.ts","./src/device/device-manager.ts","./src/device/device-store.ts","./src/device/session-id.ts","./src/dpop/dpop-manager.ts","./src/dpop/dpop-nonce.ts","./src/dpop/dpop-proof.ts","./src/errors/access-denied-error.ts","./src/errors/account-selection-required-error.ts","./src/errors/authorization-error.ts","./src/errors/consent-required-error.ts","./src/errors/error-parser.ts","./src/errors/handle-unavailable-error.ts","./src/errors/invalid-authorization-details-error.ts","./src/errors/invalid-client-error.ts","./src/errors/invalid-client-id-error.ts","./src/errors/invalid-client-metadata-error.ts","./src/errors/invalid-dpop-key-binding-error.ts","./src/errors/invalid-dpop-proof-error.ts","./src/errors/invalid-grant-error.ts","./src/errors/invalid-invite-code-error.ts","./src/errors/invalid-redirect-uri-error.ts","./src/errors/invalid-request-error.ts","./src/errors/invalid-scope-error.ts","./src/errors/invalid-token-error.ts","./src/errors/login-required-error.ts","./src/errors/oauth-error.ts","./src/errors/second-authentication-factor-required-error.ts","./src/errors/unauthorized-client-error.ts","./src/errors/use-dpop-nonce-error.ts","./src/errors/www-authenticate-error.ts","./src/lexicon/lexicon-data.ts","./src/lexicon/lexicon-getter.ts","./src/lexicon/lexicon-manager.ts","./src/lexicon/lexicon-store.ts","./src/lib/hcaptcha.ts","./src/lib/nsid.ts","./src/lib/redis.ts","./src/lib/write-form-redirect.ts","./src/lib/write-html.ts","./src/lib/csp/index.ts","./src/lib/html/build-document.ts","./src/lib/html/escapers.ts","./src/lib/html/html.ts","./src/lib/html/hydration-data.ts","./src/lib/html/index.ts","./src/lib/html/tags.ts","./src/lib/html/util.ts","./src/lib/http/accept.ts","./src/lib/http/context.ts","./src/lib/http/headers.ts","./src/lib/http/index.ts","./src/lib/http/method.ts","./src/lib/http/middleware.ts","./src/lib/http/parser.ts","./src/lib/http/path.ts","./src/lib/http/request.ts","./src/lib/http/response.ts","./src/lib/http/route.ts","./src/lib/http/router.ts","./src/lib/http/security-headers.ts","./src/lib/http/stream.ts","./src/lib/http/types.ts","./src/lib/http/url.ts","./src/lib/util/authorization-header.ts","./src/lib/util/cast.ts","./src/lib/util/color.ts","./src/lib/util/crypto.ts","./src/lib/util/date.ts","./src/lib/util/error.ts","./src/lib/util/function.ts","./src/lib/util/locale.ts","./src/lib/util/object.ts","./src/lib/util/redirect-uri.ts","./src/lib/util/time.ts","./src/lib/util/type.ts","./src/lib/util/ui8.ts","./src/lib/util/well-known.ts","./src/lib/util/zod-error.ts","./src/metadata/build-metadata.ts","./src/oidc/sub.ts","./src/replay/replay-manager.ts","./src/replay/replay-store-memory.ts","./src/replay/replay-store-redis.ts","./src/replay/replay-store.ts","./src/request/code.ts","./src/request/request-data.ts","./src/request/request-id.ts","./src/request/request-manager.ts","./src/request/request-store.ts","./src/request/request-uri.ts","./src/result/authorization-redirect-parameters.ts","./src/result/authorization-result-authorize-page.ts","./src/result/authorization-result-redirect.ts","./src/router/create-account-page-middleware.ts","./src/router/create-api-middleware.ts","./src/router/create-authorization-page-middleware.ts","./src/router/create-oauth-middleware.ts","./src/router/error-handler.ts","./src/router/middleware-options.ts","./src/router/assets/assets-manifest.ts","./src/router/assets/assets.ts","./src/router/assets/csrf.ts","./src/router/assets/send-account-page.ts","./src/router/assets/send-authorization-page.ts","./src/router/assets/send-cookie-error-page.ts","./src/router/assets/send-error-page.ts","./src/router/assets/send-redirect.ts","./src/signer/access-token-payload.ts","./src/signer/api-token-payload.ts","./src/signer/signer.ts","./src/token/refresh-token.ts","./src/token/token-claims.ts","./src/token/token-data.ts","./src/token/token-id.ts","./src/token/token-manager.ts","./src/token/token-store.ts","./src/types/authorization-response-error.ts","./src/types/color-hue.ts","./src/types/email-otp.ts","./src/types/email.ts","./src/types/handle.ts","./src/types/invite-code.ts","./src/types/par-response-error.ts","./src/types/password.ts","./src/types/rgb-color.ts"],"version":"5.8.3"}
1
+ {"root":["./src/constants.ts","./src/index.ts","./src/oauth-client.ts","./src/oauth-dpop.ts","./src/oauth-errors.ts","./src/oauth-hooks.ts","./src/oauth-middleware.ts","./src/oauth-provider.ts","./src/oauth-store.ts","./src/oauth-verifier.ts","./src/access-token/access-token-mode.ts","./src/account/account-manager.ts","./src/account/account-store.ts","./src/account/sign-in-data.ts","./src/account/sign-up-input.ts","./src/client/client-auth.ts","./src/client/client-data.ts","./src/client/client-id.ts","./src/client/client-info.ts","./src/client/client-manager.ts","./src/client/client-store.ts","./src/client/client-utils.ts","./src/client/client.ts","./src/customization/branding.ts","./src/customization/build-customization-css.ts","./src/customization/build-customization-data.ts","./src/customization/colors.ts","./src/customization/customization.ts","./src/customization/links.ts","./src/device/device-data.ts","./src/device/device-id.ts","./src/device/device-manager.ts","./src/device/device-store.ts","./src/device/session-id.ts","./src/dpop/dpop-manager.ts","./src/dpop/dpop-nonce.ts","./src/dpop/dpop-proof.ts","./src/errors/access-denied-error.ts","./src/errors/account-selection-required-error.ts","./src/errors/authorization-error.ts","./src/errors/consent-required-error.ts","./src/errors/error-parser.ts","./src/errors/handle-unavailable-error.ts","./src/errors/invalid-authorization-details-error.ts","./src/errors/invalid-client-error.ts","./src/errors/invalid-client-id-error.ts","./src/errors/invalid-client-metadata-error.ts","./src/errors/invalid-credentials-error.ts","./src/errors/invalid-dpop-key-binding-error.ts","./src/errors/invalid-dpop-proof-error.ts","./src/errors/invalid-grant-error.ts","./src/errors/invalid-invite-code-error.ts","./src/errors/invalid-redirect-uri-error.ts","./src/errors/invalid-request-error.ts","./src/errors/invalid-scope-error.ts","./src/errors/invalid-token-error.ts","./src/errors/login-required-error.ts","./src/errors/oauth-error.ts","./src/errors/second-authentication-factor-required-error.ts","./src/errors/unauthorized-client-error.ts","./src/errors/use-dpop-nonce-error.ts","./src/errors/www-authenticate-error.ts","./src/lexicon/lexicon-data.ts","./src/lexicon/lexicon-getter.ts","./src/lexicon/lexicon-manager.ts","./src/lexicon/lexicon-store.ts","./src/lib/hcaptcha.ts","./src/lib/nsid.ts","./src/lib/redis.ts","./src/lib/write-form-redirect.ts","./src/lib/write-html.ts","./src/lib/csp/index.ts","./src/lib/html/build-document.ts","./src/lib/html/escapers.ts","./src/lib/html/html.ts","./src/lib/html/hydration-data.ts","./src/lib/html/index.ts","./src/lib/html/tags.ts","./src/lib/html/util.ts","./src/lib/http/accept.ts","./src/lib/http/context.ts","./src/lib/http/headers.ts","./src/lib/http/index.ts","./src/lib/http/method.ts","./src/lib/http/middleware.ts","./src/lib/http/parser.ts","./src/lib/http/path.ts","./src/lib/http/request.ts","./src/lib/http/response.ts","./src/lib/http/route.ts","./src/lib/http/router.ts","./src/lib/http/security-headers.ts","./src/lib/http/stream.ts","./src/lib/http/types.ts","./src/lib/http/url.ts","./src/lib/util/authorization-header.ts","./src/lib/util/cast.ts","./src/lib/util/color.ts","./src/lib/util/crypto.ts","./src/lib/util/date.ts","./src/lib/util/error.ts","./src/lib/util/function.ts","./src/lib/util/locale.ts","./src/lib/util/object.ts","./src/lib/util/redirect-uri.ts","./src/lib/util/time.ts","./src/lib/util/type.ts","./src/lib/util/ui8.ts","./src/lib/util/well-known.ts","./src/lib/util/zod-error.ts","./src/metadata/build-metadata.ts","./src/oidc/sub.ts","./src/replay/replay-manager.ts","./src/replay/replay-store-memory.ts","./src/replay/replay-store-redis.ts","./src/replay/replay-store.ts","./src/request/code.ts","./src/request/request-data.ts","./src/request/request-id.ts","./src/request/request-manager.ts","./src/request/request-store.ts","./src/request/request-uri.ts","./src/result/authorization-redirect-parameters.ts","./src/result/authorization-result-authorize-page.ts","./src/result/authorization-result-redirect.ts","./src/router/create-account-page-middleware.ts","./src/router/create-api-middleware.ts","./src/router/create-authorization-page-middleware.ts","./src/router/create-oauth-middleware.ts","./src/router/error-handler.ts","./src/router/middleware-options.ts","./src/router/assets/assets-manifest.ts","./src/router/assets/assets.ts","./src/router/assets/csrf.ts","./src/router/assets/send-account-page.ts","./src/router/assets/send-authorization-page.ts","./src/router/assets/send-cookie-error-page.ts","./src/router/assets/send-error-page.ts","./src/router/assets/send-redirect.ts","./src/signer/access-token-payload.ts","./src/signer/api-token-payload.ts","./src/signer/signer.ts","./src/token/refresh-token.ts","./src/token/token-claims.ts","./src/token/token-data.ts","./src/token/token-id.ts","./src/token/token-manager.ts","./src/token/token-store.ts","./src/types/authorization-response-error.ts","./src/types/color-hue.ts","./src/types/email-otp.ts","./src/types/email.ts","./src/types/handle.ts","./src/types/invite-code.ts","./src/types/par-response-error.ts","./src/types/password.ts","./src/types/rgb-color.ts"],"version":"5.8.3"}