@ultimat3/auth 25.0.0 → 25.1.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 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. This package can read the
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.0.0",
3
+ "version": "25.1.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.0.0",
36
- "@ultimat3/db": "25.0.0",
37
- "@ultimat3/schema": "25.0.0"
35
+ "@ultimat3/core": "25.1.0",
36
+ "@ultimat3/db": "25.1.0",
37
+ "@ultimat3/schema": "25.1.0"
38
38
  }
39
39
  }
@@ -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 { agentActor, anonymousActor, assertNever, serviceActor, userActor } from '@ultimat3/core';
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) => covers(grant, scope))),
100
+ (ownerGrants === null || ownerGrants.some((grant) => grantCovers(grant, scope))),
103
101
  );
104
102
  }
105
103