@owlmeans/auth-token 0.1.18-rc.30 → 0.1.18-rc.32

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.47
9
+ npx @owlmeans/agent-skills@^0.1.18-rc.49
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/auth-token",
4
- "version": "0.1.18-rc.30",
5
- "generatedAt": "2026-10-03T20:05:07.600Z",
4
+ "version": "0.1.18-rc.32",
5
+ "generatedAt": "2026-10-05T21:03:06.401Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/auth-token
9
9
 
10
10
  **Layer:** Auth shared
11
- **Install:** `"@owlmeans/auth-token": "^0.1.18-rc.30"` in `dependencies`
11
+ **Install:** `"@owlmeans/auth-token": "^0.1.18-rc.32"` in `dependencies`
12
12
 
13
13
  The contract half of long-lived access tokens: the record shape, the route declarations, the
14
14
  format helpers, and one client-side guard that presents a token it was handed. The server half —
@@ -0,0 +1,27 @@
1
+ /** An `Authorization` header taken apart: the scheme lower-cased, the value trimmed. */
2
+ export interface AuthorizationHeader {
3
+ scheme: string;
4
+ value: string;
5
+ }
6
+ /** How an access token is recognised on the wire and shown back to a person. */
7
+ export interface TokenFormatHelper {
8
+ /**
9
+ * Split an `Authorization` header into its scheme and its value.
10
+ *
11
+ * Deliberately NOT `extractAuthToken` from `@owlmeans/auth-common`: that compares the prefix
12
+ * against `type.toUpperCase()`, so it matches `AUTH-TOKEN` and can never match `Bearer` — the
13
+ * exact spelling every third-party client sends. The scheme comes back lower-cased so a caller
14
+ * compares once.
15
+ */
16
+ parseAuthorizationHeader: (header: string | string[] | undefined) => AuthorizationHeader | null;
17
+ /** Whether a value looks like an access token this deployment issued. */
18
+ isAccessToken: (value: string | null | undefined, prefix: string) => boolean;
19
+ /**
20
+ * The half of a token that may be shown again.
21
+ *
22
+ * Prefix plus the first characters of the secret. Enough to tell two tokens apart in a list, and
23
+ * far short of anything that could be replayed.
24
+ */
25
+ displayOf: (token: string, prefix: string) => string;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/format/types.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,wBAAwB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAK,mBAAmB,GAAG,IAAI,CAAA;IAC/F,yEAAyE;IACzE,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAA;IAC5E;;;;;OAKG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;CACrD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/format/types.ts"],"names":[],"mappings":""}
package/build/format.d.ts CHANGED
@@ -1,22 +1,4 @@
1
- /**
2
- * Split an `Authorization` header into its scheme and its value.
3
- *
4
- * Deliberately NOT `extractAuthToken` from `@owlmeans/auth-common`: that compares the prefix
5
- * against `type.toUpperCase()`, so it matches `AUTH-TOKEN` and can never match `Bearer` — the
6
- * exact spelling every third-party client sends. The scheme comes back lower-cased so a caller
7
- * compares once.
8
- */
9
- export declare const parseAuthorizationHeader: (header: string | string[] | undefined) => {
10
- scheme: string;
11
- value: string;
12
- } | null;
13
- /** Whether a value looks like an access token this deployment issued. */
14
- export declare const isAccessToken: (value: string | null | undefined, prefix: string) => boolean;
15
- /**
16
- * The half of a token that may be shown again.
17
- *
18
- * Prefix plus the first characters of the secret. Enough to tell two tokens apart in a list, and
19
- * far short of anything that could be replayed.
20
- */
21
- export declare const displayOf: (token: string, prefix: string) => string;
1
+ import type { TokenFormatHelper } from './format/types.js';
2
+ export declare const createTokenFormatHelper: () => TokenFormatHelper;
3
+ export declare const tokenFormatHelper: TokenFormatHelper;
22
4
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,WAC3B,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KACpC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAatC,CAAA;AAED,yEAAyE;AACzE,eAAO,MAAM,aAAa,UAAW,MAAM,GAAG,IAAI,GAAG,SAAS,UAAU,MAAM,KAAG,OACN,CAAA;AAE3E;;;;;GAKG;AACH,eAAO,MAAM,SAAS,UAAW,MAAM,UAAU,MAAM,KAAG,MAIzD,CAAA"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE/E,eAAO,MAAM,uBAAuB,QAAO,iBA4B1C,CAAA;AAED,eAAO,MAAM,iBAAiB,mBAA4B,CAAA"}
package/build/format.js CHANGED
@@ -1,36 +1,25 @@
1
1
  import { AUTH_TOKEN_DISPLAY_LENGTH } from './consts.js';
