@ultimat3/auth 25.0.0 → 25.2.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/CLAUDE.md +1 -1
- package/README.md +3 -1
- package/package.json +4 -4
- package/src/policy-bridge.ts +9 -11
package/CLAUDE.md
CHANGED
|
@@ -167,7 +167,7 @@ Tier 2. Produces the `Actor`; produces nothing else. Authorization is `@ultimat3
|
|
|
167
167
|
keys beside their sessions, and logs before its first write.
|
|
168
168
|
- **A key's scopes only ever shrink** (`apiKeyScopes`, `policy-bridge.ts`): `*` and `<res>:*` are
|
|
169
169
|
refused at `issueApiKey` (`X_CONFIG_INVALID`) and dropped from a stored row; an OWNED key keeps
|
|
170
|
-
only what its owner's grants cover. Roles are policy's to expand, so the default grants are the
|
|
170
|
+
only what its owner's grants cover (core's `grantCovers`, never a local copy). Roles are policy's to expand, so the default grants are the
|
|
171
171
|
row's `permissions` + `scopes` (`directGrants`) and a role-based app passes
|
|
172
172
|
`apiKeyResolver(store, { grantsOf })`. Never union a key with the owner's roles.
|
|
173
173
|
- **`apiKeyResolver(() => store)` is the ONE token → caller mapping** (`api-key-resolver.ts`):
|
package/README.md
CHANGED
|
@@ -796,7 +796,9 @@ default set:
|
|
|
796
796
|
|
|
797
797
|
- `*` and `<resource>:*` are refused at `issueApiKey` (`X_CONFIG_INVALID`), and dropped from a row
|
|
798
798
|
that already holds one.
|
|
799
|
-
- A key a user owns keeps only the scopes that owner's grants cover
|
|
799
|
+
- A key a user owns keeps only the scopes that owner's grants cover — `@ultimat3/core`'s
|
|
800
|
+
`grantCovers`, the reading `can()` uses: an owner with `billing:invoice:*` covers
|
|
801
|
+
`billing:invoice:read`, never `billing:refund:issue`. This package can read the
|
|
800
802
|
row's `permissions` and `scopes` (`directGrants`) and cannot expand a role — that is
|
|
801
803
|
`@ultimat3/policy`'s — so an app whose users hold roles says what they grant:
|
|
802
804
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/auth",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.2.0",
|
|
4
4
|
"description": "Sessions, passwords, OAuth, MFA and api keys — resolved to one Actor",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"test": "bun test"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ultimat3/core": "25.
|
|
36
|
-
"@ultimat3/db": "25.
|
|
37
|
-
"@ultimat3/schema": "25.
|
|
35
|
+
"@ultimat3/core": "25.2.0",
|
|
36
|
+
"@ultimat3/db": "25.2.0",
|
|
37
|
+
"@ultimat3/schema": "25.2.0"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/src/policy-bridge.ts
CHANGED
|
@@ -5,7 +5,14 @@
|
|
|
5
5
|
// import a same-tier package; policy binds to it by structure, in whichever order they land.
|
|
6
6
|
|
|
7
7
|
import type { Actor } from '@ultimat3/core';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
agentActor,
|
|
10
|
+
anonymousActor,
|
|
11
|
+
assertNever,
|
|
12
|
+
grantCovers,
|
|
13
|
+
serviceActor,
|
|
14
|
+
userActor,
|
|
15
|
+
} from '@ultimat3/core';
|
|
9
16
|
import type { AuthApiKeyRecord, AuthSession, AuthUser } from './adapter';
|
|
10
17
|
|
|
11
18
|
/** Structural mirror of `@ultimat3/policy`'s `PolicyActorFields`. Kept in sync by hand. */
|
|
@@ -72,15 +79,6 @@ export function actorFromUser(user: AuthUser, session: AuthSession): PolicyActor
|
|
|
72
79
|
/** `*` and `<resource>:*` — the two spellings a role uses to be granted everything. */
|
|
73
80
|
export const isWildcardScope = (scope: string): boolean => scope === '*' || scope.endsWith(':*');
|
|
74
81
|
|
|
75
|
-
// `@ultimat3/policy`'s own reading of a grant's resource, mirrored: same tier, so not imported.
|
|
76
|
-
const resourceOf = (grant: string): string => grant.split(':')[0] ?? grant;
|
|
77
|
-
|
|
78
|
-
/** Whether one of the owner's grants reaches a named permission. A grant MAY be a wildcard. */
|
|
79
|
-
const covers = (grant: string, wanted: string): boolean =>
|
|
80
|
-
grant === '*' ||
|
|
81
|
-
grant === wanted ||
|
|
82
|
-
(grant.endsWith(':*') && resourceOf(grant) === resourceOf(wanted));
|
|
83
|
-
|
|
84
82
|
/**
|
|
85
83
|
* What a key may carry of what it names. Two cuts, and both only ever remove:
|
|
86
84
|
*
|
|
@@ -99,7 +97,7 @@ export function apiKeyScopes(
|
|
|
99
97
|
return scopes.filter(
|
|
100
98
|
(scope) =>
|
|
101
99
|
!isWildcardScope(scope) &&
|
|
102
|
-
(ownerGrants === null || ownerGrants.some((grant) =>
|
|
100
|
+
(ownerGrants === null || ownerGrants.some((grant) => grantCovers(grant, scope))),
|
|
103
101
|
);
|
|
104
102
|
}
|
|
105
103
|
|