@toa.io/extensions.exposition 1.0.0-alpha.282 → 1.0.0-alpha.283
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 +27 -0
- package/components/exposition.octets/manifest.toa.yaml +11 -0
- package/components/exposition.octets/types/index.d.ts +3 -0
- package/components/exposition.octets/types/toa.d.ts +41 -0
- package/components/identity.bans/types/index.d.ts +3 -0
- package/components/identity.bans/types/toa.d.ts +34 -0
- package/components/identity.basic/manifest.toa.yaml +13 -2
- package/components/identity.basic/operations/add.d.ts +3 -3
- package/components/identity.basic/operations/authenticate.d.ts +2 -2
- package/components/identity.basic/operations/check.d.ts +1 -1
- package/components/identity.basic/operations/delete.d.ts +2 -2
- package/components/identity.basic/operations/incept.d.ts +2 -2
- package/components/identity.basic/operations/info.d.ts +1 -1
- package/components/identity.basic/operations/transit.d.ts +4 -3
- package/components/identity.basic/operations/transit.js +2 -1
- package/components/identity.basic/operations/transit.js.map +1 -1
- package/components/identity.basic/source/add.ts +3 -3
- package/components/identity.basic/source/authenticate.ts +2 -2
- package/components/identity.basic/source/check.ts +1 -1
- package/components/identity.basic/source/delete.ts +2 -2
- package/components/identity.basic/source/incept.ts +2 -2
- package/components/identity.basic/source/info.ts +1 -1
- package/components/identity.basic/source/transit.ts +6 -4
- package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
- package/components/identity.basic/types/index.d.ts +21 -0
- package/components/identity.basic/types/toa.d.ts +121 -0
- package/components/identity.clients/manifest.toa.yaml +243 -0
- package/components/identity.clients/operations/describe.d.ts +27 -0
- package/components/identity.clients/operations/describe.js +44 -0
- package/components/identity.clients/operations/describe.js.map +1 -0
- package/components/identity.clients/operations/lib/Context.d.ts +29 -0
- package/components/identity.clients/operations/lib/Context.js +2 -0
- package/components/identity.clients/operations/lib/Context.js.map +1 -0
- package/components/identity.clients/operations/lib/Entity.d.ts +29 -0
- package/components/identity.clients/operations/lib/Entity.js +2 -0
- package/components/identity.clients/operations/lib/Entity.js.map +1 -0
- package/components/identity.clients/operations/lib/canonical.d.ts +11 -0
- package/components/identity.clients/operations/lib/canonical.js +24 -0
- package/components/identity.clients/operations/lib/canonical.js.map +1 -0
- package/components/identity.clients/operations/lib/cimd.d.ts +8 -0
- package/components/identity.clients/operations/lib/cimd.js +86 -0
- package/components/identity.clients/operations/lib/cimd.js.map +1 -0
- package/components/identity.clients/operations/lib/errors.d.ts +7 -0
- package/components/identity.clients/operations/lib/errors.js +5 -0
- package/components/identity.clients/operations/lib/errors.js.map +1 -0
- package/components/identity.clients/operations/lib/index.d.ts +6 -0
- package/components/identity.clients/operations/lib/index.js +7 -0
- package/components/identity.clients/operations/lib/index.js.map +1 -0
- package/components/identity.clients/operations/lib/redirect.d.ts +10 -0
- package/components/identity.clients/operations/lib/redirect.js +32 -0
- package/components/identity.clients/operations/lib/redirect.js.map +1 -0
- package/components/identity.clients/operations/register.d.ts +37 -0
- package/components/identity.clients/operations/register.js +84 -0
- package/components/identity.clients/operations/register.js.map +1 -0
- package/components/identity.clients/source/describe.ts +65 -0
- package/components/identity.clients/source/lib/Context.ts +38 -0
- package/components/identity.clients/source/lib/Entity.ts +31 -0
- package/components/identity.clients/source/lib/canonical.test.ts +43 -0
- package/components/identity.clients/source/lib/canonical.ts +27 -0
- package/components/identity.clients/source/lib/cimd.test.ts +120 -0
- package/components/identity.clients/source/lib/cimd.ts +107 -0
- package/components/identity.clients/source/lib/errors.ts +4 -0
- package/components/identity.clients/source/lib/index.ts +6 -0
- package/components/identity.clients/source/lib/redirect.test.ts +41 -0
- package/components/identity.clients/source/lib/redirect.ts +35 -0
- package/components/identity.clients/source/register.ts +125 -0
- package/components/identity.clients/tsconfig.json +10 -0
- package/components/identity.clients/tsconfig.tsbuildinfo +1 -0
- package/components/identity.clients/types/index.d.ts +3 -0
- package/components/identity.clients/types/toa.d.ts +109 -0
- package/components/identity.credentials/operations/list.d.ts +2 -1
- package/components/identity.credentials/operations/list.js.map +1 -1
- package/components/identity.credentials/source/list.ts +2 -1
- package/components/identity.credentials/tsconfig.tsbuildinfo +1 -1
- package/components/identity.credentials/types/index.d.ts +3 -0
- package/components/identity.credentials/types/toa.d.ts +30 -0
- package/components/identity.federation/manifest.toa.yaml +37 -1
- package/components/identity.federation/operations/authenticate.d.ts +1 -1
- package/components/identity.federation/operations/lib/Ctx.d.ts +2 -2
- package/components/identity.federation/operations/lib/decode.js +22 -8
- package/components/identity.federation/operations/lib/decode.js.map +1 -1
- package/components/identity.federation/operations/lib/errors.d.ts +7 -0
- package/components/identity.federation/operations/lib/errors.js +4 -0
- package/components/identity.federation/operations/lib/errors.js.map +1 -1
- package/components/identity.federation/operations/lib/exchange.js +11 -6
- package/components/identity.federation/operations/lib/exchange.js.map +1 -1
- package/components/identity.federation/operations/types/configuration.d.ts +1 -1
- package/components/identity.federation/operations/types/context.d.ts +3 -2
- package/components/identity.federation/source/authenticate.ts +1 -1
- package/components/identity.federation/source/lib/Ctx.ts +2 -2
- package/components/identity.federation/source/lib/decode.ts +25 -9
- package/components/identity.federation/source/lib/errors.ts +5 -0
- package/components/identity.federation/source/lib/exchange.ts +11 -7
- package/components/identity.federation/source/types/configuration.ts +1 -1
- package/components/identity.federation/source/types/context.ts +3 -2
- package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/types/index.d.ts +3 -0
- package/components/identity.federation/types/toa.d.ts +127 -0
- package/components/identity.grants/manifest.toa.yaml +245 -0
- package/components/identity.grants/operations/authorize.d.ts +35 -0
- package/components/identity.grants/operations/authorize.js +54 -0
- package/components/identity.grants/operations/authorize.js.map +1 -0
- package/components/identity.grants/operations/exchange.d.ts +37 -0
- package/components/identity.grants/operations/exchange.js +89 -0
- package/components/identity.grants/operations/exchange.js.map +1 -0
- package/components/identity.grants/operations/lib/code.d.ts +15 -0
- package/components/identity.grants/operations/lib/code.js +30 -0
- package/components/identity.grants/operations/lib/code.js.map +1 -0
- package/components/identity.grants/operations/lib/identify.d.ts +6 -0
- package/components/identity.grants/operations/lib/identify.js +15 -0
- package/components/identity.grants/operations/lib/identify.js.map +1 -0
- package/components/identity.grants/operations/lib/index.d.ts +3 -0
- package/components/identity.grants/operations/lib/index.js +3 -0
- package/components/identity.grants/operations/lib/index.js.map +1 -0
- package/components/identity.grants/operations/list.d.ts +13 -0
- package/components/identity.grants/operations/list.js +18 -0
- package/components/identity.grants/operations/list.js.map +1 -0
- package/components/identity.grants/operations/revoke.d.ts +18 -0
- package/components/identity.grants/operations/revoke.js +25 -0
- package/components/identity.grants/operations/revoke.js.map +1 -0
- package/components/identity.grants/source/authorize.ts +90 -0
- package/components/identity.grants/source/exchange.ts +130 -0
- package/components/identity.grants/source/lib/code.ts +39 -0
- package/components/identity.grants/source/lib/identify.ts +16 -0
- package/components/identity.grants/source/lib/index.ts +3 -0
- package/components/identity.grants/source/list.ts +28 -0
- package/components/identity.grants/source/revoke.ts +38 -0
- package/components/identity.grants/tsconfig.json +10 -0
- package/components/identity.grants/tsconfig.tsbuildinfo +1 -0
- package/components/identity.grants/types/index.d.ts +60 -0
- package/components/identity.grants/types/toa.d.ts +108 -0
- package/components/identity.keys/manifest.toa.yaml +1 -0
- package/components/identity.keys/operations/create.d.ts +1 -1
- package/components/identity.keys/operations/revoke.d.ts +1 -1
- package/components/identity.keys/source/create.ts +1 -1
- package/components/identity.keys/source/revoke.ts +1 -1
- package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.keys/types/index.d.ts +3 -0
- package/components/identity.keys/types/toa.d.ts +47 -0
- package/components/identity.otp/operations/lib/Context.d.ts +4 -2
- package/components/identity.otp/source/lib/Context.ts +4 -2
- package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
- package/components/identity.otp/types/index.d.ts +3 -0
- package/components/identity.otp/types/toa.d.ts +56 -0
- package/components/identity.passkeys/manifest.toa.yaml +3 -0
- package/components/identity.passkeys/operations/authenticate.d.ts +1 -1
- package/components/identity.passkeys/operations/challenge.d.ts +1 -1
- package/components/identity.passkeys/operations/create.d.ts +1 -1
- package/components/identity.passkeys/operations/types/Context.d.ts +2 -1
- package/components/identity.passkeys/operations/use.d.ts +1 -1
- package/components/identity.passkeys/source/authenticate.ts +1 -1
- package/components/identity.passkeys/source/challenge.ts +1 -1
- package/components/identity.passkeys/source/create.ts +1 -1
- package/components/identity.passkeys/source/types/Context.ts +2 -1
- package/components/identity.passkeys/source/use.ts +1 -1
- package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/types/index.d.ts +3 -0
- package/components/identity.passkeys/types/toa.d.ts +120 -0
- package/components/identity.roles/operations/principal.d.ts +1 -1
- package/components/identity.roles/source/principal.ts +1 -1
- package/components/identity.roles/tsconfig.tsbuildinfo +1 -1
- package/components/identity.roles/types/index.d.ts +3 -0
- package/components/identity.roles/types/toa.d.ts +43 -0
- package/components/identity.tokens/manifest.toa.yaml +34 -3
- package/components/identity.tokens/operations/authenticate.d.ts +2 -1
- package/components/identity.tokens/operations/authenticate.js +8 -0
- package/components/identity.tokens/operations/authenticate.js.map +1 -1
- package/components/identity.tokens/operations/decrypt.d.ts +2 -1
- package/components/identity.tokens/operations/decrypt.js +2 -2
- package/components/identity.tokens/operations/decrypt.js.map +1 -1
- package/components/identity.tokens/operations/encrypt.d.ts +2 -1
- package/components/identity.tokens/operations/encrypt.js.map +1 -1
- package/components/identity.tokens/operations/issue.d.ts +2 -1
- package/components/identity.tokens/operations/issue.js +2 -1
- package/components/identity.tokens/operations/issue.js.map +1 -1
- package/components/identity.tokens/operations/lib/form.d.ts +8 -0
- package/components/identity.tokens/operations/lib/form.js +24 -0
- package/components/identity.tokens/operations/lib/form.js.map +1 -0
- package/components/identity.tokens/operations/lib/index.d.ts +2 -1
- package/components/identity.tokens/operations/lib/index.js +1 -1
- package/components/identity.tokens/operations/lib/index.js.map +1 -1
- package/components/identity.tokens/source/authenticate.test.ts +8 -5
- package/components/identity.tokens/source/authenticate.ts +12 -1
- package/components/identity.tokens/source/decrypt.test.ts +1 -1
- package/components/identity.tokens/source/decrypt.ts +4 -3
- package/components/identity.tokens/source/encrypt.test.ts +1 -1
- package/components/identity.tokens/source/encrypt.ts +2 -1
- package/components/identity.tokens/source/issue.test.ts +71 -0
- package/components/identity.tokens/source/issue.ts +4 -2
- package/components/identity.tokens/source/lib/form.test.ts +25 -0
- package/components/identity.tokens/source/lib/form.ts +31 -0
- package/components/identity.tokens/source/lib/index.ts +2 -1
- package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
- package/components/identity.tokens/types/index.d.ts +61 -0
- package/components/identity.tokens/types/toa.d.ts +113 -0
- package/documentation/cache.md +10 -1
- package/documentation/consent.md +101 -0
- package/documentation/identity.md +3 -1
- package/documentation/io.md +36 -2
- package/documentation/oauth.md +179 -0
- package/documentation/protocol.md +4 -0
- package/features/auth.bearer.feature +120 -0
- package/features/cache.feature +46 -0
- package/features/oauth.clients.feature +195 -0
- package/features/oauth.discovery.feature +165 -0
- package/features/oauth.grants.feature +286 -0
- package/features/steps/Gateway.ts +6 -1
- package/features/steps/Parameters.ts +4 -0
- package/features/steps/components/echo/manifest.toa.yaml +2 -0
- package/features/steps/components/octets.tester/manifest.toa.yaml +2 -0
- package/features/steps/components/octets.tester/operations/redirect.js +6 -1
- package/features/steps/components/users/manifest.toa.yaml +1 -0
- package/package.json +8 -6
- package/readme.md +3 -0
- package/schemas/annotation.cos.yaml +30 -0
- package/source/Annotation.ts +23 -0
- package/source/Factory.ts +1 -1
- package/source/HTTP/Server.ts +9 -5
- package/source/HTTP/formats/form.ts +21 -0
- package/source/HTTP/formats/index.ts +17 -2
- package/source/HTTP/messages.test.ts +25 -1
- package/source/HTTP/messages.ts +10 -3
- package/source/Interception.ts +8 -2
- package/source/deployment.ts +4 -1
- package/source/directives/auth/Authorization.ts +46 -19
- package/source/directives/auth/Incept.ts +14 -9
- package/source/directives/auth/schemes.test.ts +8 -4
- package/source/directives/auth/schemes.ts +22 -9
- package/source/directives/auth/types.ts +5 -2
- package/source/directives/cache/Cache.ts +13 -4
- package/source/directives/cache/Control.ts +0 -3
- package/source/directives/flow/Fetch.ts +1 -1
- package/source/directives/index.ts +2 -1
- package/source/directives/io/IO.ts +2 -0
- package/source/directives/io/Output.ts +4 -1
- package/source/directives/io/Status.test.ts +84 -0
- package/source/directives/io/Status.ts +46 -0
- package/source/directives/oauth/Discovery.ts +76 -0
- package/source/directives/oauth/documents.ts +113 -0
- package/source/directives/oauth/index.ts +3 -0
- package/source/directives/octets/Delete.ts +1 -1
- package/source/directives/octets/Get.ts +1 -1
- package/source/directives/octets/Workflow.ts +1 -1
- package/source/directives/octets/workflows/Execution.ts +1 -1
- package/source/index.ts +6 -0
- package/source/octets.d.ts +17 -0
- package/source/userland.ts +47 -0
- package/transpiled/Annotation.d.ts +17 -0
- package/transpiled/Factory.js +1 -1
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/HTTP/Server.d.ts +4 -1
- package/transpiled/HTTP/Server.js +4 -3
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/HTTP/formats/form.d.ts +9 -0
- package/transpiled/HTTP/formats/form.js +18 -0
- package/transpiled/HTTP/formats/form.js.map +1 -0
- package/transpiled/HTTP/formats/index.d.ts +10 -2
- package/transpiled/HTTP/formats/index.js +10 -0
- package/transpiled/HTTP/formats/index.js.map +1 -1
- package/transpiled/HTTP/messages.d.ts +6 -0
- package/transpiled/HTTP/messages.js +3 -3
- package/transpiled/HTTP/messages.js.map +1 -1
- package/transpiled/Interception.d.ts +4 -1
- package/transpiled/Interception.js +4 -2
- package/transpiled/Interception.js.map +1 -1
- package/transpiled/deployment.js +2 -0
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/directives/auth/Authorization.d.ts +1 -1
- package/transpiled/directives/auth/Authorization.js +38 -18
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/auth/Incept.d.ts +1 -1
- package/transpiled/directives/auth/Incept.js +11 -7
- package/transpiled/directives/auth/Incept.js.map +1 -1
- package/transpiled/directives/auth/schemes.d.ts +16 -4
- package/transpiled/directives/auth/schemes.js +20 -8
- package/transpiled/directives/auth/schemes.js.map +1 -1
- package/transpiled/directives/auth/types.d.ts +4 -2
- package/transpiled/directives/cache/Cache.js +9 -2
- package/transpiled/directives/cache/Cache.js.map +1 -1
- package/transpiled/directives/cache/Control.js +0 -2
- package/transpiled/directives/cache/Control.js.map +1 -1
- package/transpiled/directives/index.js +2 -1
- package/transpiled/directives/index.js.map +1 -1
- package/transpiled/directives/io/IO.js +2 -0
- package/transpiled/directives/io/IO.js.map +1 -1
- package/transpiled/directives/io/Output.js +4 -1
- package/transpiled/directives/io/Output.js.map +1 -1
- package/transpiled/directives/io/Status.d.ts +19 -0
- package/transpiled/directives/io/Status.js +34 -0
- package/transpiled/directives/io/Status.js.map +1 -0
- package/transpiled/directives/oauth/Discovery.d.ts +23 -0
- package/transpiled/directives/oauth/Discovery.js +58 -0
- package/transpiled/directives/oauth/Discovery.js.map +1 -0
- package/transpiled/directives/oauth/documents.d.ts +9 -0
- package/transpiled/directives/oauth/documents.js +82 -0
- package/transpiled/directives/oauth/documents.js.map +1 -0
- package/transpiled/directives/oauth/index.d.ts +2 -0
- package/transpiled/directives/oauth/index.js +3 -0
- package/transpiled/directives/oauth/index.js.map +1 -0
- package/transpiled/index.d.ts +2 -0
- package/transpiled/userland.d.ts +39 -0
- package/transpiled/userland.js +7 -0
- package/transpiled/userland.js.map +1 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/components/identity.basic/operations/types.d.ts +0 -61
- package/components/identity.basic/operations/types.js +0 -2
- package/components/identity.basic/operations/types.js.map +0 -1
- package/components/identity.basic/source/types.ts +0 -69
- package/components/identity.tokens/operations/lib/types.d.ts +0 -88
- package/components/identity.tokens/operations/lib/types.js +0 -2
- package/components/identity.tokens/operations/lib/types.js.map +0 -1
- package/components/identity.tokens/source/lib/types.ts +0 -102
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Written by `toa types`. Every run rewrites it.
|
|
2
|
+
// What a manifest does not state belongs in a file of your own.
|
|
3
|
+
|
|
4
|
+
import type { Query, RemoteError } from '@toa.io/core'
|
|
5
|
+
import type { Secret } from '@toa.io/extensions.configuration'
|
|
6
|
+
import type { FetchInit } from '@toa.io/extensions.fetch'
|
|
7
|
+
import type { Logs, Span } from '@toa.io/extensions.telemetry'
|
|
8
|
+
import type { Readable } from 'node:stream'
|
|
9
|
+
|
|
10
|
+
export interface Entity {
|
|
11
|
+
/** The authority token is valid for */
|
|
12
|
+
authority: string
|
|
13
|
+
/** The issuer, or signer, of the token, URI like `https://accounts.google.com` */
|
|
14
|
+
iss: string
|
|
15
|
+
/** The ID that represents the principal making the request */
|
|
16
|
+
sub: string
|
|
17
|
+
/** Identity associated with these credentials */
|
|
18
|
+
identity: string
|
|
19
|
+
id: string
|
|
20
|
+
_version?: number
|
|
21
|
+
_created?: number
|
|
22
|
+
_updated?: number
|
|
23
|
+
_deleted?: number | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type TransitInput = {
|
|
27
|
+
authority: string
|
|
28
|
+
iss: string
|
|
29
|
+
sub: string
|
|
30
|
+
identity: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type CreateInput = {
|
|
34
|
+
scheme: "bearer" | "code"
|
|
35
|
+
authority: string
|
|
36
|
+
credentials: string
|
|
37
|
+
id: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type InceptInput = {
|
|
41
|
+
scheme: "bearer" | "code"
|
|
42
|
+
authority: string
|
|
43
|
+
credentials: string
|
|
44
|
+
id: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type InceptOutput = {
|
|
48
|
+
id?: string
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type AuthenticateInput = {
|
|
52
|
+
scheme: "bearer" | "code"
|
|
53
|
+
authority: string
|
|
54
|
+
credentials: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type AuthenticateOutput = {
|
|
58
|
+
identity?: {
|
|
59
|
+
id: string
|
|
60
|
+
claims: {
|
|
61
|
+
iss: string
|
|
62
|
+
aud?: string
|
|
63
|
+
sub: string
|
|
64
|
+
[key: string]: unknown
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type DecodeInput = string
|
|
70
|
+
|
|
71
|
+
export type DecodeOutput = {
|
|
72
|
+
iss: string
|
|
73
|
+
aud?: string
|
|
74
|
+
sub: string
|
|
75
|
+
[key: string]: unknown
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type ListInput = {
|
|
79
|
+
authority: string
|
|
80
|
+
identity: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type DeleteInput = {
|
|
84
|
+
authority: string
|
|
85
|
+
identity: string
|
|
86
|
+
credential: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface Component {
|
|
90
|
+
transit: (request: { input: TransitInput, query?: Query<Entity>, task?: boolean }) => Promise<Entity>
|
|
91
|
+
create: (request: { input: CreateInput, task?: boolean }) => Promise<unknown | RemoteError<"EXISTS" | "TOKEN" | "TRUST" | "RESPONSE" | "CONFIG" | "NO_TOKEN" | "ISS" | "SUB" | "EXP" | "REPLAY" | "CODE_NOT_ENABLED" | "CODE_SCHEMA">>
|
|
92
|
+
incept: (request: { input: InceptInput, task?: boolean }) => Promise<InceptOutput | RemoteError<"EXISTS" | "TOKEN" | "TRUST" | "RESPONSE" | "CONFIG" | "NO_TOKEN" | "ISS" | "SUB" | "EXP" | "REPLAY" | "CODE_NOT_ENABLED" | "CODE_SCHEMA">>
|
|
93
|
+
authenticate: (request: { input: AuthenticateInput, task?: boolean }) => Promise<AuthenticateOutput | RemoteError<"NOT_FOUND" | "TOKEN" | "TRUST" | "RESPONSE" | "CONFIG" | "NO_TOKEN" | "ISS" | "SUB" | "EXP" | "REPLAY" | "CODE_NOT_ENABLED" | "CODE_SCHEMA">>
|
|
94
|
+
decode: (request: { input: DecodeInput, task?: boolean }) => Promise<DecodeOutput>
|
|
95
|
+
list: (request: { input: ListInput, task?: boolean }) => Promise<unknown>
|
|
96
|
+
delete: (request: { input: DeleteInput, task?: boolean }) => Promise<unknown>
|
|
97
|
+
assign: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity>
|
|
98
|
+
ensure: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity>
|
|
99
|
+
enumerate: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity[]>
|
|
100
|
+
observe: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity | null>
|
|
101
|
+
stream: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Readable>
|
|
102
|
+
terminate: (request: { input?: null, query?: Query<Entity>, task?: boolean }) => Promise<Entity>
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface Configuration {
|
|
106
|
+
trust?: Array<{
|
|
107
|
+
/** Allowed values for a token `iss` field */
|
|
108
|
+
iss: string
|
|
109
|
+
/** Acceptable `aud` value(s) */
|
|
110
|
+
aud: string | string[]
|
|
111
|
+
signature?: {
|
|
112
|
+
iss: string
|
|
113
|
+
kid: string
|
|
114
|
+
/** PKCS8 private key in PEM form */
|
|
115
|
+
key: string
|
|
116
|
+
}
|
|
117
|
+
/** Client secret for the Identity Provider. Required for Authorization Code Flow. */
|
|
118
|
+
secret?: string
|
|
119
|
+
}>
|
|
120
|
+
/** Subject that will be assigned the `system` Role */
|
|
121
|
+
principal?: {
|
|
122
|
+
authority: string
|
|
123
|
+
iss: string
|
|
124
|
+
sub: string
|
|
125
|
+
}
|
|
126
|
+
assert?: boolean
|
|
127
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
namespace: identity
|
|
2
|
+
name: grants
|
|
3
|
+
|
|
4
|
+
# What a user allowed a client to do on their behalf. The authorization code is not here:
|
|
5
|
+
# it is single-use and lives seconds, so it is held in the stash and redeemed atomically.
|
|
6
|
+
entity:
|
|
7
|
+
associated: true
|
|
8
|
+
schema:
|
|
9
|
+
type: object
|
|
10
|
+
properties:
|
|
11
|
+
authority:
|
|
12
|
+
type: string
|
|
13
|
+
identity:
|
|
14
|
+
type: string
|
|
15
|
+
client:
|
|
16
|
+
type: string
|
|
17
|
+
scope:
|
|
18
|
+
type: array
|
|
19
|
+
items:
|
|
20
|
+
type: string
|
|
21
|
+
resource:
|
|
22
|
+
type: array
|
|
23
|
+
items:
|
|
24
|
+
type: string
|
|
25
|
+
kid:
|
|
26
|
+
description: The identity.keys record backing the access token, revoked with the grant
|
|
27
|
+
type: string
|
|
28
|
+
revokedAt:
|
|
29
|
+
type: integer
|
|
30
|
+
required:
|
|
31
|
+
- authority
|
|
32
|
+
- identity
|
|
33
|
+
- client
|
|
34
|
+
index:
|
|
35
|
+
identity:
|
|
36
|
+
authority: asc
|
|
37
|
+
identity: asc
|
|
38
|
+
|
|
39
|
+
operations:
|
|
40
|
+
transit:
|
|
41
|
+
concurrency: retry
|
|
42
|
+
input:
|
|
43
|
+
type: object
|
|
44
|
+
properties:
|
|
45
|
+
authority:
|
|
46
|
+
type: string
|
|
47
|
+
identity:
|
|
48
|
+
type: string
|
|
49
|
+
client:
|
|
50
|
+
type: string
|
|
51
|
+
scope:
|
|
52
|
+
type: array
|
|
53
|
+
items:
|
|
54
|
+
type: string
|
|
55
|
+
resource:
|
|
56
|
+
type: array
|
|
57
|
+
items:
|
|
58
|
+
type: string
|
|
59
|
+
kid:
|
|
60
|
+
type: string
|
|
61
|
+
required:
|
|
62
|
+
- authority
|
|
63
|
+
- identity
|
|
64
|
+
- client
|
|
65
|
+
|
|
66
|
+
authorize:
|
|
67
|
+
query: false
|
|
68
|
+
input:
|
|
69
|
+
type: object
|
|
70
|
+
properties:
|
|
71
|
+
authority:
|
|
72
|
+
type: string
|
|
73
|
+
identity:
|
|
74
|
+
type: string
|
|
75
|
+
client:
|
|
76
|
+
type: string
|
|
77
|
+
maxLength: 2048
|
|
78
|
+
redirect:
|
|
79
|
+
type: string
|
|
80
|
+
maxLength: 2048
|
|
81
|
+
challenge:
|
|
82
|
+
description: PKCE code challenge, S256 of the verifier
|
|
83
|
+
type: string
|
|
84
|
+
minLength: 43
|
|
85
|
+
maxLength: 128
|
|
86
|
+
method:
|
|
87
|
+
type: string
|
|
88
|
+
scope:
|
|
89
|
+
type: array
|
|
90
|
+
items:
|
|
91
|
+
type: string
|
|
92
|
+
resource:
|
|
93
|
+
type: array
|
|
94
|
+
items:
|
|
95
|
+
type: string
|
|
96
|
+
maxLength: 2048
|
|
97
|
+
required:
|
|
98
|
+
- authority
|
|
99
|
+
- identity
|
|
100
|
+
- client
|
|
101
|
+
- redirect
|
|
102
|
+
- challenge
|
|
103
|
+
- method
|
|
104
|
+
output:
|
|
105
|
+
type: object
|
|
106
|
+
properties:
|
|
107
|
+
status:
|
|
108
|
+
description: Read and removed by `io:status`
|
|
109
|
+
type: number
|
|
110
|
+
code:
|
|
111
|
+
type: string
|
|
112
|
+
expires_in:
|
|
113
|
+
type: number
|
|
114
|
+
error:
|
|
115
|
+
type: string
|
|
116
|
+
error_description:
|
|
117
|
+
type: string
|
|
118
|
+
|
|
119
|
+
exchange:
|
|
120
|
+
query: false
|
|
121
|
+
input:
|
|
122
|
+
type: object
|
|
123
|
+
properties:
|
|
124
|
+
authority:
|
|
125
|
+
type: string
|
|
126
|
+
grant_type:
|
|
127
|
+
type: string
|
|
128
|
+
code:
|
|
129
|
+
type: string
|
|
130
|
+
maxLength: 512
|
|
131
|
+
code_verifier:
|
|
132
|
+
type: string
|
|
133
|
+
minLength: 43
|
|
134
|
+
maxLength: 128
|
|
135
|
+
redirect_uri:
|
|
136
|
+
type: string
|
|
137
|
+
maxLength: 2048
|
|
138
|
+
client_id:
|
|
139
|
+
type: string
|
|
140
|
+
maxLength: 2048
|
|
141
|
+
resource:
|
|
142
|
+
description: A form repeats a name to say a list, so either shape arrives
|
|
143
|
+
type: [string, array]
|
|
144
|
+
required:
|
|
145
|
+
- authority
|
|
146
|
+
- grant_type
|
|
147
|
+
output:
|
|
148
|
+
type: object
|
|
149
|
+
properties:
|
|
150
|
+
status:
|
|
151
|
+
type: number
|
|
152
|
+
access_token:
|
|
153
|
+
type: string
|
|
154
|
+
token_type:
|
|
155
|
+
type: string
|
|
156
|
+
expires_in:
|
|
157
|
+
type: number
|
|
158
|
+
scope:
|
|
159
|
+
type: string
|
|
160
|
+
error:
|
|
161
|
+
type: string
|
|
162
|
+
error_description:
|
|
163
|
+
type: string
|
|
164
|
+
|
|
165
|
+
list:
|
|
166
|
+
input:
|
|
167
|
+
type: object
|
|
168
|
+
properties:
|
|
169
|
+
authority:
|
|
170
|
+
type: string
|
|
171
|
+
identity:
|
|
172
|
+
type: string
|
|
173
|
+
required:
|
|
174
|
+
- authority
|
|
175
|
+
- identity
|
|
176
|
+
|
|
177
|
+
revoke:
|
|
178
|
+
output: { type: 'null' }
|
|
179
|
+
concurrency: retry
|
|
180
|
+
input:
|
|
181
|
+
type: object
|
|
182
|
+
properties:
|
|
183
|
+
authority:
|
|
184
|
+
type: string
|
|
185
|
+
identity:
|
|
186
|
+
type: string
|
|
187
|
+
required:
|
|
188
|
+
- authority
|
|
189
|
+
- identity
|
|
190
|
+
errors:
|
|
191
|
+
- NOT_FOUND
|
|
192
|
+
|
|
193
|
+
configuration:
|
|
194
|
+
schema:
|
|
195
|
+
type: object
|
|
196
|
+
properties:
|
|
197
|
+
lifetime:
|
|
198
|
+
description: Seconds an authorization code may be redeemed within
|
|
199
|
+
type: integer
|
|
200
|
+
minimum: 1
|
|
201
|
+
maximum: 600
|
|
202
|
+
default: 60
|
|
203
|
+
token:
|
|
204
|
+
description: Seconds an access token is valid for; 0 is until it is revoked
|
|
205
|
+
type: integer
|
|
206
|
+
minimum: 0
|
|
207
|
+
default: 2592000
|
|
208
|
+
# both have defaults, so the component reads them without checking
|
|
209
|
+
required: [lifetime, token]
|
|
210
|
+
|
|
211
|
+
stash: ~
|
|
212
|
+
|
|
213
|
+
exposition:
|
|
214
|
+
isolated: true
|
|
215
|
+
/:
|
|
216
|
+
POST:
|
|
217
|
+
anonymous: true
|
|
218
|
+
map:authority: authority
|
|
219
|
+
io:input: [grant_type, code, code_verifier, redirect_uri, client_id, resource]
|
|
220
|
+
io:output: true
|
|
221
|
+
io:status: status
|
|
222
|
+
io:throttle:
|
|
223
|
+
key: ip
|
|
224
|
+
requests: 60
|
|
225
|
+
interval: 60
|
|
226
|
+
cache:exact: no-store
|
|
227
|
+
endpoint: exchange
|
|
228
|
+
/:identity:
|
|
229
|
+
auth:id: identity
|
|
230
|
+
map:authority: authority
|
|
231
|
+
map:segments:
|
|
232
|
+
identity: identity
|
|
233
|
+
GET:
|
|
234
|
+
io:output: [id, client, scope, resource, _created]
|
|
235
|
+
endpoint: list
|
|
236
|
+
POST:
|
|
237
|
+
io:input: [client, redirect, challenge, method, scope, resource]
|
|
238
|
+
io:output: [code, expires_in, error, error_description]
|
|
239
|
+
io:status: status
|
|
240
|
+
cache:exact: no-store
|
|
241
|
+
endpoint: authorize
|
|
242
|
+
/:id:
|
|
243
|
+
DELETE:
|
|
244
|
+
io:output: false
|
|
245
|
+
endpoint: revoke
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Maybe } from '@toa.io/core';
|
|
2
|
+
import type { Operation } from '@toa.io/bridges.node';
|
|
3
|
+
import type { Context } from './lib/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* What the consent page calls when a user allows a client. The page holds the user's own
|
|
6
|
+
* credentials, so this is the user's own route; the code it answers with is what the client
|
|
7
|
+
* redeems at the token endpoint.
|
|
8
|
+
*
|
|
9
|
+
* The grant is recorded so a user can see what they allowed and take it back. It is not
|
|
10
|
+
* consulted to skip the prompt — consent is asked every time, which is what keeps a client
|
|
11
|
+
* that changed where it wants a code sent from being waved through on an older one.
|
|
12
|
+
*/
|
|
13
|
+
export declare class Effect implements Operation {
|
|
14
|
+
private context;
|
|
15
|
+
mount(context: Context): void;
|
|
16
|
+
execute(input: Input): Promise<Maybe<Output>>;
|
|
17
|
+
}
|
|
18
|
+
interface Input {
|
|
19
|
+
authority: string;
|
|
20
|
+
identity: string;
|
|
21
|
+
client: string;
|
|
22
|
+
redirect: string;
|
|
23
|
+
challenge: string;
|
|
24
|
+
method: string;
|
|
25
|
+
scope?: string[];
|
|
26
|
+
resource?: string[];
|
|
27
|
+
}
|
|
28
|
+
interface Output {
|
|
29
|
+
status?: number;
|
|
30
|
+
code?: string;
|
|
31
|
+
expires_in?: number;
|
|
32
|
+
error?: string;
|
|
33
|
+
error_description?: string;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { hold, identify } from './lib/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* What the consent page calls when a user allows a client. The page holds the user's own
|
|
4
|
+
* credentials, so this is the user's own route; the code it answers with is what the client
|
|
5
|
+
* redeems at the token endpoint.
|
|
6
|
+
*
|
|
7
|
+
* The grant is recorded so a user can see what they allowed and take it back. It is not
|
|
8
|
+
* consulted to skip the prompt — consent is asked every time, which is what keeps a client
|
|
9
|
+
* that changed where it wants a code sent from being waved through on an older one.
|
|
10
|
+
*/
|
|
11
|
+
export class Effect {
|
|
12
|
+
context;
|
|
13
|
+
mount(context) {
|
|
14
|
+
this.context = context;
|
|
15
|
+
}
|
|
16
|
+
async execute(input) {
|
|
17
|
+
const { authority, identity } = input;
|
|
18
|
+
if (input.method !== S256)
|
|
19
|
+
return invalid('invalid_request', 'Only the S256 code challenge method is supported');
|
|
20
|
+
const client = await this.context.remote.identity.clients.describe({
|
|
21
|
+
input: { authority, id: input.client, redirect: input.redirect }
|
|
22
|
+
});
|
|
23
|
+
if (client instanceof Error)
|
|
24
|
+
return invalid('invalid_client', 'No such client');
|
|
25
|
+
if (client.permitted !== true)
|
|
26
|
+
return invalid('invalid_request', 'The redirect is not one this client may receive a code at');
|
|
27
|
+
const scope = input.scope ?? [];
|
|
28
|
+
// recorded on the grant and carried to the token endpoint, and restricting nothing yet:
|
|
29
|
+
// a token carries the rights of the identity that consented, over every path it may
|
|
30
|
+
// reach. What binds it is a `permissions` argument in `exchange`, see documentation/oauth.md
|
|
31
|
+
const resource = input.resource ?? [];
|
|
32
|
+
const grant = await this.context.local.transit({
|
|
33
|
+
query: { id: identify(authority, identity, input.client) },
|
|
34
|
+
input: { authority, identity, client: input.client, scope, resource }
|
|
35
|
+
});
|
|
36
|
+
if (grant instanceof Error)
|
|
37
|
+
return grant;
|
|
38
|
+
const code = await hold(authority, {
|
|
39
|
+
identity,
|
|
40
|
+
client: input.client,
|
|
41
|
+
redirect: input.redirect,
|
|
42
|
+
challenge: input.challenge,
|
|
43
|
+
scope,
|
|
44
|
+
resource
|
|
45
|
+
}, this.context);
|
|
46
|
+
return { code, expires_in: this.context.configuration.lifetime };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function invalid(error, description) {
|
|
50
|
+
return { status: BAD_REQUEST, error, error_description: description };
|
|
51
|
+
}
|
|
52
|
+
const S256 = 'S256';
|
|
53
|
+
const BAD_REQUEST = 400;
|
|
54
|
+
//# sourceMappingURL=authorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../source/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAK/C;;;;;;;;GAQG;AACH,MAAM,OAAO,MAAM;IACT,OAAO,CAAU;IAElB,KAAK,CAAE,OAAgB;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAEM,KAAK,CAAC,OAAO,CAAE,KAAY;QAChC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;QAErC,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI;YACvB,OAAO,OAAO,CAAC,iBAAiB,EAAE,kDAAkD,CAAC,CAAA;QAEvF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC;YACjE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;SACjE,CAAC,CAAA;QAEF,IAAI,MAAM,YAAY,KAAK;YACzB,OAAO,OAAO,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAA;QAEpD,IAAI,MAAM,CAAC,SAAS,KAAK,IAAI;YAC3B,OAAO,OAAO,CAAC,iBAAiB,EAAE,2DAA2D,CAAC,CAAA;QAEhG,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAA;QAE/B,wFAAwF;QACxF,oFAAoF;QACpF,6FAA6F;QAC7F,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAA;QAErC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;YAC7C,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE;YAC1D,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE;SACtE,CAAC,CAAA;QAEF,IAAI,KAAK,YAAY,KAAK;YACxB,OAAO,KAAK,CAAA;QAEd,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE;YACjC,QAAQ;YACR,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,KAAK;YACL,QAAQ;SACT,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAEhB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAA;IAClE,CAAC;CACF;AAED,SAAS,OAAO,CAAE,KAAa,EAAE,WAAmB;IAClD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAA;AACvE,CAAC;AAED,MAAM,IAAI,GAAG,MAAM,CAAA;AACnB,MAAM,WAAW,GAAG,GAAG,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Maybe } from '@toa.io/core';
|
|
2
|
+
import type { Operation } from '@toa.io/bridges.node';
|
|
3
|
+
import type { Context } from './lib/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* The token endpoint, RFC 6749 §4.1.3. Anonymous, because the clients this server knows are
|
|
6
|
+
* public ones with no secret to authenticate with: what stands in for that is PKCE, since
|
|
7
|
+
* only the client that began the flow holds the verifier the challenge was made from.
|
|
8
|
+
*
|
|
9
|
+
* Every failure here is `invalid_grant` without saying which check failed. A code that was
|
|
10
|
+
* replayed, a verifier that does not match and a redirect that was swapped are all the same
|
|
11
|
+
* answer, and telling them apart would say whether a stolen code was still live.
|
|
12
|
+
*/
|
|
13
|
+
export declare class Effect implements Operation {
|
|
14
|
+
private context;
|
|
15
|
+
mount(context: Context): void;
|
|
16
|
+
execute(input: Input): Promise<Maybe<Output>>;
|
|
17
|
+
private redeemable;
|
|
18
|
+
}
|
|
19
|
+
interface Input {
|
|
20
|
+
authority: string;
|
|
21
|
+
grant_type: string;
|
|
22
|
+
code?: string;
|
|
23
|
+
code_verifier?: string;
|
|
24
|
+
redirect_uri?: string;
|
|
25
|
+
client_id?: string;
|
|
26
|
+
resource?: string | string[];
|
|
27
|
+
}
|
|
28
|
+
interface Output {
|
|
29
|
+
status?: number;
|
|
30
|
+
access_token?: string;
|
|
31
|
+
token_type?: string;
|
|
32
|
+
expires_in?: number;
|
|
33
|
+
scope?: string;
|
|
34
|
+
error?: string;
|
|
35
|
+
error_description?: string;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { identify, spend, verifies } from './lib/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The token endpoint, RFC 6749 §4.1.3. Anonymous, because the clients this server knows are
|
|
4
|
+
* public ones with no secret to authenticate with: what stands in for that is PKCE, since
|
|
5
|
+
* only the client that began the flow holds the verifier the challenge was made from.
|
|
6
|
+
*
|
|
7
|
+
* Every failure here is `invalid_grant` without saying which check failed. A code that was
|
|
8
|
+
* replayed, a verifier that does not match and a redirect that was swapped are all the same
|
|
9
|
+
* answer, and telling them apart would say whether a stolen code was still live.
|
|
10
|
+
*/
|
|
11
|
+
export class Effect {
|
|
12
|
+
context;
|
|
13
|
+
mount(context) {
|
|
14
|
+
this.context = context;
|
|
15
|
+
}
|
|
16
|
+
async execute(input) {
|
|
17
|
+
const { authority } = input;
|
|
18
|
+
if (input.grant_type !== AUTHORIZATION_CODE)
|
|
19
|
+
return invalid('unsupported_grant_type', 'Only the authorization code grant is supported');
|
|
20
|
+
if (input.code === undefined || input.code_verifier === undefined)
|
|
21
|
+
return invalid('invalid_request', 'A code and a code verifier are required');
|
|
22
|
+
// spent first: a wrong verifier burns the code, which is what stops it being guessed at
|
|
23
|
+
const code = await spend(authority, input.code, this.context);
|
|
24
|
+
if (code === null || !this.redeemable(code, input))
|
|
25
|
+
return invalid('invalid_grant', 'The authorization code is not redeemable');
|
|
26
|
+
// `permissions` would bind the token to `code.resource` — `{'/mcp/**': ['*']}` for a
|
|
27
|
+
// resource at `/mcp/`, which `permits()` reads on every request. Left unset: the token
|
|
28
|
+
// carries the rights of the identity that consented, see documentation/oauth.md#audience
|
|
29
|
+
const issued = await this.context.remote.identity.tokens.issue({
|
|
30
|
+
input: {
|
|
31
|
+
authority,
|
|
32
|
+
identity: code.identity,
|
|
33
|
+
label: label(code.client),
|
|
34
|
+
lifetime: this.context.configuration.token,
|
|
35
|
+
...(code.scope.length === 0 ? {} : { scopes: code.scope })
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
// the only refusal `issue` makes is a scope the consenting identity does not hold, and
|
|
39
|
+
// what it says about that is its own; the client is told what it may act on
|
|
40
|
+
if (issued instanceof Error)
|
|
41
|
+
return invalid('invalid_scope', 'The requested scope is not one the identity holds');
|
|
42
|
+
// the key the token was issued under, so revoking the grant revokes the token. The token
|
|
43
|
+
// is already out, so a grant that fails to record it is logged rather than unwound — it
|
|
44
|
+
// costs the user the ability to revoke, and refusing here would cost them the token too
|
|
45
|
+
const grant = await this.context.local.transit({
|
|
46
|
+
query: { id: identify(authority, code.identity, code.client) },
|
|
47
|
+
input: {
|
|
48
|
+
authority,
|
|
49
|
+
identity: code.identity,
|
|
50
|
+
client: code.client,
|
|
51
|
+
scope: code.scope,
|
|
52
|
+
resource: code.resource,
|
|
53
|
+
kid: issued.kid
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
if (grant instanceof Error)
|
|
57
|
+
this.context.logs.error('Grant not recorded for an issued token', { client: code.client, kid: issued.kid });
|
|
58
|
+
return {
|
|
59
|
+
// RFC 6749 §5.1 asks for 200, where a POST would otherwise answer 201
|
|
60
|
+
status: OK,
|
|
61
|
+
access_token: issued.token,
|
|
62
|
+
token_type: 'Bearer',
|
|
63
|
+
...(issued.exp === undefined
|
|
64
|
+
? {}
|
|
65
|
+
: { expires_in: Math.floor((issued.exp - Date.now()) / 1000) }),
|
|
66
|
+
...(code.scope.length === 0 ? {} : { scope: code.scope.join(' ') })
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
redeemable(code, input) {
|
|
70
|
+
if (!verifies(code.challenge, input.code_verifier))
|
|
71
|
+
return false;
|
|
72
|
+
// RFC 6749 §4.1.3: what was sent to `authorize` must be sent again
|
|
73
|
+
if (input.redirect_uri !== undefined && input.redirect_uri !== code.redirect)
|
|
74
|
+
return false;
|
|
75
|
+
return input.client_id === undefined || input.client_id === code.client;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** `identity.tokens` requires one, and it is what a user sees when listing what they issued. */
|
|
79
|
+
function label(client) {
|
|
80
|
+
return client.slice(0, LABEL);
|
|
81
|
+
}
|
|
82
|
+
function invalid(error, description) {
|
|
83
|
+
return { status: BAD_REQUEST, error, error_description: description };
|
|
84
|
+
}
|
|
85
|
+
const AUTHORIZATION_CODE = 'authorization_code';
|
|
86
|
+
const OK = 200;
|
|
87
|
+
const BAD_REQUEST = 400;
|
|
88
|
+
const LABEL = 64;
|
|
89
|
+
//# sourceMappingURL=exchange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exchange.js","sourceRoot":"","sources":["../source/exchange.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Code, Context } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* An authorization code is a bearer credential that lives for seconds. It is held under a
|
|
4
|
+
* hash of itself, so a dump of the store hands out none of them, and redeemed with `GETDEL`,
|
|
5
|
+
* so it is spent once however many requests arrive at once — the property the whole flow
|
|
6
|
+
* rests on, since a replayed code is a stolen grant.
|
|
7
|
+
*/
|
|
8
|
+
export declare function hold(authority: string, code: Code, context: Context): Promise<string>;
|
|
9
|
+
export declare function spend(authority: string, value: string, context: Context): Promise<Code | null>;
|
|
10
|
+
/**
|
|
11
|
+
* PKCE, RFC 7636: the client kept a verifier and sent its hash, and only the client that
|
|
12
|
+
* began the flow can present the verifier now. `S256` alone — `plain` proves nothing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function verifies(challenge: string, verifier: string): boolean;
|
|
15
|
+
export declare function digest(verifier: string): string;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* An authorization code is a bearer credential that lives for seconds. It is held under a
|
|
4
|
+
* hash of itself, so a dump of the store hands out none of them, and redeemed with `GETDEL`,
|
|
5
|
+
* so it is spent once however many requests arrive at once — the property the whole flow
|
|
6
|
+
* rests on, since a replayed code is a stolen grant.
|
|
7
|
+
*/
|
|
8
|
+
export async function hold(authority, code, context) {
|
|
9
|
+
const value = randomBytes(32).toString('base64url');
|
|
10
|
+
await context.stash.set(key(authority, value), JSON.stringify(code), 'EX', context.configuration.lifetime);
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
export async function spend(authority, value, context) {
|
|
14
|
+
const held = await context.stash.getdel(key(authority, value));
|
|
15
|
+
return held === null ? null : JSON.parse(held);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* PKCE, RFC 7636: the client kept a verifier and sent its hash, and only the client that
|
|
19
|
+
* began the flow can present the verifier now. `S256` alone — `plain` proves nothing.
|
|
20
|
+
*/
|
|
21
|
+
export function verifies(challenge, verifier) {
|
|
22
|
+
return digest(verifier) === challenge;
|
|
23
|
+
}
|
|
24
|
+
export function digest(verifier) {
|
|
25
|
+
return createHash('sha256').update(verifier).digest('base64url');
|
|
26
|
+
}
|
|
27
|
+
function key(authority, value) {
|
|
28
|
+
return `oauth:code:${authority}:${createHash('sha256').update(value).digest('hex')}`;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../source/lib/code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAGrD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAE,SAAiB,EAAE,IAAU,EAAE,OAAgB;IACzE,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAEnD,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EACjE,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IAEvC,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAE,SAAiB,EAAE,KAAa,EAAE,OAAgB;IAC7E,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAA;IAE9D,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAS,CAAA;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAE,SAAiB,EAAE,QAAgB;IAC3D,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,MAAM,CAAE,QAAgB;IACtC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;AAClE,CAAC;AAED,SAAS,GAAG,CAAE,SAAiB,EAAE,KAAa;IAC5C,OAAO,cAAc,SAAS,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;AACtF,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A user holds one grant per client, so the record is addressed by the three things that
|
|
3
|
+
* say which: authorizing again replaces what is there rather than leaving another row, and
|
|
4
|
+
* revoking has one thing to revoke.
|
|
5
|
+
*/
|
|
6
|
+
export declare function identify(authority: string, identity: string, client: string): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* A user holds one grant per client, so the record is addressed by the three things that
|
|
4
|
+
* say which: authorizing again replaces what is there rather than leaving another row, and
|
|
5
|
+
* revoking has one thing to revoke.
|
|
6
|
+
*/
|
|
7
|
+
export function identify(authority, identity, client) {
|
|
8
|
+
return createHash('sha256')
|
|
9
|
+
.update(JSON.stringify([authority, identity, client]))
|
|
10
|
+
.digest('hex')
|
|
11
|
+
.slice(0, LENGTH);
|
|
12
|
+
}
|
|
13
|
+
/** What the entity prototype accepts as an id. */
|
|
14
|
+
const LENGTH = 32;
|
|
15
|
+
//# sourceMappingURL=identify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identify.js","sourceRoot":"","sources":["../../source/lib/identify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAE,SAAiB,EAAE,QAAgB,EAAE,MAAc;IAC3E,OAAO,UAAU,CAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;SACrD,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;AACrB,CAAC;AAED,kDAAkD;AAClD,MAAM,MAAM,GAAG,EAAE,CAAA"}
|