@spinajs/authn-keys 0.0.0-stage → 3.0.0
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/README.md +165 -2
- package/lib/cjs/bootstrap.d.ts +6 -0
- package/lib/cjs/bootstrap.d.ts.map +1 -0
- package/lib/cjs/bootstrap.js +23 -0
- package/lib/cjs/bootstrap.js.map +1 -0
- package/lib/cjs/challenge/challenge.d.ts +27 -0
- package/lib/cjs/challenge/challenge.d.ts.map +1 -0
- package/lib/cjs/challenge/challenge.js +56 -0
- package/lib/cjs/challenge/challenge.js.map +1 -0
- package/lib/cjs/challenge/index.d.ts +2 -0
- package/lib/cjs/challenge/index.d.ts.map +1 -0
- package/lib/cjs/challenge/index.js +18 -0
- package/lib/cjs/challenge/index.js.map +1 -0
- package/lib/cjs/cli/AddKey.d.ts +18 -0
- package/lib/cjs/cli/AddKey.d.ts.map +1 -0
- package/lib/cjs/cli/AddKey.js +65 -0
- package/lib/cjs/cli/AddKey.js.map +1 -0
- package/lib/cjs/cli/ListKeys.d.ts +11 -0
- package/lib/cjs/cli/ListKeys.d.ts.map +1 -0
- package/lib/cjs/cli/ListKeys.js +48 -0
- package/lib/cjs/cli/ListKeys.js.map +1 -0
- package/lib/cjs/cli/RevokeKey.d.ts +9 -0
- package/lib/cjs/cli/RevokeKey.d.ts.map +1 -0
- package/lib/cjs/cli/RevokeKey.js +42 -0
- package/lib/cjs/cli/RevokeKey.js.map +1 -0
- package/lib/cjs/cli/common.d.ts +9 -0
- package/lib/cjs/cli/common.d.ts.map +1 -0
- package/lib/cjs/cli/common.js +35 -0
- package/lib/cjs/cli/common.js.map +1 -0
- package/lib/cjs/cli/index.d.ts +5 -0
- package/lib/cjs/cli/index.d.ts.map +1 -0
- package/lib/cjs/cli/index.js +21 -0
- package/lib/cjs/cli/index.js.map +1 -0
- package/lib/cjs/config/authn-keys.d.ts +9 -0
- package/lib/cjs/config/authn-keys.d.ts.map +1 -0
- package/lib/cjs/config/authn-keys.js +23 -0
- package/lib/cjs/config/authn-keys.js.map +1 -0
- package/lib/cjs/httpsig/base.d.ts +8 -0
- package/lib/cjs/httpsig/base.d.ts.map +1 -0
- package/lib/cjs/httpsig/base.js +73 -0
- package/lib/cjs/httpsig/base.js.map +1 -0
- package/lib/cjs/httpsig/fields.d.ts +17 -0
- package/lib/cjs/httpsig/fields.d.ts.map +1 -0
- package/lib/cjs/httpsig/fields.js +137 -0
- package/lib/cjs/httpsig/fields.js.map +1 -0
- package/lib/cjs/httpsig/index.d.ts +3 -0
- package/lib/cjs/httpsig/index.d.ts.map +1 -0
- package/lib/cjs/httpsig/index.js +19 -0
- package/lib/cjs/httpsig/index.js.map +1 -0
- package/lib/cjs/index.d.ts +13 -0
- package/lib/cjs/index.d.ts.map +1 -0
- package/lib/cjs/index.js +29 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/jwk/base64url.d.ts +7 -0
- package/lib/cjs/jwk/base64url.d.ts.map +1 -0
- package/lib/cjs/jwk/base64url.js +22 -0
- package/lib/cjs/jwk/base64url.js.map +1 -0
- package/lib/cjs/jwk/ed25519.d.ts +9 -0
- package/lib/cjs/jwk/ed25519.d.ts.map +1 -0
- package/lib/cjs/jwk/ed25519.js +47 -0
- package/lib/cjs/jwk/ed25519.js.map +1 -0
- package/lib/cjs/jwk/index.d.ts +5 -0
- package/lib/cjs/jwk/index.d.ts.map +1 -0
- package/lib/cjs/jwk/index.js +21 -0
- package/lib/cjs/jwk/index.js.map +1 -0
- package/lib/cjs/jwk/keys.d.ts +18 -0
- package/lib/cjs/jwk/keys.d.ts.map +1 -0
- package/lib/cjs/jwk/keys.js +102 -0
- package/lib/cjs/jwk/keys.js.map +1 -0
- package/lib/cjs/jwk/signature.d.ts +11 -0
- package/lib/cjs/jwk/signature.d.ts.map +1 -0
- package/lib/cjs/jwk/signature.js +45 -0
- package/lib/cjs/jwk/signature.js.map +1 -0
- package/lib/cjs/jws/index.d.ts +4 -0
- package/lib/cjs/jws/index.d.ts.map +1 -0
- package/lib/cjs/jws/index.js +20 -0
- package/lib/cjs/jws/index.js.map +1 -0
- package/lib/cjs/jws/json.d.ts +11 -0
- package/lib/cjs/jws/json.d.ts.map +1 -0
- package/lib/cjs/jws/json.js +76 -0
- package/lib/cjs/jws/json.js.map +1 -0
- package/lib/cjs/jws/signer.d.ts +25 -0
- package/lib/cjs/jws/signer.d.ts.map +1 -0
- package/lib/cjs/jws/signer.js +83 -0
- package/lib/cjs/jws/signer.js.map +1 -0
- package/lib/cjs/jws/verifier.d.ts +10 -0
- package/lib/cjs/jws/verifier.d.ts.map +1 -0
- package/lib/cjs/jws/verifier.js +86 -0
- package/lib/cjs/jws/verifier.js.map +1 -0
- package/lib/cjs/migrations/AuthnKeys_2026_10_02_00_00_00.d.ts +7 -0
- package/lib/cjs/migrations/AuthnKeys_2026_10_02_00_00_00.d.ts.map +1 -0
- package/lib/cjs/migrations/AuthnKeys_2026_10_02_00_00_00.js +40 -0
- package/lib/cjs/migrations/AuthnKeys_2026_10_02_00_00_00.js.map +1 -0
- package/lib/cjs/migrations/index.d.ts +2 -0
- package/lib/cjs/migrations/index.d.ts.map +1 -0
- package/lib/cjs/migrations/index.js +18 -0
- package/lib/cjs/migrations/index.js.map +1 -0
- package/lib/cjs/models/AuthnKey.d.ts +22 -0
- package/lib/cjs/models/AuthnKey.d.ts.map +1 -0
- package/lib/cjs/models/AuthnKey.js +43 -0
- package/lib/cjs/models/AuthnKey.js.map +1 -0
- package/lib/cjs/models/AuthnNonce.d.ts +8 -0
- package/lib/cjs/models/AuthnNonce.d.ts.map +1 -0
- package/lib/cjs/models/AuthnNonce.js +31 -0
- package/lib/cjs/models/AuthnNonce.js.map +1 -0
- package/lib/cjs/models/index.d.ts +3 -0
- package/lib/cjs/models/index.d.ts.map +1 -0
- package/lib/cjs/models/index.js +19 -0
- package/lib/cjs/models/index.js.map +1 -0
- package/lib/cjs/nonce/index.d.ts +2 -0
- package/lib/cjs/nonce/index.d.ts.map +1 -0
- package/lib/cjs/nonce/index.js +18 -0
- package/lib/cjs/nonce/index.js.map +1 -0
- package/lib/cjs/nonce/store.d.ts +22 -0
- package/lib/cjs/nonce/store.d.ts.map +1 -0
- package/lib/cjs/nonce/store.js +98 -0
- package/lib/cjs/nonce/store.js.map +1 -0
- package/lib/cjs/options/index.d.ts +2 -0
- package/lib/cjs/options/index.d.ts.map +1 -0
- package/lib/cjs/options/index.js +18 -0
- package/lib/cjs/options/index.js.map +1 -0
- package/lib/cjs/options/options.d.ts +47 -0
- package/lib/cjs/options/options.d.ts.map +1 -0
- package/lib/cjs/options/options.js +42 -0
- package/lib/cjs/options/options.js.map +1 -0
- package/lib/cjs/package.json +1 -0
- package/lib/cjs/register-schema.d.ts +2 -0
- package/lib/cjs/register-schema.d.ts.map +1 -0
- package/lib/cjs/register-schema.js +9 -0
- package/lib/cjs/register-schema.js.map +1 -0
- package/lib/cjs/registry/index.d.ts +4 -0
- package/lib/cjs/registry/index.d.ts.map +1 -0
- package/lib/cjs/registry/index.js +20 -0
- package/lib/cjs/registry/index.js.map +1 -0
- package/lib/cjs/registry/orm-store.d.ts +15 -0
- package/lib/cjs/registry/orm-store.d.ts.map +1 -0
- package/lib/cjs/registry/orm-store.js +109 -0
- package/lib/cjs/registry/orm-store.js.map +1 -0
- package/lib/cjs/registry/registry.d.ts +54 -0
- package/lib/cjs/registry/registry.d.ts.map +1 -0
- package/lib/cjs/registry/registry.js +244 -0
- package/lib/cjs/registry/registry.js.map +1 -0
- package/lib/cjs/registry/store.d.ts +25 -0
- package/lib/cjs/registry/store.d.ts.map +1 -0
- package/lib/cjs/registry/store.js +66 -0
- package/lib/cjs/registry/store.js.map +1 -0
- package/lib/cjs/schema.d.ts +88 -0
- package/lib/cjs/schema.d.ts.map +1 -0
- package/lib/cjs/schema.js +41 -0
- package/lib/cjs/schema.js.map +1 -0
- package/lib/cjs/strategies/assertion.d.ts +29 -0
- package/lib/cjs/strategies/assertion.d.ts.map +1 -0
- package/lib/cjs/strategies/assertion.js +118 -0
- package/lib/cjs/strategies/assertion.js.map +1 -0
- package/lib/cjs/strategies/challenge.d.ts +25 -0
- package/lib/cjs/strategies/challenge.d.ts.map +1 -0
- package/lib/cjs/strategies/challenge.js +81 -0
- package/lib/cjs/strategies/challenge.js.map +1 -0
- package/lib/cjs/strategies/httpsig.d.ts +34 -0
- package/lib/cjs/strategies/httpsig.d.ts.map +1 -0
- package/lib/cjs/strategies/httpsig.js +127 -0
- package/lib/cjs/strategies/httpsig.js.map +1 -0
- package/lib/cjs/strategies/index.d.ts +5 -0
- package/lib/cjs/strategies/index.d.ts.map +1 -0
- package/lib/cjs/strategies/index.js +21 -0
- package/lib/cjs/strategies/index.js.map +1 -0
- package/lib/cjs/strategies/keys.d.ts +43 -0
- package/lib/cjs/strategies/keys.d.ts.map +1 -0
- package/lib/cjs/strategies/keys.js +93 -0
- package/lib/cjs/strategies/keys.js.map +1 -0
- package/lib/mjs/bootstrap.d.ts +6 -0
- package/lib/mjs/bootstrap.d.ts.map +1 -0
- package/lib/mjs/bootstrap.js +20 -0
- package/lib/mjs/bootstrap.js.map +1 -0
- package/lib/mjs/challenge/challenge.d.ts +27 -0
- package/lib/mjs/challenge/challenge.d.ts.map +1 -0
- package/lib/mjs/challenge/challenge.js +45 -0
- package/lib/mjs/challenge/challenge.js.map +1 -0
- package/lib/mjs/challenge/index.d.ts +2 -0
- package/lib/mjs/challenge/index.d.ts.map +1 -0
- package/lib/mjs/challenge/index.js +2 -0
- package/lib/mjs/challenge/index.js.map +1 -0
- package/lib/mjs/cli/AddKey.d.ts +18 -0
- package/lib/mjs/cli/AddKey.d.ts.map +1 -0
- package/lib/mjs/cli/AddKey.js +62 -0
- package/lib/mjs/cli/AddKey.js.map +1 -0
- package/lib/mjs/cli/ListKeys.d.ts +11 -0
- package/lib/mjs/cli/ListKeys.d.ts.map +1 -0
- package/lib/mjs/cli/ListKeys.js +44 -0
- package/lib/mjs/cli/ListKeys.js.map +1 -0
- package/lib/mjs/cli/RevokeKey.d.ts +9 -0
- package/lib/mjs/cli/RevokeKey.d.ts.map +1 -0
- package/lib/mjs/cli/RevokeKey.js +39 -0
- package/lib/mjs/cli/RevokeKey.js.map +1 -0
- package/lib/mjs/cli/common.d.ts +9 -0
- package/lib/mjs/cli/common.d.ts.map +1 -0
- package/lib/mjs/cli/common.js +30 -0
- package/lib/mjs/cli/common.js.map +1 -0
- package/lib/mjs/cli/index.d.ts +5 -0
- package/lib/mjs/cli/index.d.ts.map +1 -0
- package/lib/mjs/cli/index.js +5 -0
- package/lib/mjs/cli/index.js.map +1 -0
- package/lib/mjs/config/authn-keys.d.ts +9 -0
- package/lib/mjs/config/authn-keys.d.ts.map +1 -0
- package/lib/mjs/config/authn-keys.js +21 -0
- package/lib/mjs/config/authn-keys.js.map +1 -0
- package/lib/mjs/httpsig/base.d.ts +8 -0
- package/lib/mjs/httpsig/base.d.ts.map +1 -0
- package/lib/mjs/httpsig/base.js +67 -0
- package/lib/mjs/httpsig/base.js.map +1 -0
- package/lib/mjs/httpsig/fields.d.ts +17 -0
- package/lib/mjs/httpsig/fields.d.ts.map +1 -0
- package/lib/mjs/httpsig/fields.js +133 -0
- package/lib/mjs/httpsig/fields.js.map +1 -0
- package/lib/mjs/httpsig/index.d.ts +3 -0
- package/lib/mjs/httpsig/index.d.ts.map +1 -0
- package/lib/mjs/httpsig/index.js +3 -0
- package/lib/mjs/httpsig/index.js.map +1 -0
- package/lib/mjs/index.d.ts +13 -0
- package/lib/mjs/index.d.ts.map +1 -0
- package/lib/mjs/index.js +13 -0
- package/lib/mjs/index.js.map +1 -0
- package/lib/mjs/jwk/base64url.d.ts +7 -0
- package/lib/mjs/jwk/base64url.d.ts.map +1 -0
- package/lib/mjs/jwk/base64url.js +18 -0
- package/lib/mjs/jwk/base64url.js.map +1 -0
- package/lib/mjs/jwk/ed25519.d.ts +9 -0
- package/lib/mjs/jwk/ed25519.d.ts.map +1 -0
- package/lib/mjs/jwk/ed25519.js +42 -0
- package/lib/mjs/jwk/ed25519.js.map +1 -0
- package/lib/mjs/jwk/index.d.ts +5 -0
- package/lib/mjs/jwk/index.d.ts.map +1 -0
- package/lib/mjs/jwk/index.js +5 -0
- package/lib/mjs/jwk/index.js.map +1 -0
- package/lib/mjs/jwk/keys.d.ts +18 -0
- package/lib/mjs/jwk/keys.d.ts.map +1 -0
- package/lib/mjs/jwk/keys.js +95 -0
- package/lib/mjs/jwk/keys.js.map +1 -0
- package/lib/mjs/jwk/signature.d.ts +11 -0
- package/lib/mjs/jwk/signature.d.ts.map +1 -0
- package/lib/mjs/jwk/signature.js +39 -0
- package/lib/mjs/jwk/signature.js.map +1 -0
- package/lib/mjs/jws/index.d.ts +4 -0
- package/lib/mjs/jws/index.d.ts.map +1 -0
- package/lib/mjs/jws/index.js +4 -0
- package/lib/mjs/jws/index.js.map +1 -0
- package/lib/mjs/jws/json.d.ts +11 -0
- package/lib/mjs/jws/json.d.ts.map +1 -0
- package/lib/mjs/jws/json.js +72 -0
- package/lib/mjs/jws/json.js.map +1 -0
- package/lib/mjs/jws/signer.d.ts +25 -0
- package/lib/mjs/jws/signer.d.ts.map +1 -0
- package/lib/mjs/jws/signer.js +79 -0
- package/lib/mjs/jws/signer.js.map +1 -0
- package/lib/mjs/jws/verifier.d.ts +10 -0
- package/lib/mjs/jws/verifier.d.ts.map +1 -0
- package/lib/mjs/jws/verifier.js +83 -0
- package/lib/mjs/jws/verifier.js.map +1 -0
- package/lib/mjs/migrations/AuthnKeys_2026_10_02_00_00_00.d.ts +7 -0
- package/lib/mjs/migrations/AuthnKeys_2026_10_02_00_00_00.d.ts.map +1 -0
- package/lib/mjs/migrations/AuthnKeys_2026_10_02_00_00_00.js +37 -0
- package/lib/mjs/migrations/AuthnKeys_2026_10_02_00_00_00.js.map +1 -0
- package/lib/mjs/migrations/index.d.ts +2 -0
- package/lib/mjs/migrations/index.d.ts.map +1 -0
- package/lib/mjs/migrations/index.js +2 -0
- package/lib/mjs/migrations/index.js.map +1 -0
- package/lib/mjs/models/AuthnKey.d.ts +22 -0
- package/lib/mjs/models/AuthnKey.d.ts.map +1 -0
- package/lib/mjs/models/AuthnKey.js +40 -0
- package/lib/mjs/models/AuthnKey.js.map +1 -0
- package/lib/mjs/models/AuthnNonce.d.ts +8 -0
- package/lib/mjs/models/AuthnNonce.d.ts.map +1 -0
- package/lib/mjs/models/AuthnNonce.js +28 -0
- package/lib/mjs/models/AuthnNonce.js.map +1 -0
- package/lib/mjs/models/index.d.ts +3 -0
- package/lib/mjs/models/index.d.ts.map +1 -0
- package/lib/mjs/models/index.js +3 -0
- package/lib/mjs/models/index.js.map +1 -0
- package/lib/mjs/nonce/index.d.ts +2 -0
- package/lib/mjs/nonce/index.d.ts.map +1 -0
- package/lib/mjs/nonce/index.js +2 -0
- package/lib/mjs/nonce/index.js.map +1 -0
- package/lib/mjs/nonce/store.d.ts +22 -0
- package/lib/mjs/nonce/store.d.ts.map +1 -0
- package/lib/mjs/nonce/store.js +92 -0
- package/lib/mjs/nonce/store.js.map +1 -0
- package/lib/mjs/options/index.d.ts +2 -0
- package/lib/mjs/options/index.d.ts.map +1 -0
- package/lib/mjs/options/index.js +2 -0
- package/lib/mjs/options/index.js.map +1 -0
- package/lib/mjs/options/options.d.ts +47 -0
- package/lib/mjs/options/options.d.ts.map +1 -0
- package/lib/mjs/options/options.js +39 -0
- package/lib/mjs/options/options.js.map +1 -0
- package/lib/mjs/package.json +1 -0
- package/lib/mjs/register-schema.d.ts +2 -0
- package/lib/mjs/register-schema.d.ts.map +1 -0
- package/lib/mjs/register-schema.js +7 -0
- package/lib/mjs/register-schema.js.map +1 -0
- package/lib/mjs/registry/index.d.ts +4 -0
- package/lib/mjs/registry/index.d.ts.map +1 -0
- package/lib/mjs/registry/index.js +4 -0
- package/lib/mjs/registry/index.js.map +1 -0
- package/lib/mjs/registry/orm-store.d.ts +15 -0
- package/lib/mjs/registry/orm-store.d.ts.map +1 -0
- package/lib/mjs/registry/orm-store.js +106 -0
- package/lib/mjs/registry/orm-store.js.map +1 -0
- package/lib/mjs/registry/registry.d.ts +54 -0
- package/lib/mjs/registry/registry.d.ts.map +1 -0
- package/lib/mjs/registry/registry.js +239 -0
- package/lib/mjs/registry/registry.js.map +1 -0
- package/lib/mjs/registry/store.d.ts +25 -0
- package/lib/mjs/registry/store.d.ts.map +1 -0
- package/lib/mjs/registry/store.js +57 -0
- package/lib/mjs/registry/store.js.map +1 -0
- package/lib/mjs/schema.d.ts +88 -0
- package/lib/mjs/schema.d.ts.map +1 -0
- package/lib/mjs/schema.js +38 -0
- package/lib/mjs/schema.js.map +1 -0
- package/lib/mjs/strategies/assertion.d.ts +29 -0
- package/lib/mjs/strategies/assertion.d.ts.map +1 -0
- package/lib/mjs/strategies/assertion.js +115 -0
- package/lib/mjs/strategies/assertion.js.map +1 -0
- package/lib/mjs/strategies/challenge.d.ts +25 -0
- package/lib/mjs/strategies/challenge.d.ts.map +1 -0
- package/lib/mjs/strategies/challenge.js +78 -0
- package/lib/mjs/strategies/challenge.js.map +1 -0
- package/lib/mjs/strategies/httpsig.d.ts +34 -0
- package/lib/mjs/strategies/httpsig.d.ts.map +1 -0
- package/lib/mjs/strategies/httpsig.js +124 -0
- package/lib/mjs/strategies/httpsig.js.map +1 -0
- package/lib/mjs/strategies/index.d.ts +5 -0
- package/lib/mjs/strategies/index.d.ts.map +1 -0
- package/lib/mjs/strategies/index.js +5 -0
- package/lib/mjs/strategies/index.js.map +1 -0
- package/lib/mjs/strategies/keys.d.ts +43 -0
- package/lib/mjs/strategies/keys.d.ts.map +1 -0
- package/lib/mjs/strategies/keys.js +85 -0
- package/lib/mjs/strategies/keys.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -0
- package/lib/tsconfig.mjs.tsbuildinfo +1 -0
- package/package.json +79 -4
package/README.md
CHANGED
|
@@ -1,3 +1,166 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@spinajs/authn-keys`
|
|
2
|
+
|
|
3
|
+
Public-key authentication for SpinaJS: a key registry, strict JWS (the
|
|
4
|
+
canvas C8 rules), JWS client assertions, RFC 9421 HTTP signatures and a
|
|
5
|
+
challenge-response login.
|
|
6
|
+
|
|
7
|
+
## Keys
|
|
8
|
+
|
|
9
|
+
- Ed25519 (`{ "kty": "OKP", "crv": "Ed25519", "x" }`, RFC 8037) and P-256
|
|
10
|
+
(`{ "kty": "EC", "crv": "P-256", "x", "y" }`) public JWKs only. The
|
|
11
|
+
algorithm comes from the key type (`EdDSA`, `ES256`), never from a token.
|
|
12
|
+
- Refused: private parts (`d`), other key types and curves, values that are
|
|
13
|
+
not strict unpadded base64url of 32 bytes, Ed25519 points that are not
|
|
14
|
+
canonical or have small order, P-256 points off the curve.
|
|
15
|
+
- `kid`: 1–64 of `[A-Za-z0-9._-]`; default: the RFC 7638 thumbprint.
|
|
16
|
+
- Owners: a user, a service or an agent. A user's key needs a proof of
|
|
17
|
+
possession (below); services and agents are registered by an admin route
|
|
18
|
+
or the CLI.
|
|
19
|
+
- Table `authn_key` (migration `AuthnKeys_2026_10_02_00_00_00`).
|
|
20
|
+
|
|
21
|
+
`DefaultKeyRegistry` implements `KeyRegistry` over a `KeyStore`
|
|
22
|
+
(`OrmKeyStore` by default, `MemoryKeyStore` for tests) and a `SecretStore`.
|
|
23
|
+
The registry and the strategies get their dependencies through DI. Tests
|
|
24
|
+
build them with `new`, passing the dependencies in one object, e.g.
|
|
25
|
+
`new DefaultKeyRegistry({ store, secrets, throttle, options })` or
|
|
26
|
+
`new HttpSignatureStrategy({ registry, nonces, options })`. `OrmKeyStore`
|
|
27
|
+
and `DbKeyNonceStore` inject the `Orm`, so the `authn:key-*` commands bring
|
|
28
|
+
the database up on their own.
|
|
29
|
+
|
|
30
|
+
## Challenges
|
|
31
|
+
|
|
32
|
+
`KeyRegistry.challenge` answers `{ id, nonce, expiresAt }`. The client signs
|
|
33
|
+
the UTF-8 bytes of
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
spinajs-key-<purpose>:<nonce>:<audience>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
where `<purpose>` is `register` (proof of possession when a user adds a
|
|
40
|
+
key) or `login` (challenge-response login), `<nonce>` is the `nonce`
|
|
41
|
+
exactly as received (base64url, so it holds no `:`), and `<audience>` is
|
|
42
|
+
the service's `authn.keys.challenge.audience` (default: its
|
|
43
|
+
`authn.keys.assertion.audience`). The client knows the audience from its
|
|
44
|
+
own configuration of the service, never from the challenge answer: a
|
|
45
|
+
service that relays another one's challenge then gets a signature the
|
|
46
|
+
other one refuses. It sends the raw signature as unpadded base64url:
|
|
47
|
+
64 bytes for Ed25519, and for P-256 an ECDSA SHA-256 signature as
|
|
48
|
+
IEEE P1363 `r || s` (64 bytes, not DER). It names the challenge by its
|
|
49
|
+
`id`, unchanged: the id carries the nonce back to the server, so treat it
|
|
50
|
+
as opaque.
|
|
51
|
+
|
|
52
|
+
- Registration: `{ owner, jwk, proof: { challengeId: id, signature } }`.
|
|
53
|
+
- Login: credentials `{ kind: 'key-challenge', keyId, challengeId: id, signature }`.
|
|
54
|
+
|
|
55
|
+
A challenge is single use, lives `authn.keys.challenge.ttlSeconds` (default
|
|
56
|
+
120 s), and is redeemed only after the signature holds. A login challenge
|
|
57
|
+
counts against the `login` flow's IP throttle key.
|
|
58
|
+
|
|
59
|
+
With OpenSSL 3, for an Ed25519 key in `key.pem`:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
printf 'spinajs-key-login:%s:%s' "$NONCE" "$AUDIENCE" > input.bin
|
|
63
|
+
openssl pkeyutl -sign -inkey key.pem -rawin -in input.bin | basenc --base64url | tr -d '=\n'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
From Node: `signChallenge(challenge, 'login', audience, privateKey, 'EdDSA')`.
|
|
67
|
+
|
|
68
|
+
## JWS
|
|
69
|
+
|
|
70
|
+
`StrictJwsVerifier` implements `JwsVerifier` under the C8 D4 rules. Checks
|
|
71
|
+
run in this order and the first failure gives the code: `malformed`,
|
|
72
|
+
`bad_header` (exactly `{ alg, kid, typ }`), `unknown_key`, `bad_signature`
|
|
73
|
+
(with the key's own algorithm), `invalid_claims` (JSON read as serde_json
|
|
74
|
+
reads it; integer `iat < exp`, lifetime within the limit), `wrong_issuer`,
|
|
75
|
+
`wrong_audience`, `expired` (`now ≥ exp + skew`), `not_yet_valid`
|
|
76
|
+
(`iat > now + skew`).
|
|
77
|
+
|
|
78
|
+
`LocalJwsSigner` implements `JwsSigner` over private JWKs the app loads:
|
|
79
|
+
`new LocalJwsSigner(privateJwks, { current: kid })`. It writes the header
|
|
80
|
+
as `{"alg","kid","typ"}` and the claims in the caller's order, so the canvas
|
|
81
|
+
C8 golden tickets come out byte for byte.
|
|
82
|
+
|
|
83
|
+
## JWS client assertions (`keys-jws`)
|
|
84
|
+
|
|
85
|
+
`Authorization: Bearer <jws>` → credentials `{ kind: 'jws-assertion', jws }`.
|
|
86
|
+
|
|
87
|
+
- Header: `{ "alg": "EdDSA" | "ES256", "kid": "<kid>", "typ": "spinajs-client+jwt" }`.
|
|
88
|
+
- Claims: `iss` is the key owner as `<type>:<id>` (`user:<uuid>`,
|
|
89
|
+
`service:runner-1`), `aud` is `authn.keys.assertion.audience` (required),
|
|
90
|
+
integer `iat` and `exp` with `exp − iat ≤ 300` (or the configured
|
|
91
|
+
`maxLifetimeSeconds`), and a `jti` of 1–128 characters.
|
|
92
|
+
- A `jti` is accepted once per key until `exp` plus the skew (`authn_nonce`
|
|
93
|
+
or memory).
|
|
94
|
+
|
|
95
|
+
## HTTP signatures (`keys-httpsig`)
|
|
96
|
+
|
|
97
|
+
An RFC 9421 subset with exactly one signature:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Signature-Input: sig1=("@method" "@target-uri" "content-digest");created=1790000000;expires=1790000300;nonce="<random>";keyid="<kid>"
|
|
101
|
+
Signature: sig1=:<standard base64 of the raw signature>:
|
|
102
|
+
Content-Digest: sha-256=:<standard base64 of SHA-256(body)>:
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- Any label; the `Signature-Input` member must be in canonical RFC 8941
|
|
106
|
+
form (single spaces, no parameters on components).
|
|
107
|
+
- Covered: `@method` and `@target-uri` always; `content-digest` whenever
|
|
108
|
+
the request has a body (RFC 9530, `sha-256` or `sha-512`, checked against
|
|
109
|
+
the body); also each name in `authn.keys.httpSignature.requiredComponents`.
|
|
110
|
+
`@authority`, `@path`, `@query` and present request headers may be
|
|
111
|
+
covered too.
|
|
112
|
+
- Parameters: `created`, `expires`, `nonce`, `keyid` required; `alg`
|
|
113
|
+
optional (`ed25519` or `ecdsa-p256-sha256`, and then it must name the
|
|
114
|
+
key's algorithm); nothing else.
|
|
115
|
+
- `expires − created ≤ maxAgeSeconds` (300), `created` not ahead of now by
|
|
116
|
+
more than the skew, `now < expires`; the nonce is accepted once per key.
|
|
117
|
+
- The signature base is RFC 9421 §2.5; its `@signature-params` line is the
|
|
118
|
+
`Signature-Input` member value as sent.
|
|
119
|
+
|
|
120
|
+
## Strategies
|
|
121
|
+
|
|
122
|
+
| Name | Credentials kind | Session |
|
|
123
|
+
|---|---|---|
|
|
124
|
+
| `keys-jws` (`JwsAssertionStrategy`) | `jws-assertion` | no |
|
|
125
|
+
| `keys-httpsig` (`HttpSignatureStrategy`) | `http-signature` | no |
|
|
126
|
+
| `keys-challenge` (`KeyChallengeStrategy`) | `key-challenge` | yes |
|
|
127
|
+
|
|
128
|
+
Each refuses credentials over 8 KiB before parsing them, verifies only
|
|
129
|
+
usable keys (not revoked, not expired), answers an
|
|
130
|
+
unknown key with the same work and result as a bad signature, throttles by
|
|
131
|
+
`key:<kid>`, and returns the key owner as the identity with the key's
|
|
132
|
+
`scope` as the ceiling.
|
|
133
|
+
|
|
134
|
+
## Configuration (`authn.keys`)
|
|
135
|
+
|
|
136
|
+
| Key | Default |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `algorithms` | `['EdDSA', 'ES256']` |
|
|
139
|
+
| `challenge.ttlSeconds` | `120` |
|
|
140
|
+
| `challenge.audience` | `null` (then `assertion.audience`; challenges need one) |
|
|
141
|
+
| `assertion.typ` | `'spinajs-client+jwt'` |
|
|
142
|
+
| `assertion.audience` | `null` (required for `keys-jws`) |
|
|
143
|
+
| `assertion.maxLifetimeSeconds` | `300` (at most 300) |
|
|
144
|
+
| `assertion.skewSeconds` | `30` |
|
|
145
|
+
| `httpSignature.maxAgeSeconds` | `300` (at most 300) |
|
|
146
|
+
| `httpSignature.skewSeconds` | `30` |
|
|
147
|
+
| `httpSignature.requiredComponents` | `['@method', '@target-uri', 'content-digest']` |
|
|
148
|
+
|
|
149
|
+
`authn_nonce` comes with `@spinajs/authn-orm`'s `AuthnInitial` migration.
|
|
150
|
+
|
|
151
|
+
List the strategies an app uses in `authn.strategies`; `service` is the
|
|
152
|
+
class name:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
authn: {
|
|
156
|
+
strategies: [
|
|
157
|
+
{ name: 'keys-jws', service: 'JwsAssertionStrategy' },
|
|
158
|
+
{ name: 'keys-httpsig', service: 'HttpSignatureStrategy' },
|
|
159
|
+
{ name: 'keys-challenge', service: 'KeyChallengeStrategy' },
|
|
160
|
+
],
|
|
161
|
+
keys: { assertion: { audience: 'https://api.example.com' } },
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Dates in `authn_key` and `authn_nonce` are UTC instants; the process time zone does not matter.
|
|
2
166
|
|
|
3
|
-
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Bootstrapper } from '@spinajs/di';
|
|
2
|
+
/** Registers the authn.keys schema, so the configuration validates that subtree and fills its defaults. */
|
|
3
|
+
export declare class AuthnKeysConfigBootstrapper extends Bootstrapper {
|
|
4
|
+
bootstrap(): void;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/bootstrap.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AAIvD,2GAA2G;AAC3G,qBACa,2BAA4B,SAAQ,YAAY;IACpD,SAAS,IAAI,IAAI,CAEvB;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.AuthnKeysConfigBootstrapper = void 0;
|
|
10
|
+
const configuration_1 = require("@spinajs/configuration");
|
|
11
|
+
const di_1 = require("@spinajs/di");
|
|
12
|
+
const schema_js_1 = require("./schema.js");
|
|
13
|
+
/** Registers the authn.keys schema, so the configuration validates that subtree and fills its defaults. */
|
|
14
|
+
let AuthnKeysConfigBootstrapper = class AuthnKeysConfigBootstrapper extends di_1.Bootstrapper {
|
|
15
|
+
bootstrap() {
|
|
16
|
+
(0, configuration_1.registerConfigurationSchema)(schema_js_1.AUTHN_KEYS_CONFIGURATION_SCHEMA);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
exports.AuthnKeysConfigBootstrapper = AuthnKeysConfigBootstrapper;
|
|
20
|
+
exports.AuthnKeysConfigBootstrapper = AuthnKeysConfigBootstrapper = __decorate([
|
|
21
|
+
(0, di_1.Injectable)(di_1.Bootstrapper)
|
|
22
|
+
], AuthnKeysConfigBootstrapper);
|
|
23
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/bootstrap.ts"],"names":[],"mappings":";;;;;;;;;AAAA,0DAAqE;AACrE,oCAAuD;AAEvD,2CAA8D;AAE9D,2GAA2G;AAEpG,IAAM,2BAA2B,GAAjC,MAAM,2BAA4B,SAAQ,iBAAY;IACpD,SAAS;QACd,IAAA,2CAA2B,EAAC,2CAA+B,CAAC,CAAC;IAC/D,CAAC;CACF,CAAA;;sCAJY,2BAA2B;IADvC,IAAA,eAAU,EAAC,iBAAY,CAAC;GACZ,2BAA2B,CAIvC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { KeyObject } from 'node:crypto';
|
|
2
|
+
import type { KeyAlgorithm, KeyChallenge, KeyMaterial } from '@spinajs/authn/contracts';
|
|
3
|
+
export type ChallengePurpose = 'register' | 'login';
|
|
4
|
+
/** The SecretStore purpose of one challenge; `rid` keeps it within authn_secret.purpose (varchar 32). */
|
|
5
|
+
export declare const challengeSecretPurpose: (rid: string) => string;
|
|
6
|
+
export declare const newChallengeRid: () => string;
|
|
7
|
+
/**
|
|
8
|
+
* `<rid>.<nonce>`. SecretStore keeps only a hash of the nonce and redeems by the raw value, and the credentials carry
|
|
9
|
+
* only the challenge id and a signature, so the id has to bring the nonce back.
|
|
10
|
+
*/
|
|
11
|
+
export declare const challengeId: (rid: string, nonce: string) => string;
|
|
12
|
+
export declare function parseChallengeId(id: unknown): {
|
|
13
|
+
rid: string;
|
|
14
|
+
nonce: string;
|
|
15
|
+
} | null;
|
|
16
|
+
/** Nonces are base64url, so the first ':' after one ends it and the signing input has exactly one reading. */
|
|
17
|
+
export declare const isChallengeNonce: (nonce: unknown) => nonce is string;
|
|
18
|
+
/**
|
|
19
|
+
* The bytes a key signs to answer a challenge: UTF-8 of `spinajs-key-<purpose>:<nonce>:<audience>`. The purpose keeps
|
|
20
|
+
* a login signature from proving a registration; the audience is the service as the client knows it (never taken from
|
|
21
|
+
* the challenge answer), so a service that relays another one's challenge gets a signature the other one refuses.
|
|
22
|
+
*/
|
|
23
|
+
export declare function challengeSigningInput(challenge: Pick<KeyChallenge, 'nonce'>, purpose: ChallengePurpose, audience: string): Buffer;
|
|
24
|
+
/** The answer a client sends: the raw signature (64 bytes; ES256 as IEEE P1363) as unpadded base64url. */
|
|
25
|
+
export declare function signChallenge(challenge: Pick<KeyChallenge, 'nonce'>, purpose: ChallengePurpose, audience: string, privateKey: KeyObject, alg: KeyAlgorithm): string;
|
|
26
|
+
export declare function verifyChallengeSignature(key: KeyMaterial, nonce: string, purpose: ChallengePurpose, audience: string, signature: unknown): boolean;
|
|
27
|
+
//# sourceMappingURL=challenge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../../../src/challenge/challenge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAKxF,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,OAAO,CAAC;AAEpD,yGAAyG;AACzG,eAAO,MAAM,sBAAsB,QAAS,MAAM,KAAG,MAAgC,CAAC;AAQtF,eAAO,MAAM,eAAe,QAAO,MAAsD,CAAC;AAE1F;;;GAGG;AACH,eAAO,MAAM,WAAW,QAAS,MAAM,SAAS,MAAM,KAAG,MAA2B,CAAC;AAErF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAQnF;AAED,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,UAAW,OAAO,KAAG,KAAK,IAAI,MAAwD,CAAC;AAEpH;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjI;AAED,0GAA0G;AAC1G,wBAAgB,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,CAEnK;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAGlJ"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isChallengeNonce = exports.challengeId = exports.newChallengeRid = exports.challengeSecretPurpose = void 0;
|
|
4
|
+
exports.parseChallengeId = parseChallengeId;
|
|
5
|
+
exports.challengeSigningInput = challengeSigningInput;
|
|
6
|
+
exports.signChallenge = signChallenge;
|
|
7
|
+
exports.verifyChallengeSignature = verifyChallengeSignature;
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
const util_1 = require("@spinajs/util");
|
|
10
|
+
const base64url_js_1 = require("../jwk/base64url.js");
|
|
11
|
+
const signature_js_1 = require("../jwk/signature.js");
|
|
12
|
+
/** The SecretStore purpose of one challenge; `rid` keeps it within authn_secret.purpose (varchar 32). */
|
|
13
|
+
const challengeSecretPurpose = (rid) => `key-challenge:${rid}`;
|
|
14
|
+
exports.challengeSecretPurpose = challengeSecretPurpose;
|
|
15
|
+
const RID_BYTES = 12;
|
|
16
|
+
const RID_LENGTH = 16;
|
|
17
|
+
const RID = /^[A-Za-z0-9_-]{16}$/;
|
|
18
|
+
const MAX_NONCE_LENGTH = 512;
|
|
19
|
+
const NONCE = /^[A-Za-z0-9_-]{22,512}$/;
|
|
20
|
+
const newChallengeRid = () => (0, node_crypto_1.randomBytes)(RID_BYTES).toString('base64url');
|
|
21
|
+
exports.newChallengeRid = newChallengeRid;
|
|
22
|
+
/**
|
|
23
|
+
* `<rid>.<nonce>`. SecretStore keeps only a hash of the nonce and redeems by the raw value, and the credentials carry
|
|
24
|
+
* only the challenge id and a signature, so the id has to bring the nonce back.
|
|
25
|
+
*/
|
|
26
|
+
const challengeId = (rid, nonce) => `${rid}.${nonce}`;
|
|
27
|
+
exports.challengeId = challengeId;
|
|
28
|
+
function parseChallengeId(id) {
|
|
29
|
+
const [ok, text] = (0, util_1._try_check_arg)((0, util_1._is_string)((0, util_1._between)(RID_LENGTH + 2, RID_LENGTH + 1 + MAX_NONCE_LENGTH), (0, util_1._custom)((s) => s[RID_LENGTH] === '.')))(id, 'id');
|
|
30
|
+
if (!ok) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const rid = text.slice(0, RID_LENGTH);
|
|
34
|
+
const nonce = text.slice(RID_LENGTH + 1);
|
|
35
|
+
return RID.test(rid) && (0, exports.isChallengeNonce)(nonce) ? { rid, nonce } : null;
|
|
36
|
+
}
|
|
37
|
+
/** Nonces are base64url, so the first ':' after one ends it and the signing input has exactly one reading. */
|
|
38
|
+
const isChallengeNonce = (nonce) => typeof nonce === 'string' && NONCE.test(nonce);
|
|
39
|
+
exports.isChallengeNonce = isChallengeNonce;
|
|
40
|
+
/**
|
|
41
|
+
* The bytes a key signs to answer a challenge: UTF-8 of `spinajs-key-<purpose>:<nonce>:<audience>`. The purpose keeps
|
|
42
|
+
* a login signature from proving a registration; the audience is the service as the client knows it (never taken from
|
|
43
|
+
* the challenge answer), so a service that relays another one's challenge gets a signature the other one refuses.
|
|
44
|
+
*/
|
|
45
|
+
function challengeSigningInput(challenge, purpose, audience) {
|
|
46
|
+
return Buffer.from(`spinajs-key-${purpose}:${challenge.nonce}:${audience}`, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
/** The answer a client sends: the raw signature (64 bytes; ES256 as IEEE P1363) as unpadded base64url. */
|
|
49
|
+
function signChallenge(challenge, purpose, audience, privateKey, alg) {
|
|
50
|
+
return (0, base64url_js_1.encodeB64url)((0, signature_js_1.signRaw)(alg, privateKey, challengeSigningInput(challenge, purpose, audience)));
|
|
51
|
+
}
|
|
52
|
+
function verifyChallengeSignature(key, nonce, purpose, audience, signature) {
|
|
53
|
+
const raw = (0, base64url_js_1.decodeB64url)(signature);
|
|
54
|
+
return raw !== null && (0, exports.isChallengeNonce)(nonce) && (0, signature_js_1.verifySignature)(key, challengeSigningInput({ nonce }, purpose, audience), raw);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=challenge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"challenge.js","sourceRoot":"","sources":["../../../src/challenge/challenge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/challenge/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./challenge.js"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/challenge/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA+B"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CliCommand } from '@spinajs/cli';
|
|
2
|
+
import { Log } from '@spinajs/log';
|
|
3
|
+
import { KeyRegistry } from '@spinajs/authn/contracts';
|
|
4
|
+
interface AddKeyOptions {
|
|
5
|
+
jwk?: string;
|
|
6
|
+
file?: string;
|
|
7
|
+
kid?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
expires?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Services and agents only: a user registers a key with a proof of possession, which the CLI cannot give. */
|
|
12
|
+
export declare class AddKey extends CliCommand {
|
|
13
|
+
protected Log: Log;
|
|
14
|
+
protected Registry: KeyRegistry;
|
|
15
|
+
execute(ownerType: string, ownerId: string, options: AddKeyOptions): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=AddKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddKey.d.ts","sourceRoot":"","sources":["../../../src/cli/AddKey.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,UAAU,EAAmB,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAa,WAAW,EAAc,MAAM,0BAA0B,CAAC;AAK9E,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,8GAA8G;AAC9G,qBAQa,MAAO,SAAQ,UAAU;IAEpC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAGnB,SAAS,CAAC,QAAQ,EAAE,WAAW,CAAC;IAEnB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAuB9F;CACF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.AddKey = void 0;
|
|
13
|
+
const di_1 = require("@spinajs/di");
|
|
14
|
+
const cli_1 = require("@spinajs/cli");
|
|
15
|
+
const log_1 = require("@spinajs/log");
|
|
16
|
+
const contracts_1 = require("@spinajs/authn/contracts");
|
|
17
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
18
|
+
const common_js_1 = require("./common.js");
|
|
19
|
+
/** Services and agents only: a user registers a key with a proof of possession, which the CLI cannot give. */
|
|
20
|
+
let AddKey = class AddKey extends cli_1.CliCommand {
|
|
21
|
+
async execute(ownerType, ownerId, options) {
|
|
22
|
+
const owner = (0, common_js_1.ownerArg)(ownerType, ownerId);
|
|
23
|
+
if (owner.type === 'user') {
|
|
24
|
+
throw new exceptions_1.InvalidArgument('a user registers a key with a proof of possession, through POST /user/keys');
|
|
25
|
+
}
|
|
26
|
+
const expiresAt = options.expires === undefined ? undefined : new Date(options.expires);
|
|
27
|
+
// Without a zone, Date reads the time as local, which differs per host.
|
|
28
|
+
if (expiresAt !== undefined && (Number.isNaN(expiresAt.getTime()) || !/T.*(Z|[+-]\d{2}:?\d{2})$/i.test(options.expires))) {
|
|
29
|
+
throw new exceptions_1.InvalidArgument(`--expires is not an ISO 8601 instant with Z or an offset: ${options.expires}`);
|
|
30
|
+
}
|
|
31
|
+
const reg = {
|
|
32
|
+
owner,
|
|
33
|
+
actor: contracts_1.CLI_ACTOR,
|
|
34
|
+
jwk: (0, common_js_1.readPublicJwk)(options),
|
|
35
|
+
...(options.kid !== undefined ? { kid: options.kid } : {}),
|
|
36
|
+
...(options.name !== undefined ? { name: options.name } : {}),
|
|
37
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
38
|
+
};
|
|
39
|
+
const r = await this.Registry.register(reg, (0, contracts_1.cliContext)());
|
|
40
|
+
if (!r.ok) {
|
|
41
|
+
throw new exceptions_1.InvalidArgument(`key not registered: ${r.code}`);
|
|
42
|
+
}
|
|
43
|
+
this.Log.success({ kid: r.key.kid, alg: r.key.alg, thumbprint: r.key.thumbprint }, 'Key registered: kid ${kid}, ${alg}, thumbprint ${thumbprint}');
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
exports.AddKey = AddKey;
|
|
47
|
+
__decorate([
|
|
48
|
+
(0, log_1.Logger)('authn-keys'),
|
|
49
|
+
__metadata("design:type", log_1.Log)
|
|
50
|
+
], AddKey.prototype, "Log", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, di_1.Autoinject)(contracts_1.KeyRegistry),
|
|
53
|
+
__metadata("design:type", contracts_1.KeyRegistry)
|
|
54
|
+
], AddKey.prototype, "Registry", void 0);
|
|
55
|
+
exports.AddKey = AddKey = __decorate([
|
|
56
|
+
(0, cli_1.Command)('authn:key-add', 'Registers an Ed25519 or P-256 public key for a service or an agent'),
|
|
57
|
+
(0, cli_1.Argument)({ name: 'ownerType', required: true, description: 'service or agent', choices: ['service', 'agent'] }),
|
|
58
|
+
(0, cli_1.Argument)('ownerId', true, 'id of the service or agent'),
|
|
59
|
+
(0, cli_1.Option)('--jwk <json>', false, 'the public JWK as JSON'),
|
|
60
|
+
(0, cli_1.Option)('--file <path>', false, 'a file with the public JWK (JSON) or a PEM public key'),
|
|
61
|
+
(0, cli_1.Option)('--kid <kid>', false, 'key id, 1-64 of [A-Za-z0-9._-]; default: the RFC 7638 thumbprint'),
|
|
62
|
+
(0, cli_1.Option)('--name <name>', false, 'label, up to 128 characters'),
|
|
63
|
+
(0, cli_1.Option)('--expires <iso>', false, 'ISO 8601 expiry instant with Z or an offset')
|
|
64
|
+
], AddKey);
|
|
65
|
+
//# sourceMappingURL=AddKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddKey.js","sourceRoot":"","sources":["../../../src/cli/AddKey.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CliCommand } from '@spinajs/cli';
|
|
2
|
+
import { Log } from '@spinajs/log';
|
|
3
|
+
import { KeyRegistry } from '@spinajs/authn/contracts';
|
|
4
|
+
import type { KeyRecord } from '@spinajs/authn/contracts';
|
|
5
|
+
export declare function keyLine(k: KeyRecord): string;
|
|
6
|
+
export declare class ListKeys extends CliCommand {
|
|
7
|
+
protected Log: Log;
|
|
8
|
+
protected Registry: KeyRegistry;
|
|
9
|
+
execute(ownerType: string, ownerId: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ListKeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListKeys.d.ts","sourceRoot":"","sources":["../../../src/cli/ListKeys.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,UAAU,EAAW,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAK1D,wBAAgB,OAAO,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAE5C;AAED,qBAGa,QAAS,SAAQ,UAAU;IAEtC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAGnB,SAAS,CAAC,QAAQ,EAAE,WAAW,CAAC;IAEnB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtE;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ListKeys = void 0;
|
|
13
|
+
exports.keyLine = keyLine;
|
|
14
|
+
const di_1 = require("@spinajs/di");
|
|
15
|
+
const cli_1 = require("@spinajs/cli");
|
|
16
|
+
const log_1 = require("@spinajs/log");
|
|
17
|
+
const contracts_1 = require("@spinajs/authn/contracts");
|
|
18
|
+
const common_js_1 = require("./common.js");
|
|
19
|
+
const iso = (d) => (d ? d.toISOString() : '-');
|
|
20
|
+
function keyLine(k) {
|
|
21
|
+
return [k.kid, k.alg, k.name ?? '-', `created ${iso(k.createdAt)}`, `expires ${iso(k.expiresAt)}`, `revoked ${iso(k.revokedAt)}`, `last used ${iso(k.lastUsedAt)}`].join(' ');
|
|
22
|
+
}
|
|
23
|
+
let ListKeys = class ListKeys extends cli_1.CliCommand {
|
|
24
|
+
async execute(ownerType, ownerId) {
|
|
25
|
+
const keys = await this.Registry.list((0, common_js_1.ownerArg)(ownerType, ownerId));
|
|
26
|
+
if (keys.length === 0) {
|
|
27
|
+
this.Log.info('No keys');
|
|
28
|
+
}
|
|
29
|
+
for (const k of keys) {
|
|
30
|
+
this.Log.info(keyLine(k));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
exports.ListKeys = ListKeys;
|
|
35
|
+
__decorate([
|
|
36
|
+
(0, log_1.Logger)('authn-keys'),
|
|
37
|
+
__metadata("design:type", log_1.Log)
|
|
38
|
+
], ListKeys.prototype, "Log", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
(0, di_1.Autoinject)(contracts_1.KeyRegistry),
|
|
41
|
+
__metadata("design:type", contracts_1.KeyRegistry)
|
|
42
|
+
], ListKeys.prototype, "Registry", void 0);
|
|
43
|
+
exports.ListKeys = ListKeys = __decorate([
|
|
44
|
+
(0, cli_1.Command)('authn:key-list', 'Lists the public keys of a user, a service or an agent, revoked and expired ones included'),
|
|
45
|
+
(0, cli_1.Argument)({ name: 'ownerType', required: true, description: common_js_1.OWNER_TYPES.join(', '), choices: common_js_1.OWNER_TYPES }),
|
|
46
|
+
(0, cli_1.Argument)('ownerId', true, 'id of the owner (users.Uuid for users)')
|
|
47
|
+
], ListKeys);
|
|
48
|
+
//# sourceMappingURL=ListKeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListKeys.js","sourceRoot":"","sources":["../../../src/cli/ListKeys.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,oCAAyC;AACzC,sCAA6D;AAC7D,sCAA2C;AAC3C,wDAAuD;AAEvD,2CAAoD;AAEpD,MAAM,GAAG,GAAG,CAAC,CAAQ,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAE9D,iBAAwB,CAAY;IAClC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,IAAI,GAAG,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE,aAAa,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjL,CAAC;AAKM,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,gBAAU;IAO/B,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,OAAe;QACrD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAA,oBAAQ,EAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;QACpE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;CACF,CAAA;;AAdW;IADT,IAAA,YAAM,EAAC,YAAY,CAAC;8BACN,SAAG;qCAAC;AAGT;IADT,IAAA,eAAU,EAAC,uBAAW,CAAC;8BACJ,uBAAW;0CAAC;mBALrB,QAAQ;IAHpB,IAAA,aAAO,EAAC,gBAAgB,EAAE,2FAA2F,CAAC;IACtH,IAAA,cAAQ,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,uBAAW,EAAE,CAAC;IAC1G,IAAA,cAAQ,EAAC,SAAS,EAAE,IAAI,EAAE,wCAAwC,CAAC;GACvD,QAAQ,CAgBpB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CliCommand } from '@spinajs/cli';
|
|
2
|
+
import { Log } from '@spinajs/log';
|
|
3
|
+
import { KeyRegistry } from '@spinajs/authn/contracts';
|
|
4
|
+
export declare class RevokeKey extends CliCommand {
|
|
5
|
+
protected Log: Log;
|
|
6
|
+
protected Registry: KeyRegistry;
|
|
7
|
+
execute(ownerType: string, ownerId: string, kid: string): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=RevokeKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RevokeKey.d.ts","sourceRoot":"","sources":["../../../src/cli/RevokeKey.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,UAAU,EAAW,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAa,WAAW,EAAc,MAAM,0BAA0B,CAAC;AAI9E,qBAIa,SAAU,SAAQ,UAAU;IAEvC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAGnB,SAAS,CAAC,QAAQ,EAAE,WAAW,CAAC;IAEnB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKnF;CACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RevokeKey = void 0;
|
|
13
|
+
const di_1 = require("@spinajs/di");
|
|
14
|
+
const cli_1 = require("@spinajs/cli");
|
|
15
|
+
const log_1 = require("@spinajs/log");
|
|
16
|
+
const contracts_1 = require("@spinajs/authn/contracts");
|
|
17
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
18
|
+
const common_js_1 = require("./common.js");
|
|
19
|
+
let RevokeKey = class RevokeKey extends cli_1.CliCommand {
|
|
20
|
+
async execute(ownerType, ownerId, kid) {
|
|
21
|
+
if (!(await this.Registry.revoke((0, common_js_1.ownerArg)(ownerType, ownerId), kid, (0, contracts_1.cliContext)(), { actor: contracts_1.CLI_ACTOR }))) {
|
|
22
|
+
throw new exceptions_1.InvalidArgument(`no key ${kid} of ${ownerType} ${ownerId}`);
|
|
23
|
+
}
|
|
24
|
+
this.Log.success({ kid }, 'Key ${kid} revoked');
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
exports.RevokeKey = RevokeKey;
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, log_1.Logger)('authn-keys'),
|
|
30
|
+
__metadata("design:type", log_1.Log)
|
|
31
|
+
], RevokeKey.prototype, "Log", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, di_1.Autoinject)(contracts_1.KeyRegistry),
|
|
34
|
+
__metadata("design:type", contracts_1.KeyRegistry)
|
|
35
|
+
], RevokeKey.prototype, "Registry", void 0);
|
|
36
|
+
exports.RevokeKey = RevokeKey = __decorate([
|
|
37
|
+
(0, cli_1.Command)('authn:key-revoke', 'Revokes a public key; it fails on the next request'),
|
|
38
|
+
(0, cli_1.Argument)({ name: 'ownerType', required: true, description: common_js_1.OWNER_TYPES.join(', '), choices: common_js_1.OWNER_TYPES }),
|
|
39
|
+
(0, cli_1.Argument)('ownerId', true, 'id of the owner (users.Uuid for users)'),
|
|
40
|
+
(0, cli_1.Argument)('kid', true, 'key id')
|
|
41
|
+
], RevokeKey);
|
|
42
|
+
//# sourceMappingURL=RevokeKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RevokeKey.js","sourceRoot":"","sources":["../../../src/cli/RevokeKey.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,oCAAyC;AACzC,sCAA6D;AAC7D,sCAA2C;AAC3C,wDAA8E;AAC9E,oDAAsD;AACtD,2CAAoD;AAM7C,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,gBAAU;IAOhC,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,OAAe,EAAE,GAAW;QAClE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAA,oBAAQ,EAAC,SAAS,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,IAAA,sBAAU,GAAE,EAAE,EAAE,KAAK,EAAE,qBAAS,EAAE,CAAC,CAAC,EAAE,CAAC;YACzG,MAAM,IAAI,4BAAe,CAAC,UAAU,GAAG,OAAO,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,EAAE,oBAAoB,CAAC,CAAC;IAClD,CAAC;CACF,CAAA;;AAXW;IADT,IAAA,YAAM,EAAC,YAAY,CAAC;8BACN,SAAG;sCAAC;AAGT;IADT,IAAA,eAAU,EAAC,uBAAW,CAAC;8BACJ,uBAAW;2CAAC;oBALrB,SAAS;IAJrB,IAAA,aAAO,EAAC,kBAAkB,EAAE,oDAAoD,CAAC;IACjF,IAAA,cAAQ,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uBAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,uBAAW,EAAE,CAAC;IAC1G,IAAA,cAAQ,EAAC,SAAS,EAAE,IAAI,EAAE,wCAAwC,CAAC;IACnE,IAAA,cAAQ,EAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC;GACnB,SAAS,CAarB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IdentityRef } from '@spinajs/authn/contracts';
|
|
2
|
+
export declare const OWNER_TYPES: string[];
|
|
3
|
+
export declare function ownerArg(type: string, id: string): IdentityRef;
|
|
4
|
+
/** A public JWK given inline as JSON, or a file holding JSON or a PEM public key; private keys are refused. */
|
|
5
|
+
export declare function readPublicJwk(opts: {
|
|
6
|
+
jwk?: string;
|
|
7
|
+
file?: string;
|
|
8
|
+
}): unknown;
|
|
9
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/cli/common.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAG5D,eAAO,MAAM,WAAW,UAA+B,CAAC;AAExD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CAK9D;AAED,+GAA+G;AAC/G,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAgB5E"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OWNER_TYPES = void 0;
|
|
4
|
+
exports.ownerArg = ownerArg;
|
|
5
|
+
exports.readPublicJwk = readPublicJwk;
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
9
|
+
exports.OWNER_TYPES = ['user', 'service', 'agent'];
|
|
10
|
+
function ownerArg(type, id) {
|
|
11
|
+
if (!exports.OWNER_TYPES.includes(type)) {
|
|
12
|
+
throw new exceptions_1.InvalidArgument(`owner type must be one of ${exports.OWNER_TYPES.join(', ')}, not ${JSON.stringify(type)}`);
|
|
13
|
+
}
|
|
14
|
+
return { type, id };
|
|
15
|
+
}
|
|
16
|
+
/** A public JWK given inline as JSON, or a file holding JSON or a PEM public key; private keys are refused. */
|
|
17
|
+
function readPublicJwk(opts) {
|
|
18
|
+
if ((opts.jwk === undefined) === (opts.file === undefined)) {
|
|
19
|
+
throw new exceptions_1.InvalidArgument('give the key with exactly one of --jwk or --file');
|
|
20
|
+
}
|
|
21
|
+
const text = opts.jwk ?? (0, node_fs_1.readFileSync)(opts.file, 'utf8');
|
|
22
|
+
if (text.includes('-----BEGIN')) {
|
|
23
|
+
if (!text.includes('-----BEGIN PUBLIC KEY-----') || text.includes('PRIVATE KEY')) {
|
|
24
|
+
throw new exceptions_1.InvalidArgument('the PEM file must hold a public key (SPKI), never a private key');
|
|
25
|
+
}
|
|
26
|
+
return (0, node_crypto_1.createPublicKey)(text).export({ format: 'jwk' });
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(text);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new exceptions_1.InvalidArgument('the key is neither JSON nor a PEM public key');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/cli/common.ts"],"names":[],"mappings":";;;;;AAAA,qCAAuC;AACvC,6CAA8C;AAE9C,oDAAsD;AAEzC,QAAA,WAAW,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAExD,kBAAyB,IAAY,EAAE,EAAU;IAC/C,IAAI,CAAC,QAAA,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,4BAAe,CAAC,6BAA6B,QAAA,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AACtB,CAAC;AAED,+GAA+G;AAC/G,uBAA8B,IAAqC;IACjE,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,4BAAe,CAAC,kDAAkD,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,IAAA,sBAAY,EAAC,IAAI,CAAC,IAAK,EAAE,MAAM,CAAC,CAAC;IAC1D,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,4BAA4B,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACjF,MAAM,IAAI,4BAAe,CAAC,iEAAiE,CAAC,CAAC;QAC/F,CAAC;QACD,OAAO,IAAA,6BAAe,EAAC,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,4BAAe,CAAC,8CAA8C,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC"}
|