2
- /**
3
- * Split an `Authorization` header into its scheme and its value.
4
- *
5
- * Deliberately NOT `extractAuthToken` from `@owlmeans/auth-common`: that compares the prefix
6
- * against `type.toUpperCase()`, so it matches `AUTH-TOKEN` and can never match `Bearer` — the
7
- * exact spelling every third-party client sends. The scheme comes back lower-cased so a caller
8
- * compares once.
9
- */
10
- export const parseAuthorizationHeader = (header) => {
11
- const raw = Array.isArray(header) ? header[0] : header;
12
- if (raw == null)
13
- return null;
14
- const trimmed = raw.trim();
15
- const space = trimmed.indexOf(' ');
16
- if (space < 1)
17
- return null;
18
- const scheme = trimmed.slice(0, space).toLowerCase();
19
- const value = trimmed.slice(space + 1).trim();
20
- if (value.length < 1)
21
- return null;
22
- return { scheme, value };
23
- };
24
- /** Whether a value looks like an access token this deployment issued. */
25
- export const isAccessToken = (value, prefix) => value != null && value.length > prefix.length && value.startsWith(prefix);
26
- /**
27
- * The half of a token that may be shown again.
28
- *
29
- * Prefix plus the first characters of the secret. Enough to tell two tokens apart in a list, and
30
- * far short of anything that could be replayed.
31
- */
32
- export const displayOf = (token, prefix) => {
33
- const secret = token.startsWith(prefix) ? token.slice(prefix.length) : token;
34
- return `${prefix}${secret.slice(0, AUTH_TOKEN_DISPLAY_LENGTH)}`;
2
+ export const createTokenFormatHelper = () => {
3
+ const parseAuthorizationHeader = (header) => {
4
+ const raw = Array.isArray(header) ? header[0] : header;
5
+ if (raw == null)
6
+ return null;
7
+ const trimmed = raw.trim();
8
+ const space = trimmed.indexOf(' ');
9
+ if (space < 1)
10
+ return null;
11
+ const scheme = trimmed.slice(0, space).toLowerCase();
12
+ const value = trimmed.slice(space + 1).trim();
13
+ if (value.length < 1)
14
+ return null;
15
+ return { scheme, value };
16
+ };
17
+ const isAccessToken = (value, prefix) => value != null && value.length > prefix.length && value.startsWith(prefix);
18
+ const displayOf = (token, prefix) => {
19
+ const secret = token.startsWith(prefix) ? token.slice(prefix.length) : token;
20
+ return `${prefix}${secret.slice(0, AUTH_TOKEN_DISPLAY_LENGTH)}`;
21
+ };
22
+ return { parseAuthorizationHeader, isAccessToken, displayOf };
35
23
  };
24
+ export const tokenFormatHelper = createTokenFormatHelper();
36
25
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAEvD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,MAAqC,EACK,EAAE;IAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IACtD,IAAI,GAAG,IAAI,IAAI;QAAE,OAAO,IAAI,CAAA;IAE5B,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAEjC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC,CAAA;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAgC,EAAE,MAAc,EAAW,EAAE,CACzF,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;AAE3E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,MAAc,EAAU,EAAE;IACjE,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAE5E,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,yBAAyB,CAAC,EAAE,CAAA;AACjE,CAAC,CAAA"}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AAGvD,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAsB,EAAE;IAC7D,MAAM,wBAAwB,GAAG,CAC/B,MAAqC,EACT,EAAE;QAC9B,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;QACtD,IAAI,GAAG,IAAI,IAAI;YAAE,OAAO,IAAI,CAAA;QAE5B,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;QAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;QACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAEjC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;IAC1B,CAAC,CAAA;IAED,MAAM,aAAa,GAAG,CAAC,KAAgC,EAAE,MAAc,EAAW,EAAE,CAClF,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IAE3E,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,MAAc,EAAU,EAAE;QAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QAE5E,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,yBAAyB,CAAC,EAAE,CAAA;IACjE,CAAC,CAAA;IAED,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA;AAC/D,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,EAAE,CAAA"}
package/build/index.d.ts CHANGED
@@ -4,4 +4,5 @@ export * from './entrypoints.js';
4
4
  export * from './format.js';
5
5
  export * from './carrier.js';
6
6
  export type * from './types.js';
7
+ export type * from './format/types.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,mBAAmB,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,mBAAmB,YAAY,CAAA;AAC/B,mBAAmB,mBAAmB,CAAA"}
package/build/types.d.ts CHANGED
@@ -45,7 +45,8 @@ export interface AccessTokenRecord extends ResourceRecord {
45
45
  audience?: string[];
46
46
  }
