@iveri/contracts 0.6.0 → 0.7.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 +8 -2
- package/dist/cjs/api/identity/access-token-payload.interface.d.ts +53 -0
- package/dist/cjs/api/identity/access-token-payload.interface.d.ts.map +1 -0
- package/dist/cjs/api/identity/access-token-payload.interface.js +3 -0
- package/dist/cjs/api/identity/access-token-payload.interface.js.map +1 -0
- package/dist/cjs/api/identity/auth.type.d.ts +21 -0
- package/dist/cjs/api/identity/auth.type.d.ts.map +1 -1
- package/dist/cjs/api/identity/index.d.ts +2 -0
- package/dist/cjs/api/identity/index.d.ts.map +1 -1
- package/dist/cjs/api/identity/index.js +2 -0
- package/dist/cjs/api/identity/index.js.map +1 -1
- package/dist/cjs/api/identity/principal-type.enum.d.ts +19 -0
- package/dist/cjs/api/identity/principal-type.enum.d.ts.map +1 -0
- package/dist/cjs/api/identity/principal-type.enum.js +23 -0
- package/dist/cjs/api/identity/principal-type.enum.js.map +1 -0
- package/dist/esm/api/identity/access-token-payload.interface.d.ts +53 -0
- package/dist/esm/api/identity/access-token-payload.interface.d.ts.map +1 -0
- package/dist/esm/api/identity/access-token-payload.interface.js +2 -0
- package/dist/esm/api/identity/access-token-payload.interface.js.map +1 -0
- package/dist/esm/api/identity/auth.type.d.ts +21 -0
- package/dist/esm/api/identity/auth.type.d.ts.map +1 -1
- package/dist/esm/api/identity/index.d.ts +2 -0
- package/dist/esm/api/identity/index.d.ts.map +1 -1
- package/dist/esm/api/identity/index.js +2 -0
- package/dist/esm/api/identity/index.js.map +1 -1
- package/dist/esm/api/identity/principal-type.enum.d.ts +19 -0
- package/dist/esm/api/identity/principal-type.enum.d.ts.map +1 -0
- package/dist/esm/api/identity/principal-type.enum.js +20 -0
- package/dist/esm/api/identity/principal-type.enum.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,8 +69,14 @@ catalogue and are already embedded in issued tokens and stored role rows.
|
|
|
69
69
|
### `api/identity/` → `@iveri/contracts/identity`
|
|
70
70
|
|
|
71
71
|
`iveri-identity-api`'s authentication surface: `SignupBody`, `LoginBody`, `RefreshTokenBody`,
|
|
72
|
-
`AcceptInvitationBody`, `AuthTokens`, `Principal`, `AuthSession`, plus
|
|
73
|
-
`MembershipStatus`.
|
|
72
|
+
`AcceptInvitationBody`, `AuthTokens`, `ServiceToken`, `Principal`, `AuthSession`, plus
|
|
73
|
+
`TenantStatus`, `MembershipStatus` and `PrincipalType`.
|
|
74
|
+
|
|
75
|
+
It also holds **`AccessTokenPayload`** — the claims inside the JWT itself, rather than a request
|
|
76
|
+
or response body. It belongs here for a stronger reason than the rest: identity is the only
|
|
77
|
+
service that _issues_ a token and every other service _verifies_ one, so both sides must agree
|
|
78
|
+
claim for claim. `conduit-api` kept a hand-copied duplicate until the `pt` claim had to be added
|
|
79
|
+
to both at once — the drift a copy invites, arriving exactly when it is most expensive.
|
|
74
80
|
|
|
75
81
|
Auth only. Identity also exposes tenant, user, membership, role and API-key routes; those have
|
|
76
82
|
one consumer each and stay in the service until a second one appears.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { UserPermission } from '../../enum/user-permission.enum.js';
|
|
2
|
+
import type { UUID } from '../../type/uuid.type.js';
|
|
3
|
+
import type { PrincipalType } from './principal-type.enum.js';
|
|
4
|
+
/**
|
|
5
|
+
* Claims carried by an access token.
|
|
6
|
+
*
|
|
7
|
+
* **`iveri-identity-api` issues these; every other service only verifies them.** The shape is
|
|
8
|
+
* therefore a contract owned by identity, and it lives here rather than in either repo because
|
|
9
|
+
* both sides have to agree byte for byte — a service whose idea of these claims has drifted
|
|
10
|
+
* does not fail loudly, it rejects every request or, worse, reads the wrong field and keeps
|
|
11
|
+
* going. `conduit-api` carried a hand-copied duplicate of this interface until the `pt` claim
|
|
12
|
+
* had to be added to both at once, which is exactly the drift the copy invited.
|
|
13
|
+
*
|
|
14
|
+
* Names are short and non-obvious (`tid`, `perms`) because this is JWT convention and because
|
|
15
|
+
* the token travels on every request — the registered claims are three characters for the same
|
|
16
|
+
* reason.
|
|
17
|
+
*
|
|
18
|
+
* **Permissions are embedded rather than resolved per request.** That is the whole point of a
|
|
19
|
+
* JWT here: any Iveri service verifies a token with the shared secret and never calls identity
|
|
20
|
+
* on the hot path. The cost is staleness — revoking a role does not invalidate access tokens
|
|
21
|
+
* already issued, so a removed permission survives up to identity's `JWT_ACCESS_TOKEN_TTL`
|
|
22
|
+
* (15 minutes by default), or `JWT_SERVICE_TOKEN_TTL` for a service token. Anything needing
|
|
23
|
+
* immediate revocation must check state at the point of use, not rely on the token.
|
|
24
|
+
*/
|
|
25
|
+
export interface AccessTokenPayload {
|
|
26
|
+
/**
|
|
27
|
+
* Subject — **the principal**, which is a user id or an API key id depending on {@link pt}.
|
|
28
|
+
*
|
|
29
|
+
* Read it together with `pt` or not at all. Treating it as a user id unconditionally is the
|
|
30
|
+
* one mistake this shape is arranged to prevent.
|
|
31
|
+
*/
|
|
32
|
+
sub: UUID;
|
|
33
|
+
/**
|
|
34
|
+
* Which kind of principal `sub` names.
|
|
35
|
+
*
|
|
36
|
+
* Required, so adding the service-token path could not silently leave a consumer reading a
|
|
37
|
+
* machine's `sub` as a person's id.
|
|
38
|
+
*/
|
|
39
|
+
pt: PrincipalType;
|
|
40
|
+
/** Tenant the token is scoped to. Becomes `RequestContext.tenantId`; never trusted from a header. */
|
|
41
|
+
tid: UUID;
|
|
42
|
+
/** Permissions resolved from the principal's roles — or from the API key's grant — at issue time. */
|
|
43
|
+
perms: UserPermission[];
|
|
44
|
+
/** Issued-at, seconds since epoch. Set by the signer. */
|
|
45
|
+
iat?: number;
|
|
46
|
+
/** Expiry, seconds since epoch. Set by the signer. */
|
|
47
|
+
exp?: number;
|
|
48
|
+
/** Issuer, verified on every decode so a token from another Iveri environment is rejected. */
|
|
49
|
+
iss?: string;
|
|
50
|
+
/** Audience, verified alongside `iss`. */
|
|
51
|
+
aud?: string;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=access-token-payload.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token-payload.interface.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/access-token-payload.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,IAAI,CAAC;IAEV;;;;;OAKG;IACH,EAAE,EAAE,aAAa,CAAC;IAElB,qGAAqG;IACrG,GAAG,EAAE,IAAI,CAAC;IAEV,qGAAqG;IACrG,KAAK,EAAE,cAAc,EAAE,CAAC;IAExB,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,8FAA8F;IAC9F,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token-payload.interface.js","sourceRoot":"","sources":["../../../../src/api/identity/access-token-payload.interface.ts"],"names":[],"mappings":""}
|
|
@@ -51,6 +51,27 @@ export interface AuthTokens {
|
|
|
51
51
|
/** Lifetime of `accessToken` in seconds, not of `refreshToken`. */
|
|
52
52
|
expiresInSeconds: number;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Returned by `POST /auth/token` — the API-key-for-JWT exchange a service makes so that other
|
|
56
|
+
* Iveri services can authorize it **without calling identity**.
|
|
57
|
+
*
|
|
58
|
+
* **There is deliberately no refresh token.** The API key already is the long-lived credential,
|
|
59
|
+
* and it is revocable by a row. A refresh token here would be a second long-lived credential
|
|
60
|
+
* with its own independent lifetime, rotation rule and leak story, obtained from the first —
|
|
61
|
+
* so revoking the key would no longer be enough to stop the caller. A service re-exchanges
|
|
62
|
+
* instead, which costs one call per {@link expiresInSeconds}.
|
|
63
|
+
*
|
|
64
|
+
* The key travels as `x-api-key`, so this call has no request body.
|
|
65
|
+
*/
|
|
66
|
+
export interface ServiceToken {
|
|
67
|
+
/** Send as `Authorization: Bearer <accessToken>`. Carries `pt: 'service'`. */
|
|
68
|
+
accessToken: string;
|
|
69
|
+
/**
|
|
70
|
+
* Lifetime in seconds. Shorter than a human session's: a service re-exchanges with nobody
|
|
71
|
+
* waiting, so the revocation window is cheap to shrink.
|
|
72
|
+
*/
|
|
73
|
+
expiresInSeconds: number;
|
|
74
|
+
}
|
|
54
75
|
/**
|
|
55
76
|
* Who the caller is. Returned by `GET /auth/me` and nested inside {@link AuthSession}.
|
|
56
77
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.type.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/auth.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACtB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACvB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3B,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAE3B,qCAAqC;IACrC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,gEAAgE;IAChE,WAAW,EAAE,cAAc,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;CACtB"}
|
|
1
|
+
{"version":3,"file":"auth.type.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/auth.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACtB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACvB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,YAAY;IACzB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3B,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAE3B,qCAAqC;IACrC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,gEAAgE;IAChE,WAAW,EAAE,cAAc,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;CACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC"}
|
|
@@ -14,7 +14,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./access-token-payload.interface.js"), exports);
|
|
17
18
|
__exportStar(require("./auth.type.js"), exports);
|
|
18
19
|
__exportStar(require("./membership-status.enum.js"), exports);
|
|
20
|
+
__exportStar(require("./principal-type.enum.js"), exports);
|
|
19
21
|
__exportStar(require("./tenant-status.enum.js"), exports);
|
|
20
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA+B;AAC/B,8DAA4C;AAC5C,0DAAwC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sEAAoD;AACpD,iDAA+B;AAC/B,8DAA4C;AAC5C,2DAAyC;AACzC,0DAAwC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of principal an access token was minted for.
|
|
3
|
+
*
|
|
4
|
+
* It exists because `sub` alone cannot answer the question. A JWT's subject is *the principal*,
|
|
5
|
+
* and identity issues tokens for two kinds of them: a person who logged in, and a service that
|
|
6
|
+
* exchanged an API key. Both put a UUID in `sub`, and the two UUIDs mean entirely different
|
|
7
|
+
* things — one names a row in `user`, the other a row in `api_key`.
|
|
8
|
+
*
|
|
9
|
+
* A consumer that reads `sub` as a user id without checking this claim attributes a machine's
|
|
10
|
+
* actions to a user who does not exist. That is why every guard branches on it, and why it is
|
|
11
|
+
* required rather than optional: `tsc` then makes the branch impossible to forget.
|
|
12
|
+
*/
|
|
13
|
+
export declare enum PrincipalType {
|
|
14
|
+
/** A person, authenticated by password. `sub` is their `user.id`. */
|
|
15
|
+
USER = "user",
|
|
16
|
+
/** A service, authenticated by API key. `sub` is the `api_key.id`, and there is no user. */
|
|
17
|
+
SERVICE = "service"
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=principal-type.enum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principal-type.enum.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/principal-type.enum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,oBAAY,aAAa;IACrB,qEAAqE;IACrE,IAAI,SAAS;IAEb,4FAA4F;IAC5F,OAAO,YAAY;CACtB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PrincipalType = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* What kind of principal an access token was minted for.
|
|
6
|
+
*
|
|
7
|
+
* It exists because `sub` alone cannot answer the question. A JWT's subject is *the principal*,
|
|
8
|
+
* and identity issues tokens for two kinds of them: a person who logged in, and a service that
|
|
9
|
+
* exchanged an API key. Both put a UUID in `sub`, and the two UUIDs mean entirely different
|
|
10
|
+
* things — one names a row in `user`, the other a row in `api_key`.
|
|
11
|
+
*
|
|
12
|
+
* A consumer that reads `sub` as a user id without checking this claim attributes a machine's
|
|
13
|
+
* actions to a user who does not exist. That is why every guard branches on it, and why it is
|
|
14
|
+
* required rather than optional: `tsc` then makes the branch impossible to forget.
|
|
15
|
+
*/
|
|
16
|
+
var PrincipalType;
|
|
17
|
+
(function (PrincipalType) {
|
|
18
|
+
/** A person, authenticated by password. `sub` is their `user.id`. */
|
|
19
|
+
PrincipalType["USER"] = "user";
|
|
20
|
+
/** A service, authenticated by API key. `sub` is the `api_key.id`, and there is no user. */
|
|
21
|
+
PrincipalType["SERVICE"] = "service";
|
|
22
|
+
})(PrincipalType || (exports.PrincipalType = PrincipalType = {}));
|
|
23
|
+
//# sourceMappingURL=principal-type.enum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principal-type.enum.js","sourceRoot":"","sources":["../../../../src/api/identity/principal-type.enum.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;GAWG;AACH,IAAY,aAMX;AAND,WAAY,aAAa;IACrB,qEAAqE;IACrE,8BAAa,CAAA;IAEb,4FAA4F;IAC5F,oCAAmB,CAAA;AACvB,CAAC,EANW,aAAa,6BAAb,aAAa,QAMxB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { UserPermission } from '../../enum/user-permission.enum.js';
|
|
2
|
+
import type { UUID } from '../../type/uuid.type.js';
|
|
3
|
+
import type { PrincipalType } from './principal-type.enum.js';
|
|
4
|
+
/**
|
|
5
|
+
* Claims carried by an access token.
|
|
6
|
+
*
|
|
7
|
+
* **`iveri-identity-api` issues these; every other service only verifies them.** The shape is
|
|
8
|
+
* therefore a contract owned by identity, and it lives here rather than in either repo because
|
|
9
|
+
* both sides have to agree byte for byte — a service whose idea of these claims has drifted
|
|
10
|
+
* does not fail loudly, it rejects every request or, worse, reads the wrong field and keeps
|
|
11
|
+
* going. `conduit-api` carried a hand-copied duplicate of this interface until the `pt` claim
|
|
12
|
+
* had to be added to both at once, which is exactly the drift the copy invited.
|
|
13
|
+
*
|
|
14
|
+
* Names are short and non-obvious (`tid`, `perms`) because this is JWT convention and because
|
|
15
|
+
* the token travels on every request — the registered claims are three characters for the same
|
|
16
|
+
* reason.
|
|
17
|
+
*
|
|
18
|
+
* **Permissions are embedded rather than resolved per request.** That is the whole point of a
|
|
19
|
+
* JWT here: any Iveri service verifies a token with the shared secret and never calls identity
|
|
20
|
+
* on the hot path. The cost is staleness — revoking a role does not invalidate access tokens
|
|
21
|
+
* already issued, so a removed permission survives up to identity's `JWT_ACCESS_TOKEN_TTL`
|
|
22
|
+
* (15 minutes by default), or `JWT_SERVICE_TOKEN_TTL` for a service token. Anything needing
|
|
23
|
+
* immediate revocation must check state at the point of use, not rely on the token.
|
|
24
|
+
*/
|
|
25
|
+
export interface AccessTokenPayload {
|
|
26
|
+
/**
|
|
27
|
+
* Subject — **the principal**, which is a user id or an API key id depending on {@link pt}.
|
|
28
|
+
*
|
|
29
|
+
* Read it together with `pt` or not at all. Treating it as a user id unconditionally is the
|
|
30
|
+
* one mistake this shape is arranged to prevent.
|
|
31
|
+
*/
|
|
32
|
+
sub: UUID;
|
|
33
|
+
/**
|
|
34
|
+
* Which kind of principal `sub` names.
|
|
35
|
+
*
|
|
36
|
+
* Required, so adding the service-token path could not silently leave a consumer reading a
|
|
37
|
+
* machine's `sub` as a person's id.
|
|
38
|
+
*/
|
|
39
|
+
pt: PrincipalType;
|
|
40
|
+
/** Tenant the token is scoped to. Becomes `RequestContext.tenantId`; never trusted from a header. */
|
|
41
|
+
tid: UUID;
|
|
42
|
+
/** Permissions resolved from the principal's roles — or from the API key's grant — at issue time. */
|
|
43
|
+
perms: UserPermission[];
|
|
44
|
+
/** Issued-at, seconds since epoch. Set by the signer. */
|
|
45
|
+
iat?: number;
|
|
46
|
+
/** Expiry, seconds since epoch. Set by the signer. */
|
|
47
|
+
exp?: number;
|
|
48
|
+
/** Issuer, verified on every decode so a token from another Iveri environment is rejected. */
|
|
49
|
+
iss?: string;
|
|
50
|
+
/** Audience, verified alongside `iss`. */
|
|
51
|
+
aud?: string;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=access-token-payload.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token-payload.interface.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/access-token-payload.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,IAAI,CAAC;IAEV;;;;;OAKG;IACH,EAAE,EAAE,aAAa,CAAC;IAElB,qGAAqG;IACrG,GAAG,EAAE,IAAI,CAAC;IAEV,qGAAqG;IACrG,KAAK,EAAE,cAAc,EAAE,CAAC;IAExB,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,8FAA8F;IAC9F,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-token-payload.interface.js","sourceRoot":"","sources":["../../../../src/api/identity/access-token-payload.interface.ts"],"names":[],"mappings":""}
|
|
@@ -51,6 +51,27 @@ export interface AuthTokens {
|
|
|
51
51
|
/** Lifetime of `accessToken` in seconds, not of `refreshToken`. */
|
|
52
52
|
expiresInSeconds: number;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Returned by `POST /auth/token` — the API-key-for-JWT exchange a service makes so that other
|
|
56
|
+
* Iveri services can authorize it **without calling identity**.
|
|
57
|
+
*
|
|
58
|
+
* **There is deliberately no refresh token.** The API key already is the long-lived credential,
|
|
59
|
+
* and it is revocable by a row. A refresh token here would be a second long-lived credential
|
|
60
|
+
* with its own independent lifetime, rotation rule and leak story, obtained from the first —
|
|
61
|
+
* so revoking the key would no longer be enough to stop the caller. A service re-exchanges
|
|
62
|
+
* instead, which costs one call per {@link expiresInSeconds}.
|
|
63
|
+
*
|
|
64
|
+
* The key travels as `x-api-key`, so this call has no request body.
|
|
65
|
+
*/
|
|
66
|
+
export interface ServiceToken {
|
|
67
|
+
/** Send as `Authorization: Bearer <accessToken>`. Carries `pt: 'service'`. */
|
|
68
|
+
accessToken: string;
|
|
69
|
+
/**
|
|
70
|
+
* Lifetime in seconds. Shorter than a human session's: a service re-exchanges with nobody
|
|
71
|
+
* waiting, so the revocation window is cheap to shrink.
|
|
72
|
+
*/
|
|
73
|
+
expiresInSeconds: number;
|
|
74
|
+
}
|
|
54
75
|
/**
|
|
55
76
|
* Who the caller is. Returned by `GET /auth/me` and nested inside {@link AuthSession}.
|
|
56
77
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.type.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/auth.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACtB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACvB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3B,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAE3B,qCAAqC;IACrC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,gEAAgE;IAChE,WAAW,EAAE,cAAc,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;CACtB"}
|
|
1
|
+
{"version":3,"file":"auth.type.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/auth.type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACtB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC7B,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IACvB,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,YAAY;IACzB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,SAAS;IACtB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IAEvB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAE3B,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAE3B,qCAAqC;IACrC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAE7C,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,gEAAgE;IAChE,WAAW,EAAE,cAAc,EAAE,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;CACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/api/identity/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of principal an access token was minted for.
|
|
3
|
+
*
|
|
4
|
+
* It exists because `sub` alone cannot answer the question. A JWT's subject is *the principal*,
|
|
5
|
+
* and identity issues tokens for two kinds of them: a person who logged in, and a service that
|
|
6
|
+
* exchanged an API key. Both put a UUID in `sub`, and the two UUIDs mean entirely different
|
|
7
|
+
* things — one names a row in `user`, the other a row in `api_key`.
|
|
8
|
+
*
|
|
9
|
+
* A consumer that reads `sub` as a user id without checking this claim attributes a machine's
|
|
10
|
+
* actions to a user who does not exist. That is why every guard branches on it, and why it is
|
|
11
|
+
* required rather than optional: `tsc` then makes the branch impossible to forget.
|
|
12
|
+
*/
|
|
13
|
+
export declare enum PrincipalType {
|
|
14
|
+
/** A person, authenticated by password. `sub` is their `user.id`. */
|
|
15
|
+
USER = "user",
|
|
16
|
+
/** A service, authenticated by API key. `sub` is the `api_key.id`, and there is no user. */
|
|
17
|
+
SERVICE = "service"
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=principal-type.enum.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principal-type.enum.d.ts","sourceRoot":"","sources":["../../../../src/api/identity/principal-type.enum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,oBAAY,aAAa;IACrB,qEAAqE;IACrE,IAAI,SAAS;IAEb,4FAA4F;IAC5F,OAAO,YAAY;CACtB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of principal an access token was minted for.
|
|
3
|
+
*
|
|
4
|
+
* It exists because `sub` alone cannot answer the question. A JWT's subject is *the principal*,
|
|
5
|
+
* and identity issues tokens for two kinds of them: a person who logged in, and a service that
|
|
6
|
+
* exchanged an API key. Both put a UUID in `sub`, and the two UUIDs mean entirely different
|
|
7
|
+
* things — one names a row in `user`, the other a row in `api_key`.
|
|
8
|
+
*
|
|
9
|
+
* A consumer that reads `sub` as a user id without checking this claim attributes a machine's
|
|
10
|
+
* actions to a user who does not exist. That is why every guard branches on it, and why it is
|
|
11
|
+
* required rather than optional: `tsc` then makes the branch impossible to forget.
|
|
12
|
+
*/
|
|
13
|
+
export var PrincipalType;
|
|
14
|
+
(function (PrincipalType) {
|
|
15
|
+
/** A person, authenticated by password. `sub` is their `user.id`. */
|
|
16
|
+
PrincipalType["USER"] = "user";
|
|
17
|
+
/** A service, authenticated by API key. `sub` is the `api_key.id`, and there is no user. */
|
|
18
|
+
PrincipalType["SERVICE"] = "service";
|
|
19
|
+
})(PrincipalType || (PrincipalType = {}));
|
|
20
|
+
//# sourceMappingURL=principal-type.enum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"principal-type.enum.js","sourceRoot":"","sources":["../../../../src/api/identity/principal-type.enum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAN,IAAY,aAMX;AAND,WAAY,aAAa;IACrB,qEAAqE;IACrE,8BAAa,CAAA;IAEb,4FAA4F;IAC5F,oCAAmB,CAAA;AACvB,CAAC,EANW,aAAa,KAAb,aAAa,QAMxB"}
|