@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,32 +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
- * : The request is missing a required parameter, includes an unsupported
7
- * parameter value (other than grant type), repeats a parameter, includes
8
- * multiple credentials, utilizes more than one mechanism for authenticating the
9
- * client, or is otherwise malformed.
10
- *
11
- * @see
12
- * {@link https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1 | RFC6749 - Authorization Code Grant, Authorization Request}
13
- * : The request is missing a required parameter, includes an invalid parameter
14
- * value, includes a parameter more than once, or is otherwise malformed.
15
- *
16
- * @see
17
- * {@link https://datatracker.ietf.org/doc/html/rfc6750#section-3.1 | RFC6750 - The WWW-Authenticate Response Header Field}
18
- * : The request is missing a required parameter, includes an unsupported
19
- * parameter or parameter value, repeats the same parameter, uses more than one
20
- * method for including an access token, or is otherwise malformed. The resource
21
- * server SHOULD respond with the HTTP 400 (Bad Request) status code.
22
- */
23
- export class InvalidRequestError extends OAuthError {
24
- constructor(error_description: string, cause?: unknown) {
25
- super('invalid_request', error_description, 400, cause)
26
- }
27
-
28
- static from(err: unknown, message = 'Invalid request data'): OAuthError {
29
- if (err instanceof OAuthError) return err
30
- return new InvalidRequestError(message, err)
31
- }
32
- }
@@ -1,15 +0,0 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
2
- import { AuthorizationError } from './authorization-error.js'
3
-
4
- /**
5
- * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1}
6
- */
7
- export class InvalidScopeError extends AuthorizationError {
8
- constructor(
9
- parameters: OAuthAuthorizationRequestParameters,
10
- error_description: string,
11
- cause?: unknown,
12
- ) {
13
- super(parameters, error_description, 'invalid_scope', cause)
14
- }
15
- }
@@ -1,60 +0,0 @@
1
- import { errors } from 'jose'
2
- import { ZodError } from 'zod'
3
- import { JwtVerifyError } from '@atproto/jwk'
4
- import { OAuthError } from './oauth-error.js'
5
- import { WWWAuthenticateError } from './www-authenticate-error.js'
6
-
7
- const { JOSEError } = errors
8
-
9
- /**
10
- * @see
11
- * {@link https://datatracker.ietf.org/doc/html/rfc6750#section-3.1 | RFC6750 - The WWW-Authenticate Response Header Field }
12
- *
13
- * The access token provided is expired, revoked, malformed, or invalid for
14
- * other reasons. The resource SHOULD respond with the HTTP 401 (Unauthorized)
15
- * status code. The client MAY request a new access token and retry the
16
- * protected resource request.
17
- */
18
- export class InvalidTokenError extends WWWAuthenticateError {
19
- static from(
20
- err: unknown,
21
- tokenType: string,
22
- fallbackMessage = 'Invalid token',
23
- ): InvalidTokenError {
24
- if (err instanceof InvalidTokenError) {
25
- return err
26
- }
27
-
28
- if (err instanceof OAuthError) {
29
- return new InvalidTokenError(tokenType, err.error_description, err)
30
- }
31
-
32
- if (err instanceof JOSEError) {
33
- return new InvalidTokenError(tokenType, err.message, err)
34
- }
35
-
36
- if (err instanceof JwtVerifyError) {
37
- return new InvalidTokenError(tokenType, err.message, err)
38
- }
39
-
40
- if (err instanceof ZodError) {
41
- return new InvalidTokenError(tokenType, err.message, err)
42
- }
43
-
44
- return new InvalidTokenError(tokenType, fallbackMessage, err)
45
- }
46
-
47
- constructor(
48
- readonly tokenType: string,
49
- error_description: string,
50
- cause?: unknown,
51
- ) {
52
- const error = 'invalid_token'
53
- super(
54
- error,
55
- error_description,
56
- { [tokenType]: { error, error_description } },
57
- cause,
58
- )
59
- }
60
- }
@@ -1,12 +0,0 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
2
- import { AuthorizationError } from './authorization-error.js'
3
-
4
- export class LoginRequiredError extends AuthorizationError {
5
- constructor(
6
- parameters: OAuthAuthorizationRequestParameters,
7
- error_description = 'Login is required',
8
- cause?: unknown,
9
- ) {
10
- super(parameters, error_description, 'login_required', cause)
11
- }
12
- }
@@ -1,28 +0,0 @@
1
- export class OAuthError extends Error {
2
- public expose: boolean
3
-
4
- constructor(
5
- public readonly error: string,
6
- public readonly error_description: string,
7
- public readonly status = 400,
8
- cause?: unknown,
9
- ) {
10
- super(error_description, { cause })
11
-
12
- Error.captureStackTrace?.(this, this.constructor)
13
-
14
- this.name = this.constructor.name
15
- this.expose = status < 500
16
- }
17
-
18
- get statusCode() {
19
- return this.status
20
- }
21
-
22
- toJSON() {
23
- return {
24
- error: this.error,
25
- error_description: this.error_description,
26
- }
27
- }
28
- }
@@ -1,25 +0,0 @@
1
- import { OAuthError } from './oauth-error.js'
2
-
3
- export class SecondAuthenticationFactorRequiredError extends OAuthError {
4
- constructor(
5
- public type: 'emailOtp',
6
- public hint: string,
7
- cause?: unknown,
8
- ) {
9
- const error = 'second_authentication_factor_required'
10
- super(
11
- error,
12
- `${type} authentication factor required (hint: ${hint})`,
13
- 401,
14
- cause,
15
- )
16
- }
17
-
18
- toJSON() {
19
- return {
20
- ...super.toJSON(),
21
- type: this.type,
22
- hint: this.hint,
23
- } as const
24
- }
25
- }
@@ -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
- * The authenticated client is not authorized to use this authorization grant
8
- * type.
9
- *
10
- * @see
11
- * {@link https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1 | RFC6749 - Authorization Code Grant, Authorization Request}
12
- *
13
- * The client is not authorized to request an authorization code using this
14
- * method.
15
- */
16
- export class UnauthorizedClientError extends OAuthError {
17
- constructor(error_description: string, cause?: unknown) {
18
- super('unauthorized_client', error_description, 400, cause)
19
- }
20
- }
@@ -1,32 +0,0 @@
1
- import { OAuthError } from './oauth-error.js'
2
- import { WWWAuthenticateError } from './www-authenticate-error.js'
3
-
4
- /**
5
- * @see
6
- * {@link https://datatracker.ietf.org/doc/html/rfc9449#section-8 | RFC9449 - Section 8. Authorization Server-Provided Nonce}
7
- */
8
- export class UseDpopNonceError extends OAuthError {
9
- constructor(
10
- error_description = 'Authorization server requires nonce in DPoP proof',
11
- cause?: unknown,
12
- ) {
13
- super('use_dpop_nonce', error_description, 400, cause)
14
- }
15
-
16
- /**
17
- * Convert this error into an error meant to be used as "Resource
18
- * Server-Provided Nonce" error.
19
- *
20
- * @see
21
- * {@link https://datatracker.ietf.org/doc/html/rfc9449#section-9 | RFC9449 - Section 9. Resource Server-Provided Nonce}
22
- */
23
- toWwwAuthenticateError(): WWWAuthenticateError {
24
- const { error, error_description } = this
25
- return new WWWAuthenticateError(
26
- error,
27
- error_description,
28
- { DPoP: { error, error_description } },
29
- this,
30
- )
31
- }
32
- }
@@ -1,64 +0,0 @@
1
- import { VERIFY_ALGOS } from '../lib/util/crypto.js'
2
- import { OAuthError } from './oauth-error.js'
3
-
4
- export type WWWAuthenticateParams = Record<string, string | undefined>
5
- export type WWWAuthenticate = Record<string, undefined | WWWAuthenticateParams>
6
-
7
- export class WWWAuthenticateError extends OAuthError {
8
- public readonly wwwAuthenticate: WWWAuthenticate
9
-
10
- constructor(
11
- error: string,
12
- error_description: string,
13
- wwwAuthenticate: WWWAuthenticate,
14
- cause?: unknown,
15
- ) {
16
- super(error, error_description, 401, cause)
17
-
18
- this.wwwAuthenticate =
19
- wwwAuthenticate['DPoP'] != null
20
- ? {
21
- ...wwwAuthenticate,
22
- DPoP: { algs: VERIFY_ALGOS.join(' '), ...wwwAuthenticate['DPoP'] },
23
- }
24
- : wwwAuthenticate
25
- }
26
-
27
- get wwwAuthenticateHeader() {
28
- return formatWWWAuthenticateHeader(this.wwwAuthenticate)
29
- }
30
- }
31
-
32
- function formatWWWAuthenticateHeader(wwwAuthenticate: WWWAuthenticate): string {
33
- return Object.entries(wwwAuthenticate)
34
- .filter(isWWWAuthenticateEntry)
35
- .map(wwwAuthenticateEntryToString)
36
- .join(', ')
37
- }
38
-
39
- type WWWAuthenticateEntry = [type: string, params: WWWAuthenticateParams]
40
- function isWWWAuthenticateEntry(
41
- entry: [string, unknown],
42
- ): entry is WWWAuthenticateEntry {
43
- const [, value] = entry
44
- return value != null && typeof value === 'object'
45
- }
46
-
47
- function wwwAuthenticateEntryToString([type, params]: WWWAuthenticateEntry) {
48
- const paramsEnc = Object.entries(params)
49
- .filter(isParamEntry)
50
- .map(paramEntryToString)
51
-
52
- return paramsEnc.length ? `${type} ${paramsEnc.join(', ')}` : type
53
- }
54
-
55
- type ParamEntry = [name: string, value: string]
56
-
57
- function isParamEntry(entry: [string, unknown]): entry is ParamEntry {
58
- const [, value] = entry
59
- return typeof value === 'string' && value !== '' && !value.includes('"')
60
- }
61
-
62
- function paramEntryToString([name, value]: ParamEntry): string {
63
- return `${name}="${value}"`
64
- }
package/src/index.ts DELETED
@@ -1,16 +0,0 @@
1
- // Avoid having to explicitly depend sub dependencies
2
- export * from '@atproto-labs/fetch'
3
- export * from '@atproto-labs/fetch-node'
4
- export * from '@atproto/jwk'
5
- export * from '@atproto/jwk-jose'
6
- export * from '@atproto/oauth-types'
7
-
8
- export * from './constants.js'
9
- export * from './oauth-client.js'
10
- export * from './oauth-dpop.js'
11
- export * from './oauth-errors.js'
12
- export * from './oauth-hooks.js'
13
- export * from './oauth-middleware.js'
14
- export * from './oauth-provider.js'
15
- export * from './oauth-store.js'
16
- export * from './oauth-verifier.js'
@@ -1,11 +0,0 @@
1
- import { LexiconDocument } from '@atproto/lex-document'
2
-
3
- export type { LexiconDocument }
4
-
5
- export type LexiconData = {
6
- createdAt: Date
7
- updatedAt: Date
8
- lastSucceededAt: null | Date
9
- uri: null | string
10
- lexicon: null | LexiconDocument
11
- }
@@ -1,55 +0,0 @@
1
- import { LexResolver, LexResolverError } from '@atproto/lex-resolver'
2
- import { Nsid } from '@atproto/oauth-scopes'
3
- import { CachedGetter } from '@atproto-labs/simple-store'
4
- import { LEXICON_REFRESH_FREQUENCY } from '../constants.js'
5
- import { LexiconData, LexiconStore } from './lexicon-store.js'
6
-
7
- /**
8
- * This utility class handles the retrieval and caching of lexicon
9
- * data. In particular, it handles failed retrieval attempts by returning cached
10
- * data.
11
- *
12
- * @private
13
- */
14
- export class LexiconGetter extends CachedGetter<Nsid, LexiconData> {
15
- constructor(store: LexiconStore, lexResolver: LexResolver) {
16
- super(
17
- async (input, options, storedData) => {
18
- const now = new Date()
19
- const result = await lexResolver.get(input, options).catch((err) => {
20
- // We swallow LexiconResolutionError errors, returning potentially
21
- // "null" values here to avoid hammering the resolver with requests
22
- // for the same lexicon that is known to be unavailable. The getter
23
- // should be called again based on the isStale() function below.
24
- if (err instanceof LexResolverError) return undefined
25
-
26
- // Unexpected error are propagated
27
- throw err
28
- })
29
-
30
- return {
31
- // Keep original createdAt, if available
32
- createdAt: storedData?.createdAt ?? now,
33
- // Always update updatedAt
34
- updatedAt: now,
35
- // Update the data with fresh data, if available, or keep cached
36
- // values (if any) otherwise.
37
- lastSucceededAt: result ? now : storedData?.lastSucceededAt ?? null,
38
- uri: result ? result.uri.toString() : storedData?.uri ?? null,
39
- lexicon: result ? result.lexicon : storedData?.lexicon ?? null,
40
- }
41
- },
42
- {
43
- set: async (nsid, data) => store.storeLexicon(nsid, data),
44
- get: async (nsid) => (await store.findLexicon(nsid)) ?? undefined,
45
- del: async (nsid) => store.deleteLexicon(nsid),
46
- },
47
- {
48
- isStale: (nsid, data) => {
49
- const timeSinceLastUpdate = Date.now() - data.updatedAt.getTime()
50
- return timeSinceLastUpdate >= LEXICON_REFRESH_FREQUENCY
51
- },
52
- },
53
- )
54
- }
55
- }
@@ -1,116 +0,0 @@
1
- import { LexiconPermissionSet } from '@atproto/lex-document'
2
- import { LexResolver, LexResolverError } from '@atproto/lex-resolver'
3
- import { IncludeScope, Nsid } from '@atproto/oauth-scopes'
4
- import { LexiconGetter } from './lexicon-getter.js'
5
- import { LexiconStore } from './lexicon-store.js'
6
-
7
- export * from './lexicon-store.js'
8
-
9
- export class LexiconManager {
10
- protected readonly lexiconGetter: LexiconGetter
11
-
12
- constructor(store: LexiconStore, lexResolver: LexResolver) {
13
- this.lexiconGetter = new LexiconGetter(store, lexResolver)
14
- }
15
-
16
- public async getPermissionSetsFromScope(scope?: string) {
17
- const { includeScopes } = parseScope(scope)
18
- return this.extractPermissionSets(includeScopes)
19
- }
20
-
21
- /**
22
- * Transforms a scope string from an authorization request into a scope
23
- * composed solely of granular permission scopes, transforming any NSID
24
- * into its corresponding permission scopes.
25
- */
26
- public async buildTokenScope(scope: string): Promise<string> {
27
- const { includeScopes, otherScopes } = parseScope(scope)
28
-
29
- // If the scope does not contain any "include:<nsid>" scopes, return it as-is.
30
- if (!includeScopes.length) return scope
31
-
32
- const permissionSets = await this.extractPermissionSets(includeScopes)
33
-
34
- return Array.from(includeScopes)
35
- .flatMap(nsidToPermissionScopes, permissionSets)
36
- .concat(otherScopes)
37
- .join(' ')
38
- }
39
-
40
- /**
41
- * Given a list of scope values, extract those that are NSIDs and return their
42
- * corresponding permission sets.
43
- */
44
- protected async extractPermissionSets(includeScopes: IncludeScope[]) {
45
- const nsids = extractNsids(includeScopes)
46
- return this.getPermissionSets(nsids)
47
- }
48
-
49
- protected async getPermissionSets(nsids: Set<Nsid>) {
50
- return new Map<string, LexiconPermissionSet>(
51
- await Promise.all(Array.from(nsids, this.getPermissionSetEntry, this)),
52
- )
53
- }
54
-
55
- protected async getPermissionSetEntry(
56
- nsid: Nsid,
57
- ): Promise<[nsid: Nsid, permissionSet: LexiconPermissionSet]> {
58
- const permissionSet = await this.getPermissionSet(nsid)
59
- return [nsid, permissionSet]
60
- }
61
-
62
- protected async getPermissionSet(nsid: Nsid): Promise<LexiconPermissionSet> {
63
- const { lexicon } = await this.lexiconGetter.get(nsid)
64
-
65
- if (!lexicon) {
66
- throw LexResolverError.from(nsid)
67
- }
68
-
69
- if (lexicon.defs.main?.type !== 'permission-set') {
70
- const description = 'Lexicon document is not a permission set'
71
- throw LexResolverError.from(nsid, description)
72
- }
73
-
74
- return lexicon.defs.main
75
- }
76
- }
77
-
78
- function parseScope(scope?: string) {
79
- const includeScopes: IncludeScope[] = []
80
- const otherScopes: string[] = []
81
-
82
- if (scope) {
83
- for (const scopeValue of scope.split(' ')) {
84
- const parsed = IncludeScope.fromString(scopeValue)
85
- if (parsed) {
86
- includeScopes.push(parsed)
87
- } else {
88
- otherScopes.push(scopeValue)
89
- }
90
- }
91
- }
92
-
93
- return {
94
- includeScopes,
95
- otherScopes,
96
- }
97
- }
98
-
99
- function extractNsids(includeScopes: IncludeScope[]): Set<Nsid> {
100
- return new Set(Array.from(includeScopes, extractNsid))
101
- }
102
-
103
- function extractNsid(nsidScope: IncludeScope): Nsid {
104
- return nsidScope.nsid
105
- }
106
-
107
- export function nsidToPermissionScopes(
108
- this: Map<string, LexiconPermissionSet>,
109
- includeScope: IncludeScope,
110
- ): string[] {
111
- const permissionSet = this.get(includeScope.nsid)
112
- if (permissionSet) return includeScope.toScopes(permissionSet)
113
-
114
- // Should never happen (mostly there for type safety & future proofing)
115
- throw new Error(`Missing permission set for NSID: ${includeScope.nsid}`)
116
- }
@@ -1,35 +0,0 @@
1
- import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'
2
- import { LexiconData, LexiconDocument } from './lexicon-data.js'
3
-
4
- export type { Awaitable, LexiconData, LexiconDocument }
5
-
6
- export interface LexiconStore {
7
- findLexicon(nsid: string): Awaitable<LexiconData | null>
8
- storeLexicon(nsid: string, data: LexiconData): Awaitable<void>
9
- deleteLexicon(nsid: string): Awaitable<void>
10
- }
11
-
12
- export const isLexiconStore = buildInterfaceChecker<LexiconStore>([
13
- 'deleteLexicon',
14
- 'findLexicon',
15
- 'storeLexicon',
16
- ])
17
-
18
- export function ifLexiconStore<V extends Partial<LexiconStore>>(
19
- implementation?: V,
20
- ): (V & LexiconStore) | undefined {
21
- if (implementation && isLexiconStore(implementation)) {
22
- return implementation
23
- }
24
-
25
- return undefined
26
- }
27
-
28
- export function asLexiconStore<V extends Partial<LexiconStore>>(
29
- implementation?: V,
30
- ): V & LexiconStore {
31
- const store = ifLexiconStore(implementation)
32
- if (store) return store
33
-
34
- throw new Error('Invalid LexiconStore implementation')
35
- }
@@ -1,101 +0,0 @@
1
- import { CombinedTuple, Simplify } from '../util/type.js'
2
-
3
- export type CspValue =
4
- | `data:`
5
- | `http:${string}`
6
- | `https:${string}`
7
- | `'none'`
8
- | `'self'`
9
- | `'sha256-${string}'`
10
- | `'nonce-${string}'`
11
- | `'unsafe-inline'`
12
- | `'unsafe-eval'`
13
- | `'strict-dynamic'`
14
- | `'report-sample'`
15
- | `'unsafe-hashes'`
16
-
17
- const STRING_DIRECTIVES = ['base-uri'] as const
18
- const BOOLEAN_DIRECTIVES = [
19
- 'upgrade-insecure-requests',
20
- 'block-all-mixed-content',
21
- ] as const
22
- const ARRAY_DIRECTIVES = [
23
- 'connect-src',
24
- 'default-src',
25
- 'form-action',
26
- 'frame-ancestors',
27
- 'frame-src',
28
- 'img-src',
29
- 'script-src',
30
- 'style-src',
31
- ] as const
32
-
33
- export type CspConfig = Simplify<
34
- {
35
- [K in (typeof BOOLEAN_DIRECTIVES)[number]]?: boolean
36
- } & {
37
- [K in (typeof STRING_DIRECTIVES)[number]]?: CspValue
38
- } & {
39
- [K in (typeof ARRAY_DIRECTIVES)[number]]?: Iterable<CspValue>
40
- }
41
- >
42
-
43
- const NONE = "'none'"
44
-
45
- export function buildCsp(config: CspConfig): string {
46
- const values: string[] = []
47
-
48
- for (const name of BOOLEAN_DIRECTIVES) {
49
- if (config[name] === true) values.push(name)
50
- }
51
-
52
- for (const name of STRING_DIRECTIVES) {
53
- if (config[name]) values.push(`${name} ${config[name]}`)
54
- }
55
-
56
- for (const name of ARRAY_DIRECTIVES) {
57
- // Remove duplicate values by using a Set
58
- const val = config[name] ? new Set(config[name]) : undefined
59
- if (val?.size) values.push(`${name} ${Array.from(val).join(' ')}`)
60
- }
61
-
62
- return values.join('; ')
63
- }
64
-
65
- export function mergeCsp<C extends (CspConfig | null | undefined)[]>(
66
- ...configs: C
67
- ) {
68
- return configs.filter((v) => v != null).reduce(combineCsp) as CombinedTuple<C>
69
- }
70
-
71
- export function combineCsp(a: CspConfig, b: CspConfig): CspConfig {
72
- const result: CspConfig = {}
73
-
74
- for (const name of BOOLEAN_DIRECTIVES) {
75
- // @NOTE b (if defined) takes precedence
76
- const value = b[name] ?? a[name]
77
- if (value != null) result[name] = value
78
- }
79
-
80
- for (const name of STRING_DIRECTIVES) {
81
- if (a[name] || b[name]) {
82
- const aNotNone = a[name] === NONE ? undefined : a[name]
83
- const bNotNone = b[name] === NONE ? undefined : b[name]
84
- // @NOTE b takes precedence
85
- result[name] = bNotNone || aNotNone || NONE
86
- }
87
- }
88
-
89
- for (const name of ARRAY_DIRECTIVES) {
90
- if (a[name] && b[name]) {
91
- const set = new Set(a[name])
92
- if (b[name]) for (const value of b[name]) set.add(value)
93
- if (set.size > 1 && set.has(NONE)) set.delete(NONE)
94
- result[name] = [...set]
95
- } else if (a[name] || b[name]) {
96
- result[name] = a[name] || b[name]
97
- }
98
- }
99
-
100
- return result
101
- }