@owlmeans/server-auth-token 0.1.18-rc.29 → 0.1.18-rc.30

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 CHANGED
@@ -6,7 +6,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
6
6
  your project's skill store (`.agents/skills/`):
7
7
 
8
8
  ```sh
9
- npx @owlmeans/agent-skills@^0.1.18-rc.46
9
+ npx @owlmeans/agent-skills@^0.1.18-rc.47
10
10
  ```
11
11
 
12
12
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/server-auth-token",
4
- "version": "0.1.18-rc.29",
5
- "generatedAt": "2026-10-03T11:19:50.802Z",
4
+ "version": "0.1.18-rc.30",
5
+ "generatedAt": "2026-10-03T20:05:07.610Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/server-auth-token
9
9
 
10
10
  **Layer:** Server
11
- **Install:** `"@owlmeans/server-auth-token": "^0.1.18-rc.29"` in `dependencies`
11
+ **Install:** `"@owlmeans/server-auth-token": "^0.1.18-rc.30"` in `dependencies`
12
12
  **Contracts:** `@owlmeans/auth-token` — the record, the routes, the format helpers
13
13
 
14
14
  ## Key Exports
package/build/guard.d.ts CHANGED
@@ -2,23 +2,5 @@ import type { GuardService } from '@owlmeans/entrypoint';
2
2
  import type { AuthTokenContext, AuthTokenGuardOptions } from './types.js';
3
3
  /** Where the deployment's prefix comes from: the guard's own options, then config, then default. */
4
4
  export declare const prefixOf: (context: AuthTokenContext, opts?: AuthTokenGuardOptions) => string;
5
- /**
6
- * The guard that verifies a long-lived access token.
7
- *
8
- * It is a GUARD, not an authentication method: nothing is exchanged, no envelope is signed, no
9
- * session begins. A caller presents a credential it was handed once, and the guard turns it into
10
- * the same `Auth` payload every other guard produces — so ownership gates, entitlement gates and
11
- * handlers written against a browser session work unchanged for an API client.
12
- *
13
- * Three properties are what keep it from colliding with the guards it sits beside:
14
- *
15
- * - it claims a header only when the value carries the deployment's prefix, so an OwlMeans
16
- * `ED25519-BASIC-TOKEN` bearer, an OIDC one, and this one can all arrive under `Authorization`
17
- * without any of them shadowing the others;
18
- * - it accepts the OwlMeans `AUTH-TOKEN` scheme AND plain `Bearer`, because a third-party client
19
- * configured with a URL sends the latter whatever the documentation says;
20
- * - it intersects the token's scopes with its profile's on every request, so a token can never
21
- * outlive or outrank the person who minted it.
22
- */
23
5
  export declare const makeAuthTokenGuard: (alias?: string, opts?: AuthTokenGuardOptions) => GuardService;
24
6
  //# sourceMappingURL=guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAqC,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAI3F,OAAO,KAAK,EAAwC,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAE/G,oGAAoG;AACpG,eAAO,MAAM,QAAQ,YAAa,gBAAgB,SAAS,qBAAqB,KAAG,MACc,CAAA;AAEjG;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,WACtB,MAAM,SAA2B,qBAAqB,KAC5D,YAgHF,CAAA"}
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAqC,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAI3F,OAAO,KAAK,EAAwC,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAG/G,oGAAoG;AACpG,eAAO,MAAM,QAAQ,YAAa,gBAAgB,SAAS,qBAAqB,KAAG,MACc,CAAA;AAmCjG,eAAO,MAAM,kBAAkB,WACtB,MAAM,SAA2B,qBAAqB,KAC5D,YAgHF,CAAA"}
package/build/guard.js CHANGED
@@ -5,6 +5,7 @@ import { ENTITY_RESOLVER } from '@owlmeans/auth-common';
5
5
  import { createService } from '@owlmeans/context';
6
6
  import { AUTH_IDENTITY_PROFILE } from '@owlmeans/server-auth-identity';
7
7
  import { hashAccessToken } from './hash.js';
8
+ import { logger, logThrottle } from '@owlmeans/log';
8
9
  /** Where the deployment's prefix comes from: the guard's own options, then config, then default. */
9
10
  export const prefixOf = (context, opts) => opts?.prefix ?? context.cfg.authToken?.prefix ?? AUTH_TOKEN_DEFAULT_PREFIX;
10
11
  /**
@@ -25,6 +26,19 @@ export const prefixOf = (context, opts) => opts?.prefix ?? context.cfg.authToken
25
26
  * - it intersects the token's scopes with its profile's on every request, so a token can never
26
27
  * outlive or outrank the person who minted it.
27
28
  */
