@toa.io/extensions.exposition 1.0.0-alpha.250 → 1.0.0-alpha.252

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 (55) hide show
  1. package/components/identity.bans/operations/tsconfig.tsbuildinfo +1 -1
  2. package/components/identity.basic/manifest.toa.yaml +16 -0
  3. package/components/identity.basic/operations/delete.d.ts +7 -0
  4. package/components/identity.basic/operations/delete.js +11 -0
  5. package/components/identity.basic/operations/delete.js.map +1 -0
  6. package/components/identity.basic/operations/info.d.ts +10 -0
  7. package/components/identity.basic/operations/info.js +11 -0
  8. package/components/identity.basic/operations/info.js.map +1 -0
  9. package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
  10. package/components/identity.basic/operations/types.d.ts +2 -1
  11. package/components/identity.basic/source/delete.ts +15 -0
  12. package/components/identity.basic/source/info.ts +19 -0
  13. package/components/identity.basic/source/types.ts +2 -1
  14. package/components/identity.credentials/manifest.toa.yaml +57 -0
  15. package/components/identity.credentials/operations/list.d.ts +51 -0
  16. package/components/identity.credentials/operations/list.js +32 -0
  17. package/components/identity.credentials/operations/list.js.map +1 -0
  18. package/components/identity.credentials/operations/tsconfig.tsbuildinfo +1 -0
  19. package/components/identity.credentials/source/list.test.ts +47 -0
  20. package/components/identity.credentials/source/list.ts +87 -0
  21. package/components/identity.credentials/tsconfig.json +7 -0
  22. package/components/identity.federation/manifest.toa.yaml +35 -1
  23. package/components/identity.federation/operations/authenticate.js +1 -1
  24. package/components/identity.federation/operations/authenticate.js.map +1 -1
  25. package/components/identity.federation/operations/delete.d.ts +8 -0
  26. package/components/identity.federation/operations/delete.js +11 -0
  27. package/components/identity.federation/operations/delete.js.map +1 -0
  28. package/components/identity.federation/operations/incept.js +14 -4
  29. package/components/identity.federation/operations/incept.js.map +1 -1
  30. package/components/identity.federation/operations/list.d.ts +7 -0
  31. package/components/identity.federation/operations/list.js +18 -0
  32. package/components/identity.federation/operations/list.js.map +1 -0
  33. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
  34. package/components/identity.federation/operations/types/context.d.ts +5 -5
  35. package/components/identity.federation/operations/types/entity.d.ts +2 -0
  36. package/components/identity.federation/source/authenticate.ts +1 -1
  37. package/components/identity.federation/source/delete.ts +16 -0
  38. package/components/identity.federation/source/incept.ts +18 -4
  39. package/components/identity.federation/source/list.test.ts +23 -0
  40. package/components/identity.federation/source/list.ts +23 -0
  41. package/components/identity.federation/source/types/context.ts +5 -6
  42. package/components/identity.federation/source/types/entity.ts +2 -0
  43. package/components/identity.keys/operations/tsconfig.tsbuildinfo +1 -1
  44. package/components/identity.otp/operations/tsconfig.tsbuildinfo +1 -1
  45. package/components/identity.passkeys/operations/tsconfig.tsbuildinfo +1 -1
  46. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  47. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  48. package/documentation/components.md +3 -0
  49. package/documentation/credentials.md +85 -0
  50. package/documentation/identity.md +3 -0
  51. package/documentation/passkeys.md +1 -0
  52. package/features/credentials.feature +106 -0
  53. package/features/steps/Database.ts +6 -2
  54. package/package.json +6 -5
  55. package/transpiled/tsconfig.tsbuildinfo +1 -1
@@ -5,6 +5,9 @@ configured in the same
5
5
  way as if they were a part of the Context. Resources exposed by the components
