@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 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Operation } from '@toa.io/bridges.node';
|
|
2
|
+
import type { Context, Entity } from './lib/index.js';
|
|
3
|
+
/** What a user has allowed, for a screen that lets them take it back. */
|
|
4
|
+
export declare class Computation implements Operation {
|
|
5
|
+
private enumerate;
|
|
6
|
+
mount(context: Context): void;
|
|
7
|
+
execute(input: Input): Promise<Entity[]>;
|
|
8
|
+
}
|
|
9
|
+
interface Input {
|
|
10
|
+
authority: string;
|
|
11
|
+
identity: string;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { quote } from '@toa.io/generic';
|
|
2
|
+
/** What a user has allowed, for a screen that lets them take it back. */
|
|
3
|
+
export class Computation {
|
|
4
|
+
enumerate;
|
|
5
|
+
mount(context) {
|
|
6
|
+
this.enumerate = context.local.enumerate;
|
|
7
|
+
}
|
|
8
|
+
async execute(input) {
|
|
9
|
+
return await this.enumerate({
|
|
10
|
+
query: {
|
|
11
|
+
criteria: `authority==${quote(input.authority)};identity==${quote(input.identity)}`,
|
|
12
|
+
limit: LIMIT
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const LIMIT = 256;
|
|
18
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../source/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAIvC,yEAAyE;AACzE,MAAM,OAAO,WAAW;IACd,SAAS,CAAgC;IAE1C,KAAK,CAAE,OAAgB;QAC5B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAA;IAC1C,CAAC;IAEM,KAAK,CAAC,OAAO,CAAE,KAAY;QAChC,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC;YAC1B,KAAK,EAAE;gBACL,QAAQ,EAAE,cAAc,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE;gBACnF,KAAK,EAAE,KAAK;aACb;SACF,CAAC,CAAA;IACJ,CAAC;CACF;AAED,MAAM,KAAK,GAAG,GAAG,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Maybe } from '@toa.io/core';
|
|
2
|
+
import type { Operation } from '@toa.io/bridges.node';
|
|
3
|
+
import type { Context, Entity } from './lib/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Taking back what was allowed. The token itself is not deleted — it is out in the world —
|
|
6
|
+
* so what is revoked is the key it was issued under, which is what `identity.tokens` reads
|
|
7
|
+
* it with. It stops being a token within `identity.tokens.cache.ttl`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Transition implements Operation {
|
|
10
|
+
private keys;
|
|
11
|
+
mount(context: Context): void;
|
|
12
|
+
execute(input: Input, object: Entity): Promise<Maybe<void>>;
|
|
13
|
+
}
|
|
14
|
+
interface Input {
|
|
15
|
+
authority: string;
|
|
16
|
+
identity: string;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Taking back what was allowed. The token itself is not deleted — it is out in the world —
|
|
3
|
+
* so what is revoked is the key it was issued under, which is what `identity.tokens` reads
|
|
4
|
+
* it with. It stops being a token within `identity.tokens.cache.ttl`.
|
|
5
|
+
*/
|
|
6
|
+
export class Transition {
|
|
7
|
+
keys;
|
|
8
|
+
mount(context) {
|
|
9
|
+
this.keys = context.remote.identity.keys;
|
|
10
|
+
}
|
|
11
|
+
async execute(input, object) {
|
|
12
|
+
// the id is a route parameter, so a grant of another identity is asked for by anyone
|
|
13
|
+
// who guesses one; the authority and the identity are what say it is theirs
|
|
14
|
+
if (object._version === 0 || object.authority !== input.authority ||
|
|
15
|
+
object.identity !== input.identity)
|
|
16
|
+
return ERR_NOT_FOUND;
|
|
17
|
+
if (object.kid !== undefined)
|
|
18
|
+
await this.keys.disable({ query: { id: object.kid } });
|
|
19
|
+
object.revokedAt = Date.now();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const ERR_NOT_FOUND = new (class NotFoundError extends Error {
|
|
23
|
+
code = 'NOT_FOUND';
|
|
24
|
+
})();
|
|
25
|
+
//# sourceMappingURL=revoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revoke.js","sourceRoot":"","sources":["../source/revoke.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,OAAO,UAAU;IACb,IAAI,CAAwC;IAE7C,KAAK,CAAE,OAAgB;QAC5B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAA;IAC1C,CAAC;IAEM,KAAK,CAAC,OAAO,CAAE,KAAY,EAAE,MAAc;QAChD,qFAAqF;QACrF,4EAA4E;QAC5E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;YAC/D,MAAM,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;YAClC,OAAO,aAAa,CAAA;QAEtB,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS;YAC1B,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;QAExD,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAC/B,CAAC;CACF;AAED,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,aAAc,SAAQ,KAAK;IAC1C,IAAI,GAAG,WAAW,CAAA;CACnC,CAAC,EAAE,CAAA"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { hold, identify } from './lib/index.js'
|
|
2
|
+
import type { Maybe } from '@toa.io/core'
|
|
3
|
+
import type { Operation } from '@toa.io/bridges.node'
|
|
4
|
+
import type { Context } from './lib/index.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What the consent page calls when a user allows a client. The page holds the user's own
|
|
8
|
+
* credentials, so this is the user's own route; the code it answers with is what the client
|
|
9
|
+
* redeems at the token endpoint.
|
|
10
|
+
*
|
|
11
|
+
* The grant is recorded so a user can see what they allowed and take it back. It is not
|
|
12
|
+
* consulted to skip the prompt — consent is asked every time, which is what keeps a client
|
|
13
|
+
* that changed where it wants a code sent from being waved through on an older one.
|
|
14
|
+
*/
|
|
15
|
+
export class Effect implements Operation {
|
|
16
|
+
private context!: Context
|
|
17
|
+
|
|
18
|
+
public mount (context: Context): void {
|
|
19
|
+
this.context = context
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async execute (input: Input): Promise<Maybe<Output>> {
|
|
23
|
+
const { authority, identity } = input
|
|
24
|
+
|
|
25
|
+
if (input.method !== S256)
|
|
26
|
+
return invalid('invalid_request', 'Only the S256 code challenge method is supported')
|
|
27
|
+
|
|
28
|
+
const client = await this.context.remote.identity.clients.describe({
|
|
29
|
+
input: { authority, id: input.client, redirect: input.redirect }
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (client instanceof Error)
|
|
33
|
+
return invalid('invalid_client', 'No such client')
|
|
34
|
+
|
|
35
|
+
if (client.permitted !== true)
|
|
36
|
+
return invalid('invalid_request', 'The redirect is not one this client may receive a code at')
|
|
37
|
+
|
|
38
|
+
const scope = input.scope ?? []
|
|
39
|
+
|
|
40
|
+
// recorded on the grant and carried to the token endpoint, and restricting nothing yet:
|
|
41
|
+
// a token carries the rights of the identity that consented, over every path it may
|
|
42
|
+
// reach. What binds it is a `permissions` argument in `exchange`, see documentation/oauth.md
|
|
43
|
+
const resource = input.resource ?? []
|
|
44
|
+
|
|
45
|
+
const grant = await this.context.local.transit({
|
|
46
|
+
query: { id: identify(authority, identity, input.client) },
|
|
47
|
+
input: { authority, identity, client: input.client, scope, resource }
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
if (grant instanceof Error)
|
|
51
|
+
return grant
|
|
52
|
+
|
|
53
|
+
const code = await hold(authority, {
|
|
54
|
+
identity,
|
|
55
|
+
client: input.client,
|
|
56
|
+
redirect: input.redirect,
|
|
57
|
+
challenge: input.challenge,
|
|
58
|
+
scope,
|
|
59
|
+
resource
|
|
60
|
+
}, this.context)
|
|
61
|
+
|
|
62
|
+
return { code, expires_in: this.context.configuration.lifetime }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function invalid (error: string, description: string): Output {
|
|
67
|
+
return { status: BAD_REQUEST, error, error_description: description }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const S256 = 'S256'
|
|
71
|
+
const BAD_REQUEST = 400
|
|
72
|
+
|
|
73
|
+
interface Input {
|
|
74
|
+
authority: string
|
|
75
|
+
identity: string
|
|
76
|
+
client: string
|
|
77
|
+
redirect: string
|
|
78
|
+
challenge: string
|
|
79
|
+
method: string
|
|
80
|
+
scope?: string[]
|
|
81
|
+
resource?: string[]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface Output {
|
|
85
|
+
status?: number
|
|
86
|
+
code?: string
|
|
87
|
+
expires_in?: number
|
|
88
|
+
error?: string
|
|
89
|
+
error_description?: string
|
|
90
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { identify, spend, verifies } from './lib/index.js'
|
|
2
|
+
import type { Maybe } from '@toa.io/core'
|
|
3
|
+
import type { Operation } from '@toa.io/bridges.node'
|
|
4
|
+
import type { Code, Context } from './lib/index.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The token endpoint, RFC 6749 §4.1.3. Anonymous, because the clients this server knows are
|
|
8
|
+
* public ones with no secret to authenticate with: what stands in for that is PKCE, since
|
|
9
|
+
* only the client that began the flow holds the verifier the challenge was made from.
|
|
10
|
+
*
|
|
11
|
+
* Every failure here is `invalid_grant` without saying which check failed. A code that was
|
|
12
|
+
* replayed, a verifier that does not match and a redirect that was swapped are all the same
|
|
13
|
+
* answer, and telling them apart would say whether a stolen code was still live.
|
|
14
|
+
*/
|
|
15
|
+
export class Effect implements Operation {
|
|
16
|
+
private context!: Context
|
|
17
|
+
|
|
18
|
+
public mount (context: Context): void {
|
|
19
|
+
this.context = context
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async execute (input: Input): Promise<Maybe<Output>> {
|
|
23
|
+
const { authority } = input
|
|
24
|
+
|
|
25
|
+
if (input.grant_type !== AUTHORIZATION_CODE)
|
|
26
|
+
return invalid('unsupported_grant_type', 'Only the authorization code grant is supported')
|
|
27
|
+
|
|
28
|
+
if (input.code === undefined || input.code_verifier === undefined)
|
|
29
|
+
return invalid('invalid_request', 'A code and a code verifier are required')
|
|
30
|
+
|
|
31
|
+
// spent first: a wrong verifier burns the code, which is what stops it being guessed at
|
|
32
|
+
const code = await spend(authority, input.code, this.context)
|
|
33
|
+
|
|
34
|
+
if (code === null || !this.redeemable(code, input))
|
|
35
|
+
return invalid('invalid_grant', 'The authorization code is not redeemable')
|
|
36
|
+
|
|
37
|
+
// `permissions` would bind the token to `code.resource` — `{'/mcp/**': ['*']}` for a
|
|
38
|
+
// resource at `/mcp/`, which `permits()` reads on every request. Left unset: the token
|
|
39
|
+
// carries the rights of the identity that consented, see documentation/oauth.md#audience
|
|
40
|
+
const issued = await this.context.remote.identity.tokens.issue({
|
|
41
|
+
input: {
|
|
42
|
+
authority,
|
|
43
|
+
identity: code.identity,
|
|
44
|
+
label: label(code.client),
|
|
45
|
+
lifetime: this.context.configuration.token,
|
|
46
|
+
...(code.scope.length === 0 ? {} : { scopes: code.scope })
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// the only refusal `issue` makes is a scope the consenting identity does not hold, and
|
|
51
|
+
// what it says about that is its own; the client is told what it may act on
|
|
52
|
+
if (issued instanceof Error)
|
|
53
|
+
return invalid('invalid_scope', 'The requested scope is not one the identity holds')
|
|
54
|
+
|
|
55
|
+
// the key the token was issued under, so revoking the grant revokes the token. The token
|
|
56
|
+
// is already out, so a grant that fails to record it is logged rather than unwound — it
|
|
57
|
+
// costs the user the ability to revoke, and refusing here would cost them the token too
|
|
58
|
+
const grant = await this.context.local.transit({
|
|
59
|
+
query: { id: identify(authority, code.identity, code.client) },
|
|
60
|
+
input: {
|
|
61
|
+
authority,
|
|
62
|
+
identity: code.identity,
|
|
63
|
+
client: code.client,
|
|
64
|
+
scope: code.scope,
|
|
65
|
+
resource: code.resource,
|
|
66
|
+
kid: issued.kid
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
if (grant instanceof Error)
|
|
71
|
+
this.context.logs.error('Grant not recorded for an issued token',
|
|
72
|
+
{ client: code.client, kid: issued.kid })
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
// RFC 6749 §5.1 asks for 200, where a POST would otherwise answer 201
|
|
76
|
+
status: OK,
|
|
77
|
+
access_token: issued.token,
|
|
78
|
+
token_type: 'Bearer',
|
|
79
|
+
...(issued.exp === undefined
|
|
80
|
+
? {}
|
|
81
|
+
: { expires_in: Math.floor((issued.exp - Date.now()) / 1000) }),
|
|
82
|
+
...(code.scope.length === 0 ? {} : { scope: code.scope.join(' ') })
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private redeemable (code: Code, input: Input): boolean {
|
|
87
|
+
if (!verifies(code.challenge, input.code_verifier!))
|
|
88
|
+
return false
|
|
89
|
+
|
|
90
|
+
// RFC 6749 §4.1.3: what was sent to `authorize` must be sent again
|
|
91
|
+
if (input.redirect_uri !== undefined && input.redirect_uri !== code.redirect)
|
|
92
|
+
return false
|
|
93
|
+
|
|
94
|
+
return input.client_id === undefined || input.client_id === code.client
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** `identity.tokens` requires one, and it is what a user sees when listing what they issued. */
|
|
99
|
+
function label (client: string): string {
|
|
100
|
+
return client.slice(0, LABEL)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function invalid (error: string, description: string): Output {
|
|
104
|
+
return { status: BAD_REQUEST, error, error_description: description }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const AUTHORIZATION_CODE = 'authorization_code'
|
|
108
|
+
const OK = 200
|
|
109
|
+
const BAD_REQUEST = 400
|
|
110
|
+
const LABEL = 64
|
|
111
|
+
|
|
112
|
+
interface Input {
|
|
113
|
+
authority: string
|
|
114
|
+
grant_type: string
|
|
115
|
+
code?: string
|
|
116
|
+
code_verifier?: string
|
|
117
|
+
redirect_uri?: string
|
|
118
|
+
client_id?: string
|
|
119
|
+
resource?: string | string[]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface Output {
|
|
123
|
+
status?: number
|
|
124
|
+
access_token?: string
|
|
125
|
+
token_type?: string
|
|
126
|
+
expires_in?: number
|
|
127
|
+
scope?: string
|
|
128
|
+
error?: string
|
|
129
|
+
error_description?: string
|
|
130
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto'
|
|
2
|
+
import type { Code, Context } from '../../types/index.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* An authorization code is a bearer credential that lives for seconds. It is held under a
|
|
6
|
+
* hash of itself, so a dump of the store hands out none of them, and redeemed with `GETDEL`,
|
|
7
|
+
* so it is spent once however many requests arrive at once — the property the whole flow
|
|
8
|
+
* rests on, since a replayed code is a stolen grant.
|
|
9
|
+
*/
|
|
10
|
+
export async function hold (authority: string, code: Code, context: Context): Promise<string> {
|
|
11
|
+
const value = randomBytes(32).toString('base64url')
|
|
12
|
+
|
|
13
|
+
await context.stash.set(key(authority, value), JSON.stringify(code),
|
|
14
|
+
'EX', context.configuration.lifetime)
|
|
15
|
+
|
|
16
|
+
return value
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function spend (authority: string, value: string, context: Context): Promise<Code | null> {
|
|
20
|
+
const held = await context.stash.getdel(key(authority, value))
|
|
21
|
+
|
|
22
|
+
return held === null ? null : JSON.parse(held) as Code
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* PKCE, RFC 7636: the client kept a verifier and sent its hash, and only the client that
|
|
27
|
+
* began the flow can present the verifier now. `S256` alone — `plain` proves nothing.
|
|
28
|
+
*/
|
|
29
|
+
export function verifies (challenge: string, verifier: string): boolean {
|
|
30
|
+
return digest(verifier) === challenge
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function digest (verifier: string): string {
|
|
34
|
+
return createHash('sha256').update(verifier).digest('base64url')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function key (authority: string, value: string): string {
|
|
38
|
+
return `oauth:code:${authority}:${createHash('sha256').update(value).digest('hex')}`
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A user holds one grant per client, so the record is addressed by the three things that
|
|
5
|
+
* say which: authorizing again replaces what is there rather than leaving another row, and
|
|
6
|
+
* revoking has one thing to revoke.
|
|
7
|
+
*/
|
|
8
|
+
export function identify (authority: string, identity: string, client: string): string {
|
|
9
|
+
return createHash('sha256')
|
|
10
|
+
.update(JSON.stringify([authority, identity, client]))
|
|
11
|
+
.digest('hex')
|
|
12
|
+
.slice(0, LENGTH)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** What the entity prototype accepts as an id. */
|
|
16
|
+
const LENGTH = 32
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { quote } from '@toa.io/generic'
|
|
2
|
+
import type { Operation } from '@toa.io/bridges.node'
|
|
3
|
+
import type { Context, Entity } from './lib/index.js'
|
|
4
|
+
|
|
5
|
+
/** What a user has allowed, for a screen that lets them take it back. */
|
|
6
|
+
export class Computation implements Operation {
|
|
7
|
+
private enumerate!: Context['local']['enumerate']
|
|
8
|
+
|
|
9
|
+
public mount (context: Context): void {
|
|
10
|
+
this.enumerate = context.local.enumerate
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public async execute (input: Input): Promise<Entity[]> {
|
|
14
|
+
return await this.enumerate({
|
|
15
|
+
query: {
|
|
16
|
+
criteria: `authority==${quote(input.authority)};identity==${quote(input.identity)}`,
|
|
17
|
+
limit: LIMIT
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const LIMIT = 256
|
|
24
|
+
|
|
25
|
+
interface Input {
|
|
26
|
+
authority: string
|
|
27
|
+
identity: string
|
|
28
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Maybe } from '@toa.io/core'
|
|
2
|
+
import type { Operation } from '@toa.io/bridges.node'
|
|
3
|
+
import type { Context, Entity } from './lib/index.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Taking back what was allowed. The token itself is not deleted — it is out in the world —
|
|
7
|
+
* so what is revoked is the key it was issued under, which is what `identity.tokens` reads
|
|
8
|
+
* it with. It stops being a token within `identity.tokens.cache.ttl`.
|
|
9
|
+
*/
|
|
10
|
+
export class Transition implements Operation {
|
|
11
|
+
private keys!: Context['remote']['identity']['keys']
|
|
12
|
+
|
|
13
|
+
public mount (context: Context): void {
|
|
14
|
+
this.keys = context.remote.identity.keys
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public async execute (input: Input, object: Entity): Promise<Maybe<void>> {
|
|
18
|
+
// the id is a route parameter, so a grant of another identity is asked for by anyone
|
|
19
|
+
// who guesses one; the authority and the identity are what say it is theirs
|
|
20
|
+
if (object._version === 0 || object.authority !== input.authority ||
|
|
21
|
+
object.identity !== input.identity)
|
|
22
|
+
return ERR_NOT_FOUND
|
|
23
|
+
|
|
24
|
+
if (object.kid !== undefined)
|
|
25
|
+
await this.keys.disable({ query: { id: object.kid } })
|
|
26
|
+
|
|
27
|
+
object.revokedAt = Date.now()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ERR_NOT_FOUND = new (class NotFoundError extends Error {
|
|
32
|
+
public readonly code = 'NOT_FOUND'
|
|
33
|
+
})()
|
|
34
|
+
|
|
35
|
+
interface Input {
|
|
36
|
+
authority: string
|
|
37
|
+
identity: string
|
|
38
|
+
}
|