29
+ const log = logger('server-auth-token');
30
+ /**
31
+ * A presented access token the guard did not accept. Never the token, not even its display form:
32
+ * its record id only. Once a minute per token and reason — a client retrying a revoked token calls every second.
33
+ */
34
+ const refuse = (reason, record) => {
35
+ if (logThrottle(`auth-token:${reason}:${record?.id ?? ''}`, 60_000)) {
36
+ log.warn('Access token refused', {
37
+ reason, tokenId: record?.id, entityId: record?.entityId,
38
+ }, { event: 'auth.refused' });
39
+ }
40
+ return false;
41
+ };
28
42
  export const makeAuthTokenGuard = (alias = GUARD_AUTH_TOKEN, opts = {}) => {
29
43
  const touchInterval = opts.touchInterval ?? AUTH_TOKEN_TOUCH_INTERVAL;
30
44
  const present = (req, prefix) => {
@@ -58,11 +72,11 @@ export const makeAuthTokenGuard = (alias = GUARD_AUTH_TOKEN, opts = {}) => {
58
72
  const tokens = context.resource(opts.resourceAlias ?? AUTH_TOKEN_RESOURCE);
59
73
  const record = await tokens.load({ hash: hashAccessToken(presented) });
60
74
  if (record == null)
61
- return false;
75
+ return refuse('unknown');
62
76
  if (record.revokedAt != null)
63
- return false;
77
+ return refuse('revoked', record);
64
78
  if (record.expiresAt != null && new Date(record.expiresAt) < new Date())
65
- return false;
79
+ return refuse('expired', record);
66
80
  // A token minted through an OAuth grant carries the resource(s) it was issued FOR. A token
67
81
  // with no audience is the ordinary hand-minted kind and is admitted everywhere its scopes
68
82
  // reach, exactly as before this check existed — the guard has to OPT IN with `resources` to
@@ -71,7 +85,7 @@ export const makeAuthTokenGuard = (alias = GUARD_AUTH_TOKEN, opts = {}) => {
71
85
  if (record.audience != null && record.audience.length > 0
72
86
  && resources != null && resources.length > 0
73
87
  && !record.audience.some(resource => resources.includes(resource))) {
74
- return false;
88
+ return refuse('audience', record);
75
89
  }
76
90
  // A token is only ever as good as the profile behind it. Reading the profile per request is
77
91
  // what makes revoking a person's access revoke every token they ever minted, without any
@@ -82,12 +96,12 @@ export const makeAuthTokenGuard = (alias = GUARD_AUTH_TOKEN, opts = {}) => {
82
96
  profile = await profiles.load({ entityId: record.entityId, profileId: record.profileId });
83
97
  }
84
98
  catch {
85
- return false;
99
+ return refuse('profile-unreadable', record);
86
100
  }
87
101
  if (profile == null)
88
- return false;
102
+ return refuse('profile', record);
89
103
  if (profile.expiresAt != null && new Date(profile.expiresAt) < new Date())
90
- return false;
104
+ return refuse('profile-expired', record);
91
105
  const profileScopes = profile.scopes ?? [];
92
106
  const scopes = profileScopes.includes(ALL_SCOPES)
93
107
  ? record.scopes
@@ -124,7 +138,7 @@ export const makeAuthTokenGuard = (alias = GUARD_AUTH_TOKEN, opts = {}) => {
124
138
  // Fire and forget: a usage timestamp is never a reason to fail a request, and awaiting it
125
139
  // would put a database write on the critical path of every authenticated call.
126
140
  void tokens.save({ ...record, lastUsedAt: new Date() })
127
- .catch((e) => console.error('auth-token: lastUsedAt', e));
141
+ .catch((e) => { log.warn('Access token lastUsedAt not saved', { tokenId: record.id, error: e }); });
128
142
  }
129
143
  return true;
130
144
  },
@@ -1 +1 @@
1
- {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-auth-token",
3
- "version": "0.1.18-rc.29",
3
+ "version": "0.1.18-rc.30",
4
4
  "license": "MIT",
5
5
  "description": "The server half of OwlMeans access tokens — the store, the guard that verifies a presented token, the management handlers and the coguard that admits one on an existing route.",
6
6
  "type": "module",
@@ -25,15 +25,16 @@
25
25
  "dependencies": {
26
26
  "@noble/hashes": "^1.5.0",
27
27
  "@owlmeans/auth": "^0.1.18-rc.38",
28
- "@owlmeans/auth-common": "^0.1.18-rc.44",
29
- "@owlmeans/auth-token": "^0.1.18-rc.29",
28
+ "@owlmeans/auth-common": "^0.1.18-rc.45",
29
+ "@owlmeans/auth-token": "^0.1.18-rc.30",
30
30
  "@owlmeans/context": "^0.1.18-rc.32",
31
31
  "@owlmeans/entrypoint": "^0.1.18-rc.39",
32
- "@owlmeans/mongo-resource": "^0.1.18-rc.40",
33
- "@owlmeans/resource": "^0.1.18-rc.37",
34
- "@owlmeans/server-api": "^0.1.18-rc.48",
35
- "@owlmeans/server-auth-identity": "^0.1.18-rc.44",
36
- "@owlmeans/server-context": "^0.1.18-rc.40",
32
+ "@owlmeans/log": "^0.1.18-rc.0",
33
+ "@owlmeans/mongo-resource": "^0.1.18-rc.41",
34
+ "@owlmeans/resource": "^0.1.18-rc.38",
35
+ "@owlmeans/server-api": "^0.1.18-rc.49",
36
+ "@owlmeans/server-auth-identity": "^0.1.18-rc.45",
37
+ "@owlmeans/server-context": "^0.1.18-rc.41",
37
38
  "@scure/base": "^1.1.9"
38
39
  },
39
40
  "devDependencies": {
package/src/guard.ts CHANGED
@@ -13,6 +13,7 @@ import { AUTH_IDENTITY_PROFILE } from '@owlmeans/server-auth-identity'
13
13
  import type { IdentityProfile, IdentityProfileResource } from '@owlmeans/server-auth-identity'
14
14
  import { hashAccessToken } from './hash.js'
15
15
  import type { AccessTokenResource, AuthTokenConfig, AuthTokenContext, AuthTokenGuardOptions } from './types.js'
16
+ import { logger, logThrottle } from '@owlmeans/log'
16
17
 
17
18
  /** Where the deployment's prefix comes from: the guard's own options, then config, then default. */
18
19
  export const prefixOf = (context: AuthTokenContext, opts?: AuthTokenGuardOptions): string =>
@@ -36,6 +37,21 @@ export const prefixOf = (context: AuthTokenContext, opts?: AuthTokenGuardOptions
36
37
  * - it intersects the token's scopes with its profile's on every request, so a token can never
37
38
  * outlive or outrank the person who minted it.
38
39
  */
40
+ const log = logger('server-auth-token')
41
+
42
+ /**
43
+ * A presented access token the guard did not accept. Never the token, not even its display form:
44
+ * its record id only. Once a minute per token and reason — a client retrying a revoked token calls every second.
45
+ */
46
+ const refuse = (reason: string, record?: { id?: string, entityId?: string }): false => {
47
+ if (logThrottle(`auth-token:${reason}:${record?.id ?? ''}`, 60_000)) {
48
+ log.warn('Access token refused', {
49
+ reason, tokenId: record?.id, entityId: record?.entityId,
50
+ }, { event: 'auth.refused' })
51
+ }
52
+ return false
53
+ }
54
+
39
55
  export const makeAuthTokenGuard = (
40
56
  alias: string = GUARD_AUTH_TOKEN, opts: AuthTokenGuardOptions = {}
41
57
  ): GuardService => {
@@ -72,9 +88,9 @@ export const makeAuthTokenGuard = (
72
88
 
73
89
  const tokens = context.resource<AccessTokenResource>(opts.resourceAlias ?? AUTH_TOKEN_RESOURCE)
74
90
  const record = await tokens.load({ hash: hashAccessToken(presented) })
75
- if (record == null) return false as T
76
- if (record.revokedAt != null) return false as T
77
- if (record.expiresAt != null && new Date(record.expiresAt) < new Date()) return false as T
91
+ if (record == null) return refuse('unknown') as T
92
+ if (record.revokedAt != null) return refuse('revoked', record) as T
93
+ if (record.expiresAt != null && new Date(record.expiresAt) < new Date()) return refuse('expired', record) as T
78
94
  // A token minted through an OAuth grant carries the resource(s) it was issued FOR. A token
79
95
  // with no audience is the ordinary hand-minted kind and is admitted everywhere its scopes
80
96
  // reach, exactly as before this check existed — the guard has to OPT IN with `resources` to
@@ -83,7 +99,7 @@ export const makeAuthTokenGuard = (
83
99
  if (record.audience != null && record.audience.length > 0
84
100
  && resources != null && resources.length > 0
85
101
  && !record.audience.some(resource => resources.includes(resource))) {
86
- return false as T
102
+ return refuse('audience', record) as T
87
103
  }
88
104
 
89
105
  // A token is only ever as good as the profile behind it. Reading the profile per request is
@@ -96,10 +112,10 @@ export const makeAuthTokenGuard = (
96
112
  try {
97
113
  profile = await profiles.load({ entityId: record.entityId, profileId: record.profileId })
98
114
  } catch {
99
- return false as T
115
+ return refuse('profile-unreadable', record) as T
100
116
  }
101
- if (profile == null) return false as T
102
- if (profile.expiresAt != null && new Date(profile.expiresAt) < new Date()) return false as T
117
+ if (profile == null) return refuse('profile', record) as T
118
+ if (profile.expiresAt != null && new Date(profile.expiresAt) < new Date()) return refuse('profile-expired', record) as T
103
119
 
104
120
  const profileScopes = profile.scopes ?? []
105
121
  const scopes = profileScopes.includes(ALL_SCOPES)
@@ -139,7 +155,7 @@ export const makeAuthTokenGuard = (
139
155
  // Fire and forget: a usage timestamp is never a reason to fail a request, and awaiting it
140
156
  // would put a database write on the critical path of every authenticated call.
141
157
  void tokens.save({ ...record, lastUsedAt: new Date() })
142
- .catch((e: unknown) => console.error('auth-token: lastUsedAt', e))
158
+ .catch((e: unknown) => { log.warn('Access token lastUsedAt not saved', { tokenId: record.id, error: e }) })
143
159
  }
144
160
 
145
161
  return true as T