6
6
  are [isolated](tree.md#directives).
7
7
 
8
+ See [Persistent credentials](credentials.md) for the centralized Basic, federation, and passkey
9
+ management API.
10
+
8
11
  ## Basic credentials
9
12
 
10
13
  The `identity.basic` component stores basic credentials.
@@ -0,0 +1,85 @@
1
+ # Persistent credentials
2
+
3
+ Persistent credentials associate an [Identity](identity.md) with a reusable authentication method.
4
+ Exposition stores Basic, OpenID Connect federation, and WebAuthn passkey credentials. Local tokens
5
+ and one-time passwords are not persistent credentials: tokens are stateless and OTP records expire.
6
+
7
+ See [Features](../features/credentials.feature).
8
+
9
+ ## Listing credentials
10
+
11
+ `GET /identity/credentials/:identity/` returns all persistent credentials associated with the
12
+ Identity in the current authority. Access requires credentials of that Identity.
13
+
14
+ ```http
15
+ GET /identity/credentials/fc8e66ddd51d45eea89602c9dd38a542/ HTTP/1.1
16
+ authorization: Token ...
17
+ accept: application/yaml
18
+ ```
19
+
20
+ ```yaml
21
+ basic:
22
+ username: user@example.com
23
+ federation:
24
+ - id: 09200d0bcc2448c5863e3f50ae18af84
25
+ iss: https://accounts.google.com
26
+ _created: 1754899200000
27
+ passkeys:
28
+ - id: 54ed430804324e81b39756ea4708b546
29
+ aid: adce0002-35bc-c60a-648b-0b25f1f05503
30
+ synced: true
31
+ label: Personal iPhone
32
+ _created: 1754899200000
33
+ ```
34
+
35
+ `basic` is `null` when Basic credentials do not exist. `federation` and `passkeys` are always
36
+ arrays. Secret and authentication data, including password hashes, federation subjects, public
37
+ keys, signature counters, and transports, are not returned.
38
+
39
+ The federation issuer identifies the provider. For example, Google uses
40
+ `https://accounts.google.com` and Apple uses `https://appleid.apple.com`. Applications should map
41
+ trusted issuers to their own provider names and presentation.
42
+
43
+ The passkey `aid` is its
44
+ [AAGUID](https://www.w3.org/TR/webauthn-2/#aaguid), which can be resolved against an authenticator
45
+ metadata catalog. `synced` indicates whether the credential was backed up and is therefore likely
46
+ available on more than one device.
47
+
48
+ ## Basic credentials
49
+
50
+ - `POST /identity/basic/:identity/` adds Basic credentials to an Identity.
51
+ - `PATCH /identity/basic/:identity/` changes its username or password.
52
+ - `DELETE /identity/basic/:identity/` removes its Basic credentials.
53
+
54
+ An Identity can have at most one Basic credential in an authority. See
55
+ [Basic credentials configuration](components.md#basic-credentials) for password hashing,
56
+ constraints, and principal configuration.
57
+
58
+ ## Federation credentials
59
+
60
+ - `POST /identity/federation/:identity/` associates credentials from a trusted issuer.
61
+ - `GET /identity/federation/:identity/` lists federation credentials.
62
+ - `DELETE /identity/federation/:identity/:credential/` removes one federation credential.
63
+
64
+ An Identity can have credentials from multiple issuers and can have multiple subjects from the
65
+ same issuer. The `:credential` segment is the `id` returned by either listing resource. Federation
66
+ resources are provider-independent; issuer-specific authorization flows remain the responsibility
67
+ of the application.
68
+
69
+ ## Passkeys
70
+
71
+ Passkey registration consists of a challenge followed by credential creation:
72
+
73
+ - `POST /identity/passkeys/challenges/:identity/` creates a registration challenge.
74
+ - `POST /identity/passkeys/:identity/` verifies the response and stores the passkey.
75
+ - `GET /identity/passkeys/:identity/` lists passkeys.
76
+ - `DELETE /identity/passkeys/:identity/:credential/` removes one passkey.
77
+
78
+ See [Web Authentication](passkeys.md) for standards and feature scenarios.
79
+
80
+ ## Removing the last credential
81
+
82
+ Credential components intentionally do not coordinate with one another. Consequently, credentials
83
+ can be removed without checking whether another persistent credential exists.
84
+ Applications that require a recoverable login must prevent removal of the last usable credential
85
+ in their client workflow.
@@ -6,6 +6,9 @@ identifier** of an individual, organization, application or device.
6
6
  To prove its Identity, the request originator must provide a valid _credentials_ that are associated
7
7
  with that Identity.
8
8
 
9
+ Persistent authentication methods and their management resources are described in
10
+ [Persistent credentials](credentials.md).
11
+
9
12
  Identity is intrinsically linked to credentials, as an Identity is established only when the first
10
13
  set of credentials for that Identity is created.
11
14
  In other words, the creation of credentials marks the inception of an Identity.
@@ -2,3 +2,4 @@
2
2
 
3
3
  - [Standard](https://www.w3.org/TR/webauthn-2/)
4
4
  - [Features](../features/passkeys.feature)
5
+ - [Credential management](credentials.md#passkeys)
@@ -0,0 +1,106 @@
1
+ @security
2
+ Feature: Persistent credentials management
3
+
4
+ Background:
5
+ # developer:secret
6
+ Given the `identity.basic` database contains:
7
+ | _id | authority | username | password |
8
+ | efe3a65ebbee47ed95a73edd911ea328 | nex | developer | $2b$10$ZRSKkgZoGnrcTNA5w5eCcu3pxDzdTduhteVYXcp56AaNcilNkwJ.O |
9
+ And the `identity.federation` database contains:
10
+ | _id | authority | identity | iss | sub | _created |
11
+ | fed3a65ebbee47ed95a73edd911ea328 | nex | efe3a65ebbee47ed95a73edd911ea328 | https://accounts.google.com | google | 1000 |
12
+ | fed3a65ebbee47ed95a73edd911ea329 | nex | efe3a65ebbee47ed95a73edd911ea328 | https://appleid.apple.com | apple | 900 |
13
+ And the `identity.passkeys` database contains:
14
+ | _id | authority | identity | kid | aid | synced | key | counter | label | _created |
15
+ | ace3a65ebbee47ed95a73edd911ea328 | nex | efe3a65ebbee47ed95a73edd911ea328 | key-one | adce0002-35bc-c60a-648b-0b25f1f05503 | true | cHVibGlj | 0 | Personal iPhone | 2000 |
16
+
17
+ Scenario: Listing all persistent credentials
18
+ When the following request is received:
19
+ """
20
+ GET /identity/credentials/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
21
+ host: nex.toa.io
22
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
23
+ accept: application/yaml
24
+ """
25
+ Then the following reply is sent:
26
+ """
27
+ 200 OK
28
+ authorization: Token ${{ token }}
29
+
30
+ basic:
31
+ username: developer
32
+ federation:
33
+ - id: fed3a65ebbee47ed95a73edd911ea328
34
+ iss: https://accounts.google.com
35
+ _created: 1000
36
+ - id: fed3a65ebbee47ed95a73edd911ea329
37
+ iss: https://appleid.apple.com
38
+ _created: 900
39
+ passkeys:
40
+ - id: ace3a65ebbee47ed95a73edd911ea328
41
+ aid: adce0002-35bc-c60a-648b-0b25f1f05503
42
+ synced: true
43
+ label: Personal iPhone
44
+ _created: 2000
45
+ """
46
+
47
+ Scenario: Deleting credentials through their components
48
+ When the following request is received:
49
+ """
50
+ GET /identity/credentials/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
51
+ host: nex.toa.io
52
+ authorization: Basic ZGV2ZWxvcGVyOnNlY3JldA==
53
+ """
54
+ Then the following reply is sent:
55
+ """
56
+ 200 OK
57
+ authorization: Token ${{ token }}
58
+ """
59
+ When the following request is received:
60
+ """
61
+ DELETE /identity/basic/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
62
+ host: nex.toa.io
63
+ authorization: Token ${{ token }}
64
+ """
65
+ Then the following reply is sent:
66
+ """
67
+ 204 No Content
68
+ """
69
+ When the following request is received:
70
+ """
71
+ DELETE /identity/federation/efe3a65ebbee47ed95a73edd911ea328/fed3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
72
+ host: nex.toa.io
73
+ authorization: Token ${{ token }}
74
+ """
75
+ Then the following reply is sent:
76
+ """
77
+ 204 No Content
78
+ """
79
+ When the following request is received:
80
+ """
81
+ DELETE /identity/passkeys/efe3a65ebbee47ed95a73edd911ea328/ace3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
82
+ host: nex.toa.io
83
+ authorization: Token ${{ token }}
84
+ """
85
+ Then the following reply is sent:
86
+ """
87
+ 204 No Content
88
+ """
89
+ When the following request is received:
90
+ """
91
+ GET /identity/credentials/efe3a65ebbee47ed95a73edd911ea328/ HTTP/1.1
92
+ host: nex.toa.io
93
+ authorization: Token ${{ token }}
94
+ accept: application/yaml
95
+ """
96
+ Then the following reply is sent:
97
+ """
98
+ 200 OK
99
+
100
+ basic: null
101
+ federation:
102
+ - id: fed3a65ebbee47ed95a73edd911ea329
103
+ iss: https://appleid.apple.com
104
+ _created: 900
105
+ passkeys: []
106
+ """
@@ -25,7 +25,11 @@ export class Database {
25
25
  ? int
26
26
  : str === 'null'
27
27
  ? null
28
- : str
28
+ : str === 'true'
29
+ ? true
30
+ : str === 'false'
31
+ ? false
32
+ : str
29
33
  }
30
34
 
31
35
  documents.push(document)
@@ -62,4 +66,4 @@ export class Database {
62
66
  }
63
67
  }
64
68
 
65
- type Document = Record<string, string | number | null>
69
+ type Document = Record<string, string | number | boolean | null>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "1.0.0-alpha.250",
3
+ "version": "1.0.0-alpha.252",
4
4
  "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -19,7 +19,7 @@
19
19
  "dependencies": {
20
20
  "@aws-sdk/protocol-http": "3.370.0",
21
21
  "@simplewebauthn/server": "13.1.1",
22
- "@toa.io/core": "1.0.0-alpha.250",
22
+ "@toa.io/core": "1.0.0-alpha.252",
23
23
  "@toa.io/generic": "1.0.0-alpha.225",
24
24
  "@toa.io/schemas": "1.0.0-alpha.246",
25
25
  "bcryptjs": "2.4.3",
@@ -32,17 +32,18 @@
32
32
  "minimatch": "10.2.4",
33
33
  "msgpackr": "1.11.9",
34
34
  "negotiator": "1.0.0",
35
- "openspan": "1.0.0-alpha.250",
35
+ "openspan": "1.0.0-alpha.252",
36
36
  "paseto": "3.1.4"
37
37
  },
38
38
  "scripts": {
39
39
  "test": "jest",
40
- "transpile": "tsc && npm run transpile:bans && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation && npm run transpile:keys && npm run transpile:passkeys && npm run transpile:otp",
40
+ "transpile": "tsc && npm run transpile:bans && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles && npm run transpile:federation && npm run transpile:credentials && npm run transpile:keys && npm run transpile:passkeys && npm run transpile:otp",
41
41
  "transpile:bans": "tsc -p ./components/identity.bans",
42
42
  "transpile:basic": "tsc -p ./components/identity.basic",
43
43
  "transpile:tokens": "tsc -p ./components/identity.tokens",
44
44
  "transpile:roles": "tsc -p ./components/identity.roles",
45
45
  "transpile:federation": "tsc -p ./components/identity.federation",
46
+ "transpile:credentials": "tsc -p ./components/identity.credentials",
46
47
  "transpile:keys": "tsc -p ./components/identity.keys",
47
48
  "transpile:passkeys": "tsc -p ./components/identity.passkeys",
48
49
  "transpile:otp": "tsc -p ./components/identity.otp",
@@ -64,5 +65,5 @@
64
65
  },
65
66
  "testEnvironment": "node"
66
67
  },
67
- "gitHead": "443ed152a60400ad271005fa7cf8afb101390cf0"
68
+ "gitHead": "753e94e44b499be02dee28fa1db2d45c053434cf"
68
69
  }