@owlmeans/server-auth-token 0.1.18-rc.1 → 0.1.18-rc.11
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 +1 -1
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-auth-token/SKILL.md +5 -5
- package/build/coguard.d.ts +2 -3
- package/build/coguard.d.ts.map +1 -1
- package/build/coguard.js +6 -9
- package/build/coguard.js.map +1 -1
- package/build/handlers/index.d.ts +11 -3
- package/build/handlers/index.d.ts.map +1 -1
- package/build/handlers/index.js +4 -4
- package/build/handlers/index.js.map +1 -1
- package/package.json +11 -11
- package/src/coguard.ts +9 -9
- package/src/handlers/index.ts +6 -6
- package/tests/coguard.spec.ts +19 -21
- package/tests/handlers.spec.ts +14 -6
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.
|
|
9
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.23
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/server-auth-token",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"version": "0.1.18-rc.11",
|
|
5
|
+
"generatedAt": "2026-09-15T12:50:32.651Z",
|
|
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.
|
|
11
|
+
**Install:** `"@owlmeans/server-auth-token": "^0.1.18-rc.11"` in `dependencies`
|
|
12
12
|
**Contracts:** `@owlmeans/auth-token` — the record, the routes, the format helpers
|
|
13
13
|
|
|
14
14
|
## Key Exports
|
|
@@ -21,7 +21,7 @@ user-invocable: false
|
|
|
21
21
|
| `makeAccessTokenResource(dbAlias?)` | The Mongo resource, with its three indexes |
|
|
22
22
|
| `prefixOf(context, opts?)` | The deployment's prefix: guard options → config → default |
|
|
23
23
|
| `hashAccessToken(token)` · `mintAccessToken(prefix)` | The stored form; one minted `{ token, hash, display }` |
|
|
24
|
-
| `
|
|
24
|
+
| `withAuthTokenCoguard(protocolTree, guard?)` | Return the same-shaped immutable tree with the guard appended to every already-guarded protocol |
|
|
25
25
|
| `listAccessTokens` · `createAccessToken` · `revokeAccessToken` | The three handlers |
|
|
26
26
|
| `AuthTokenGuardOptions` (`prefix`, `denyAliases`, `touchInterval`, `resourceAlias`, `profileAlias`) · `AuthTokenConfig` · `AccessTokenResource` | Types |
|
|
27
27
|
|
|
@@ -99,7 +99,7 @@ tokens that are not theirs.
|
|
|
99
99
|
## The coguard admits a token everywhere, and the deny list lives in the GUARD
|
|
100
100
|
|
|
101
101
|
```typescript
|
|
102
|
-
|
|
102
|
+
const guardedManagerProtocols = withAuthTokenCoguard(managerProtocols)
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
An API client drives the same surface a browser does — projects, stories, files — so admitting the
|
|
@@ -108,8 +108,8 @@ never substitutes: the primary guard stays first and still claims its own creden
|
|
|
108
108
|
matches only a value carrying the prefix. It skips entrypoints that have no guard, and it is
|
|
109
109
|
idempotent.
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
Decorate the declaration tree before bindings are created. The helper preserves the input tree's
|
|
112
|
+
shape, leaves unguarded protocols unchanged, and is idempotent.
|
|
113
113
|
|
|
114
114
|
Routes that must stay behind an interactive session — opening a checkout, starting an OAuth flow,
|
|
115
115
|
minting another token — are named in `AuthTokenGuardOptions.denyAliases` rather than skipped here.
|
package/build/coguard.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EntrypointTree } from '@owlmeans/entrypoint';
|
|
1
2
|
/**
|
|
2
3
|
* Let an access token authenticate every route that already has a guard.
|
|
3
4
|
*
|
|
@@ -10,7 +11,5 @@
|
|
|
10
11
|
* than skipped here — a child entrypoint inherits every guard its ancestors declare and cannot
|
|
11
12
|
* drop one, so the refusal has to live where the credential is inspected.
|
|
12
13
|
*/
|
|
13
|
-
export declare const
|
|
14
|
-
guards?: string[];
|
|
15
|
-
}>, guard?: string) => void;
|
|
14
|
+
export declare const withAuthTokenCoguard: <Tree extends EntrypointTree>(tree: Tree, guard?: string) => Tree;
|
|
16
15
|
//# sourceMappingURL=coguard.d.ts.map
|
package/build/coguard.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coguard.d.ts","sourceRoot":"","sources":["../src/coguard.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"coguard.d.ts","sourceRoot":"","sources":["../src/coguard.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAAI,IAAI,SAAS,cAAc,QACxD,IAAI,UAAS,MAAM,KACxB,IAID,CAAA"}
|
package/build/coguard.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GUARD_AUTH_TOKEN } from '@owlmeans/auth-token';
|
|
2
|
+
import { decorateEntrypoint, mapProtocols } from '@owlmeans/entrypoint';
|
|
2
3
|
/**
|
|
3
4
|
* Let an access token authenticate every route that already has a guard.
|
|
4
5
|
*
|
|
@@ -11,13 +12,9 @@ import { GUARD_AUTH_TOKEN } from '@owlmeans/auth-token';
|
|
|
11
12
|
* than skipped here — a child entrypoint inherits every guard its ancestors declare and cannot
|
|
12
13
|
* drop one, so the refusal has to live where the credential is inspected.
|
|
13
14
|
*/
|
|
14
|
-
export const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return;
|
|
20
|
-
entrypoint.guards = [...entrypoint.guards, guard];
|
|
21
|
-
});
|
|
22
|
-
};
|
|
15
|
+
export const withAuthTokenCoguard = (tree, guard = GUARD_AUTH_TOKEN) => mapProtocols(tree, declaration => {
|
|
16
|
+
if (declaration.guards.length < 1 || declaration.guards.includes(guard))
|
|
17
|
+
return declaration;
|
|
18
|
+
return decorateEntrypoint(declaration, { guards: [...declaration.guards, guard] });
|
|
19
|
+
});
|
|
23
20
|
//# sourceMappingURL=coguard.js.map
|
package/build/coguard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coguard.js","sourceRoot":"","sources":["../src/coguard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"coguard.js","sourceRoot":"","sources":["../src/coguard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAGvE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,IAAU,EAAE,KAAK,GAAW,gBAAgB,EACtC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE;IAC1C,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,WAAW,CAAA;IAE3F,OAAO,kBAAkB,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;AACpF,CAAC,CAAC,CAAA"}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const
|
|
1
|
+
import type { AccessTokenList, IssuedAccessToken } from '@owlmeans/auth-token';
|
|
2
|
+
import type { AuthTokenEntrypoints } from '@owlmeans/auth-token';
|
|
3
|
+
export declare const listAccessTokens: (protocol: AuthTokenEntrypoints['list']) => import("@owlmeans/server-entrypoint").BoundEntrypointHandler<import("@owlmeans/entrypoint").EntrypointProtocol<import("@owlmeans/entrypoint").OpenRequest, AccessTokenList>>;
|
|
4
|
+
export declare const createAccessToken: (protocol: AuthTokenEntrypoints['create']) => import("@owlmeans/server-entrypoint").BoundEntrypointHandler<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
5
|
+
body: import("@owlmeans/auth-token").CreateAccessToken;
|
|
6
|
+
}, IssuedAccessToken>>;
|
|
7
|
+
export declare const revokeAccessToken: (protocol: AuthTokenEntrypoints['revoke']) => import("@owlmeans/server-entrypoint").BoundEntrypointHandler<import("@owlmeans/entrypoint").EntrypointProtocol<{
|
|
8
|
+
params: import("@owlmeans/auth-token").AccessTokenParams;
|
|
9
|
+
}, {
|
|
10
|
+
id: string;
|
|
11
|
+
}>>;
|
|
4
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EAAsC,iBAAiB,EACvE,MAAM,sBAAsB,CAAA;AAM7B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AA0BhE,eAAO,MAAM,gBAAgB,aAAc,oBAAoB,CAAC,MAAM,CAAC,iLAarE,CAAA;AAEF,eAAO,MAAM,iBAAiB,aAAc,oBAAoB,CAAC,QAAQ,CAAC;;sBAsCxE,CAAA;AAEF,eAAO,MAAM,iBAAiB,aAAc,oBAAoB,CAAC,QAAQ,CAAC;;;;GAqBxE,CAAA"}
|
package/build/handlers/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AuthForbidden, AuthroizationType, ALL_SCOPES, AuthRole } from '@owlmeans/auth';
|
|
2
2
|
import { AUTH_TOKEN_MAX_TTL, AUTH_TOKEN_RESOURCE } from '@owlmeans/auth-token';
|
|
3
3
|
import { requireEntityKey } from '@owlmeans/auth-common';
|
|
4
|
-
import {
|
|
4
|
+
import { handlers } from '@owlmeans/server-api';
|
|
5
5
|
import { mintAccessToken } from '../hash.js';
|
|
6
6
|
import { prefixOf } from '../guard.js';
|
|
7
7
|
const view = (record) => {
|
|
@@ -23,7 +23,7 @@ const refuseTokenAuth = (req, what) => {
|
|
|
23
23
|
throw new AuthForbidden(what);
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
|
-
export const listAccessTokens =
|
|
26
|
+
export const listAccessTokens = (protocol) => handlers().request(protocol, async (req, context) => {
|
|
27
27
|
const ctx = context;
|
|
28
28
|
const entityId = requireEntityKey(req);
|
|
29
29
|
const profileId = req.auth?.profileId;
|
|
@@ -36,7 +36,7 @@ export const listAccessTokens = handleRequest(async (req, context) => {
|
|
|
36
36
|
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()),
|
|
37
37
|
};
|
|
38
38
|
});
|
|
39
|
-
export const createAccessToken =
|
|
39
|
+
export const createAccessToken = (protocol) => handlers().body(protocol, async (payload, context, req) => {
|
|
40
40
|
const ctx = context;
|
|
41
41
|
refuseTokenAuth(req, 'token-mint');
|
|
42
42
|
const entityId = requireEntityKey(req);
|
|
@@ -72,7 +72,7 @@ export const createAccessToken = handleBody(async (payload, context, req) => {
|
|
|
72
72
|
// The only moment the plaintext exists outside the caller's own machine.
|
|
73
73
|
return { token: minted.token, record: view(record) };
|
|
74
74
|
});
|
|
75
|
-
export const revokeAccessToken =
|
|
75
|
+
export const revokeAccessToken = (protocol) => handlers().params(protocol, async (payload, context, req) => {
|
|
76
76
|
const ctx = context;
|
|
77
77
|
refuseTokenAuth(req, 'token-revoke');
|
|
78
78
|
const entityId = requireEntityKey(req);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACvF,OAAO,EACL,kBAAkB,EAAE,mBAAmB,EACxC,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlers/index.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.
|
|
3
|
+
"version": "0.1.18-rc.11",
|
|
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",
|
|
@@ -24,16 +24,16 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@noble/hashes": "^1.5.0",
|
|
27
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
28
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
29
|
-
"@owlmeans/auth-token": "^0.1.18-rc.
|
|
30
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
31
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
32
|
-
"@owlmeans/mongo-resource": "^0.1.18-rc.
|
|
33
|
-
"@owlmeans/resource": "^0.1.18-rc.
|
|
34
|
-
"@owlmeans/server-api": "^0.1.18-rc.
|
|
35
|
-
"@owlmeans/server-auth-identity": "^0.1.18-rc.
|
|
36
|
-
"@owlmeans/server-context": "^0.1.18-rc.
|
|
27
|
+
"@owlmeans/auth": "^0.1.18-rc.20",
|
|
28
|
+
"@owlmeans/auth-common": "^0.1.18-rc.24",
|
|
29
|
+
"@owlmeans/auth-token": "^0.1.18-rc.11",
|
|
30
|
+
"@owlmeans/context": "^0.1.18-rc.19",
|
|
31
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.22",
|
|
32
|
+
"@owlmeans/mongo-resource": "^0.1.18-rc.23",
|
|
33
|
+
"@owlmeans/resource": "^0.1.18-rc.20",
|
|
34
|
+
"@owlmeans/server-api": "^0.1.18-rc.28",
|
|
35
|
+
"@owlmeans/server-auth-identity": "^0.1.18-rc.24",
|
|
36
|
+
"@owlmeans/server-context": "^0.1.18-rc.23",
|
|
37
37
|
"@scure/base": "^1.1.9"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
package/src/coguard.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { GUARD_AUTH_TOKEN } from '@owlmeans/auth-token'
|
|
2
|
+
import { decorateEntrypoint, mapProtocols } from '@owlmeans/entrypoint'
|
|
3
|
+
import type { EntrypointTree } from '@owlmeans/entrypoint'
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Let an access token authenticate every route that already has a guard.
|
|
@@ -12,12 +14,10 @@ import { GUARD_AUTH_TOKEN } from '@owlmeans/auth-token'
|
|
|
12
14
|
* than skipped here — a child entrypoint inherits every guard its ancestors declare and cannot
|
|
13
15
|
* drop one, so the refusal has to live where the credential is inspected.
|
|
14
16
|
*/
|
|
15
|
-
export const
|
|
16
|
-
|
|
17
|
-
):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
})
|
|
23
|
-
}
|
|
17
|
+
export const withAuthTokenCoguard = <Tree extends EntrypointTree>(
|
|
18
|
+
tree: Tree, guard: string = GUARD_AUTH_TOKEN,
|
|
19
|
+
): Tree => mapProtocols(tree, declaration => {
|
|
20
|
+
if (declaration.guards.length < 1 || declaration.guards.includes(guard)) return declaration
|
|
21
|
+
|
|
22
|
+
return decorateEntrypoint(declaration, { guards: [...declaration.guards, guard] })
|
|
23
|
+
})
|
package/src/handlers/index.ts
CHANGED
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
AUTH_TOKEN_MAX_TTL, AUTH_TOKEN_RESOURCE
|
|
4
4
|
} from '@owlmeans/auth-token'
|
|
5
5
|
import type {
|
|
6
|
-
AccessTokenList,
|
|
7
|
-
IssuedAccessToken
|
|
6
|
+
AccessTokenList, AccessTokenRecord, AccessTokenView, IssuedAccessToken
|
|
8
7
|
} from '@owlmeans/auth-token'
|
|
9
8
|
import { requireEntityKey } from '@owlmeans/auth-common'
|
|
10
|
-
import {
|
|
9
|
+
import { handlers } from '@owlmeans/server-api'
|
|
11
10
|
import { mintAccessToken } from '../hash.js'
|
|
12
11
|
import { prefixOf } from '../guard.js'
|
|
13
12
|
import type { AccessTokenResource, AuthTokenContext } from '../types.js'
|
|
13
|
+
import type { AuthTokenEntrypoints } from '@owlmeans/auth-token'
|
|
14
14
|
|
|
15
15
|
const view = (record: AccessTokenRecord): AccessTokenView => {
|
|
16
16
|
const { hash, ...rest } = record
|
|
@@ -36,7 +36,7 @@ const refuseTokenAuth = (req: { auth?: { type?: string } }, what: string): void
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export const listAccessTokens =
|
|
39
|
+
export const listAccessTokens = (protocol: AuthTokenEntrypoints['list']) => handlers<AuthTokenContext>().request(protocol, async (req, context) => {
|
|
40
40
|
const ctx = context as AuthTokenContext
|
|
41
41
|
const entityId = requireEntityKey(req)
|
|
42
42
|
const profileId = req.auth?.profileId
|
|
@@ -51,7 +51,7 @@ export const listAccessTokens = handleRequest(async (req, context) => {
|
|
|
51
51
|
} satisfies AccessTokenList
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
export const createAccessToken =
|
|
54
|
+
export const createAccessToken = (protocol: AuthTokenEntrypoints['create']) => handlers<AuthTokenContext>().body(protocol, async (payload, context, req) => {
|
|
55
55
|
const ctx = context as AuthTokenContext
|
|
56
56
|
refuseTokenAuth(req, 'token-mint')
|
|
57
57
|
|
|
@@ -91,7 +91,7 @@ export const createAccessToken = handleBody<CreateAccessToken>(async (payload, c
|
|
|
91
91
|
return { token: minted.token, record: view(record) } satisfies IssuedAccessToken
|
|
92
92
|
})
|
|
93
93
|
|
|
94
|
-
export const revokeAccessToken =
|
|
94
|
+
export const revokeAccessToken = (protocol: AuthTokenEntrypoints['revoke']) => handlers<AuthTokenContext>().params(protocol, async (payload, context, req) => {
|
|
95
95
|
const ctx = context as AuthTokenContext
|
|
96
96
|
refuseTokenAuth(req, 'token-revoke')
|
|
97
97
|
|
package/tests/coguard.spec.ts
CHANGED
|
@@ -1,36 +1,34 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
2
|
import { GUARD_AUTH_TOKEN } from '@owlmeans/auth-token'
|
|
3
|
-
import {
|
|
3
|
+
import { openProtocol, protocols } from '@owlmeans/entrypoint'
|
|
4
|
+
import { route } from '@owlmeans/route'
|
|
5
|
+
import { withAuthTokenCoguard } from '../src/coguard.js'
|
|
4
6
|
|
|
5
7
|
describe('@owlmeans/server-auth-token — the coguard', () => {
|
|
6
|
-
test('
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
test('decorates a guarded protocol and leaves the primary guard first', () => {
|
|
9
|
+
const guarded = openProtocol(route('guarded', '/guarded'), { guards: 'auth' })
|
|
10
|
+
const [decorated] = protocols(withAuthTokenCoguard({ guarded }))
|
|
9
11
|
|
|
10
|
-
expect(
|
|
12
|
+
expect(decorated.guards).toEqual(['auth', GUARD_AUTH_TOKEN])
|
|
13
|
+
expect(guarded.guards).toEqual(['auth'])
|
|
11
14
|
})
|
|
12
15
|
|
|
13
|
-
test('leaves an unguarded
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
setupAuthTokenCoguard(entrypoints)
|
|
17
|
-
|
|
18
|
-
expect(entrypoints[0].guards).toBeUndefined()
|
|
19
|
-
expect(entrypoints[1].guards).toEqual([])
|
|
16
|
+
test('leaves an unguarded protocol unchanged', () => {
|
|
17
|
+
const open = openProtocol(route('open', '/open'))
|
|
18
|
+
expect(protocols(withAuthTokenCoguard({ open }))[0]).toBe(open)
|
|
20
19
|
})
|
|
21
20
|
|
|
22
|
-
test('is idempotent
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
test('is idempotent', () => {
|
|
22
|
+
const guarded = openProtocol(route('guarded', '/guarded'), { guards: 'auth' })
|
|
23
|
+
const [once] = protocols(withAuthTokenCoguard({ guarded }))
|
|
24
|
+
const [twice] = protocols(withAuthTokenCoguard({ once }))
|
|
26
25
|
|
|
27
|
-
expect(
|
|
26
|
+
expect(twice.guards).toEqual(['auth', GUARD_AUTH_TOKEN])
|
|
28
27
|
})
|
|
29
28
|
|
|
30
29
|
test('honours a custom guard alias', () => {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
expect(entrypoints[0].guards).toEqual(['auth', 'other-guard'])
|
|
30
|
+
const guarded = openProtocol(route('guarded', '/guarded'), { guards: 'auth' })
|
|
31
|
+
expect(protocols(withAuthTokenCoguard({ guarded }, 'other-guard'))[0].guards)
|
|
32
|
+
.toEqual(['auth', 'other-guard'])
|
|
35
33
|
})
|
|
36
34
|
})
|
package/tests/handlers.spec.ts
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
2
|
import { AuthroizationType, AuthRole } from '@owlmeans/auth'
|
|
3
|
-
import { AUTH_TOKEN_RESOURCE } from '@owlmeans/auth-token'
|
|
3
|
+
import { AUTH_TOKEN_RESOURCE, makeAuthTokenEntrypoints } from '@owlmeans/auth-token'
|
|
4
4
|
import type { AccessTokenRecord, IssuedAccessToken } from '@owlmeans/auth-token'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
createAccessToken as bindCreateAccessToken,
|
|
7
|
+
listAccessTokens as bindListAccessTokens,
|
|
8
|
+
revokeAccessToken as bindRevokeAccessToken,
|
|
9
|
+
} from '../src/handlers/index.js'
|
|
6
10
|
import { hashAccessToken } from '../src/hash.js'
|
|
7
11
|
import { makeTestContext, seedProfile, seedToken, TEST_ENTITY, TEST_PREFIX, TEST_PROFILE, TEST_USER } from './context.js'
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
|
-
* A
|
|
11
|
-
*
|
|
12
|
-
* RULES — who may mint, what a list shows, whose token may be revoked — rather than about routing.
|
|
14
|
+
* A protocol-bound implementation is run with the smallest binding context the transport needs.
|
|
15
|
+
* This keeps the tests about the authorization rules rather than a router's alias lookup.
|
|
13
16
|
*/
|
|
14
17
|
const invoke = async (handler: any, context: any, req: any): Promise<any> => {
|
|
15
18
|
const res: any = { resolve: (value: unknown) => { res.value = value }, reject: (e: Error) => { res.error = e } }
|
|
16
|
-
await handler({ ref: { ctx: context } })(req, res)
|
|
19
|
+
await handler.bind({ ref: { ctx: context } })(req, res)
|
|
17
20
|
if (res.error != null) throw res.error
|
|
18
21
|
|
|
19
22
|
return res.value
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
const protocols = makeAuthTokenEntrypoints()
|
|
26
|
+
const listAccessTokens = bindListAccessTokens(protocols.list)
|
|
27
|
+
const createAccessToken = bindCreateAccessToken(protocols.create)
|
|
28
|
+
const revokeAccessToken = bindRevokeAccessToken(protocols.revoke)
|
|
29
|
+
|
|
22
30
|
const session = (patch: Record<string, unknown> = {}): any => ({
|
|
23
31
|
headers: {}, params: {}, query: {}, body: {},
|
|
24
32
|
entity: { id: TEST_ENTITY, slug: TEST_ENTITY },
|