47
47
  /** What a caller may see. The hash never leaves the server. */
48
- export type AccessTokenView = Omit<AccessTokenRecord, 'hash'>;
48
+ export interface AccessTokenView extends Omit<AccessTokenRecord, 'hash'> {
49
+ }
49
50
  export interface CreateAccessToken {
50
51
  name: string;
51
52
  /** A subset of the caller's own scopes. Defaults to all of them. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAA;IACZ,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAA;IACf,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,mFAAmF;IACnF,UAAU,CAAC,EAAE,IAAI,CAAA;IACjB,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,+DAA+D;AAC/D,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAA;AAE7D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAA;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,0BAA0B;IACzC,gHAAgH;IAChH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;IAChD,IAAI,EAAE,kBAAkB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAA;IACtD,MAAM,EAAE,kBAAkB,CAAC;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE,EAAE,iBAAiB,CAAC,CAAA;IAC1E,MAAM,EAAE,kBAAkB,CAAC;QAAE,MAAM,EAAE,iBAAiB,CAAA;KAAE,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1E"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAA;IACZ,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAA;IACf,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,mFAAmF;IACnF,UAAU,CAAC,EAAE,IAAI,CAAA;IACjB,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,+DAA+D;AAC/D,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC;CAAG;AAE3E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAA;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,0BAA0B;IACzC,gHAAgH;IAChH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;IAChD,IAAI,EAAE,kBAAkB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAA;IACtD,MAAM,EAAE,kBAAkB,CAAC;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE,EAAE,iBAAiB,CAAC,CAAA;IAC1E,MAAM,EAAE,kBAAkB,CAAC;QAAE,MAAM,EAAE,iBAAiB,CAAA;KAAE,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/auth-token",
3
- "version": "0.1.18-rc.30",
3
+ "version": "0.1.18-rc.32",
4
4
  "license": "MIT",
5
5
  "description": "Long-lived access tokens (API keys) on the OwlMeans auth rails — the record shape, the token format, the management entrypoints and the client-side carrier guard.",
6
6
  "type": "module",
@@ -23,16 +23,16 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@owlmeans/auth": "^0.1.18-rc.38",
27
- "@owlmeans/context": "^0.1.18-rc.32",
28
- "@owlmeans/entrypoint": "^0.1.18-rc.39",
29
- "@owlmeans/resource": "^0.1.18-rc.38",
30
- "@owlmeans/route": "^0.1.18-rc.33"
26
+ "@owlmeans/auth": "^0.1.18-rc.40",
27
+ "@owlmeans/context": "^0.1.18-rc.34",
28
+ "@owlmeans/entrypoint": "^0.1.18-rc.41",
29
+ "@owlmeans/resource": "^0.1.18-rc.40",
30
+ "@owlmeans/route": "^0.1.18-rc.35"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@owlmeans/dep-config": "workspace:*",
34
- "@types/bun": "^1.4.0",
35
- "ajv": "^8.17.1",
34
+ "@types/bun": "^1.4.2",
35
+ "ajv": "^8.20.0",
36
36
  "nodemon": "^3.1.14",
37
37
  "typescript": "^7.0.2"
38
38
  },
@@ -0,0 +1,27 @@
1
+ /** An `Authorization` header taken apart: the scheme lower-cased, the value trimmed. */
2
+ export interface AuthorizationHeader {
3
+ scheme: string
4
+ value: string
5
+ }
6
+
7
+ /** How an access token is recognised on the wire and shown back to a person. */
8
+ export interface TokenFormatHelper {
9
+ /**
10
+ * Split an `Authorization` header into its scheme and its value.
11
+ *
12
+ * Deliberately NOT `extractAuthToken` from `@owlmeans/auth-common`: that compares the prefix
13
+ * against `type.toUpperCase()`, so it matches `AUTH-TOKEN` and can never match `Bearer` — the
14
+ * exact spelling every third-party client sends. The scheme comes back lower-cased so a caller
15
+ * compares once.
16
+ */
17
+ parseAuthorizationHeader: (header: string | string[] | undefined) => AuthorizationHeader | null
18
+ /** Whether a value looks like an access token this deployment issued. */
19
+ isAccessToken: (value: string | null | undefined, prefix: string) => boolean
20
+ /**
21
+ * The half of a token that may be shown again.
22
+ *
23
+ * Prefix plus the first characters of the secret. Enough to tell two tokens apart in a list, and
24
+ * far short of anything that could be replayed.
25
+ */
26
+ displayOf: (token: string, prefix: string) => string
27
+ }
package/src/format.ts CHANGED
@@ -1,42 +1,34 @@
1
1
  import { AUTH_TOKEN_DISPLAY_LENGTH } from './consts.js'
2
+ import type { AuthorizationHeader, TokenFormatHelper } from './format/types.js'
2
3
 
3
- /**
4
- * Split an `Authorization` header into its scheme and its value.
5
- *
6
- * Deliberately NOT `extractAuthToken` from `@owlmeans/auth-common`: that compares the prefix
7
- * against `type.toUpperCase()`, so it matches `AUTH-TOKEN` and can never match `Bearer` — the
8
- * exact spelling every third-party client sends. The scheme comes back lower-cased so a caller
9
- * compares once.
10
- */
11
- export const parseAuthorizationHeader = (
12
- header: string | string[] | undefined
13
- ): { scheme: string, value: string } | null => {
14
- const raw = Array.isArray(header) ? header[0] : header
15
- if (raw == null) return null
16
-
17
- const trimmed = raw.trim()
18
- const space = trimmed.indexOf(' ')
19
- if (space < 1) return null
20
-
21
- const scheme = trimmed.slice(0, space).toLowerCase()
22
- const value = trimmed.slice(space + 1).trim()
23
- if (value.length < 1) return null
24
-
25
- return { scheme, value }
26
- }
4
+ export const createTokenFormatHelper = (): TokenFormatHelper => {
5
+ const parseAuthorizationHeader = (
6
+ header: string | string[] | undefined
7
+ ): AuthorizationHeader | null => {
8
+ const raw = Array.isArray(header) ? header[0] : header
9
+ if (raw == null) return null
10
+
11
+ const trimmed = raw.trim()
12
+ const space = trimmed.indexOf(' ')
13
+ if (space < 1) return null
14
+
15
+ const scheme = trimmed.slice(0, space).toLowerCase()
16
+ const value = trimmed.slice(space + 1).trim()
17
+ if (value.length < 1) return null
27
18
 
28
- /** Whether a value looks like an access token this deployment issued. */
29
- export const isAccessToken = (value: string | null | undefined, prefix: string): boolean =>
30
- value != null && value.length > prefix.length && value.startsWith(prefix)
19
+ return { scheme, value }
20
+ }
31
21
 
32
- /**
33
- * The half of a token that may be shown again.
34
- *
35
- * Prefix plus the first characters of the secret. Enough to tell two tokens apart in a list, and
36
- * far short of anything that could be replayed.
37
- */
38
- export const displayOf = (token: string, prefix: string): string => {
39
- const secret = token.startsWith(prefix) ? token.slice(prefix.length) : token
22
+ const isAccessToken = (value: string | null | undefined, prefix: string): boolean =>
23
+ value != null && value.length > prefix.length && value.startsWith(prefix)
40
24
 
41
- return `${prefix}${secret.slice(0, AUTH_TOKEN_DISPLAY_LENGTH)}`
25
+ const displayOf = (token: string, prefix: string): string => {
26
+ const secret = token.startsWith(prefix) ? token.slice(prefix.length) : token
27
+
28
+ return `${prefix}${secret.slice(0, AUTH_TOKEN_DISPLAY_LENGTH)}`
29
+ }
30
+
31
+ return { parseAuthorizationHeader, isAccessToken, displayOf }
42
32
  }
33
+
34
+ export const tokenFormatHelper = createTokenFormatHelper()
package/src/index.ts CHANGED
@@ -4,3 +4,4 @@ export * from './entrypoints.js'
4
4
  export * from './format.js'
5
5
  export * from './carrier.js'
6
6
  export type * from './types.js'
7
+ export type * from './format/types.js'
package/src/types.ts CHANGED
@@ -47,7 +47,7 @@ export interface AccessTokenRecord extends ResourceRecord {
47
47
  }
48
48
 
49
49
  /** What a caller may see. The hash never leaves the server. */
50
- export type AccessTokenView = Omit<AccessTokenRecord, 'hash'>
50
+ export interface AccessTokenView extends Omit<AccessTokenRecord, 'hash'> {}
51
51
 
52
52
  export interface CreateAccessToken {
53
53
  name: string
@@ -1,43 +1,43 @@
1
1
  import { describe, expect, test } from 'bun:test'
2
- import { displayOf, isAccessToken, parseAuthorizationHeader } from '../src/format.js'
2
+ import { tokenFormatHelper } from '../src/format.js'
3
3
  import { AUTH_TOKEN_DEFAULT_PREFIX } from '../src/consts.js'
4
4
 
5
5
  describe('@owlmeans/auth-token — Authorization parsing', () => {
6
6
  test('lower-cases the scheme so Bearer and BEARER are one answer', () => {
7
- expect(parseAuthorizationHeader('Bearer owl_abc')).toEqual({ scheme: 'bearer', value: 'owl_abc' })
8
- expect(parseAuthorizationHeader('BEARER owl_abc')?.scheme).toBe('bearer')
9
- expect(parseAuthorizationHeader('AUTH-TOKEN owl_abc')?.scheme).toBe('auth-token')
7
+ expect(tokenFormatHelper.parseAuthorizationHeader('Bearer owl_abc')).toEqual({ scheme: 'bearer', value: 'owl_abc' })
8
+ expect(tokenFormatHelper.parseAuthorizationHeader('BEARER owl_abc')?.scheme).toBe('bearer')
9
+ expect(tokenFormatHelper.parseAuthorizationHeader('AUTH-TOKEN owl_abc')?.scheme).toBe('auth-token')
10
10
  })
11
11
 
12
12
  test('takes the first header when a proxy folded several', () => {
13
- expect(parseAuthorizationHeader(['Bearer one', 'Bearer two'])?.value).toBe('one')
13
+ expect(tokenFormatHelper.parseAuthorizationHeader(['Bearer one', 'Bearer two'])?.value).toBe('one')
14
14
  })
15
15
 
16
16
  test('answers null for anything that is not a scheme and a value', () => {
17
- expect(parseAuthorizationHeader(undefined)).toBeNull()
18
- expect(parseAuthorizationHeader('')).toBeNull()
19
- expect(parseAuthorizationHeader('Bearer')).toBeNull()
20
- expect(parseAuthorizationHeader('Bearer ')).toBeNull()
21
- expect(parseAuthorizationHeader(' leadingspace')).toBeNull()
17
+ expect(tokenFormatHelper.parseAuthorizationHeader(undefined)).toBeNull()
18
+ expect(tokenFormatHelper.parseAuthorizationHeader('')).toBeNull()
19
+ expect(tokenFormatHelper.parseAuthorizationHeader('Bearer')).toBeNull()
20
+ expect(tokenFormatHelper.parseAuthorizationHeader('Bearer ')).toBeNull()
21
+ expect(tokenFormatHelper.parseAuthorizationHeader(' leadingspace')).toBeNull()
22
22
  })
23
23
 
24
24
  test('keeps a value that itself contains spaces', () => {
25
- expect(parseAuthorizationHeader('Bearer a b c')?.value).toBe('a b c')
25
+ expect(tokenFormatHelper.parseAuthorizationHeader('Bearer a b c')?.value).toBe('a b c')
26
26
  })
27
27
  })
28
28
 
29
29
  describe('@owlmeans/auth-token — token shape', () => {
30
30
  test('claims only a value carrying the prefix', () => {
31
- expect(isAccessToken('owl_abcdef', AUTH_TOKEN_DEFAULT_PREFIX)).toBe(true)
32
- expect(isAccessToken('vib_abcdef', AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
33
- expect(isAccessToken('abcdef', AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
31
+ expect(tokenFormatHelper.isAccessToken('owl_abcdef', AUTH_TOKEN_DEFAULT_PREFIX)).toBe(true)
32
+ expect(tokenFormatHelper.isAccessToken('vib_abcdef', AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
33
+ expect(tokenFormatHelper.isAccessToken('abcdef', AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
34
34
  // The prefix alone is not a token — there is no secret behind it.
35
- expect(isAccessToken(AUTH_TOKEN_DEFAULT_PREFIX, AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
36
- expect(isAccessToken(null, AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
35
+ expect(tokenFormatHelper.isAccessToken(AUTH_TOKEN_DEFAULT_PREFIX, AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
36
+ expect(tokenFormatHelper.isAccessToken(null, AUTH_TOKEN_DEFAULT_PREFIX)).toBe(false)
37
37
  })
38
38
 
39
39
  test('the display form keeps the prefix and eight characters of the secret', () => {
40
- const display = displayOf('owl_ABCDEFGHIJKLMNOP', 'owl_')
40
+ const display = tokenFormatHelper.displayOf('owl_ABCDEFGHIJKLMNOP', 'owl_')
41
41
  expect(display).toBe('owl_ABCDEFGH')
42
42
  // Short enough to be useless, long enough to tell two of a person's tokens apart.
43
43
  expect(display.length).toBeLessThan('owl_ABCDEFGHIJKLMNOP'.length)