@owlmeans/server-auth 0.1.18-rc.26 → 0.1.18-rc.28

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
@@ -1,78 +1,218 @@
1
1
  # @owlmeans/server-auth
2
2
 
3
- Server-side Ed25519 authentication service for OwlMeans backends.
4
-
5
- ## Overview
6
-
7
- - Implements the `GuardService` interface for Ed25519-signed token verification
8
- - `appendAuthService()` registers the auth guard into a server context
9
- - `DEFAULT_ALIAS` (`'auth'`) is the service alias and re-exported as `DAUTH_GUARD` via `@owlmeans/server-app`
10
- - Handles token caching via a static resource identified by `AUTH_CACHE`
3
+ The server side of OwlMeans authentication, in two halves. The root export is what every protected
4
+ API service needs: the guard that verifies an Ed25519-signed bearer token and resolves
5
+ `request.auth`, and the dispatcher bindings that exchange a credential issued by the auth manager
6
+ for a bearer of this service. The `./manager` subpath is the auth manager application itself — the
7
+ challenge endpoints, the authentication plugin registry, the signed credential envelope and the rely
8
+ handshake. An ordinary service imports the root (usually implicitly, through
9
+ `@owlmeans/server-app`'s `makeContext`); only the auth manager imports `./manager`. OIDC tokens are
10
+ `@owlmeans/server-oidc-rp`, long-lived API keys `@owlmeans/server-auth-token`, local
11
+ account/profile records `@owlmeans/server-auth-identity`, and the browser side `@owlmeans/client-auth`.
11
12
 
12
13
  ## Installation
13
14
 
14
15
  ```bash
15
- bun add @owlmeans/server-auth@^0.1.18-rc.17
16
+ bun add @owlmeans/server-auth@^0.1.18-rc.28
16
17
  ```
17
18
 
19
+ ## Concepts
20
+
21
+ - **Guard** — the `AuthService` registered as `auth` (the value of `DEFAULT_GUARD`). `match` hits a
22
+ request carrying an `ed25519-basic-token`; `handle` verifies it against this service's own trusted
23
+ key (`cfg.alias ?? cfg.service`) and resolves the `Auth`. It authenticates only — authorization is
24
+ gates and handlers.
25
+ - **Token exchange** — `authenticate(token)` verifies a credential envelope against the
26
+ `auth-service` trusted record (`AUTH_SRV_KEY`), burns its challenge into `AUTH_CACHE`, and returns
27
+ a bearer signed by this service.
28
+ - **`AUTH_CACHE`** — the anti-replay store: each challenge is created once with an
29
+ `AUTHEN_TIMEFRAME` TTL. A static in-process resource by default.
30
+ - **Auth manager** — the `./manager` application: `init(request)` returns a challenge,
31
+ `authenticate(credential)` returns a signed credential envelope, and the slug on the credential is
32
+ canonicalized through the entity resolver before signing.
33
+ - **Authentication plugin** — `{ type, init, authenticate }` registered under an
34
+ `AuthenticationType`; the manager dispatches by the request's `type`.
35
+ - **Rely** — the wallet handshake that lets one authenticated party vouch for another over a socket
36
+ (`DEFAULT_RELY` guard, `RELY_TUNNEL`).
37
+
18
38
  ## Usage
19
39
 
20
- Registered automatically by `makeContext()` from `@owlmeans/server-app`. To customize, pass `customize: true` and append manually:
40
+ ### Protect routes on an ordinary service
21
41
 
22
- ```typescript
23
- import { makeContext } from '@owlmeans/server-app'
24
- import { appendAuthService, AUTH_CACHE } from '@owlmeans/server-auth'
42
+ `@owlmeans/server-app`'s `makeContext(cfg)` registers the guard. A protocol opts in with `guards`,
43
+ and everything declared under it inherits them.
25
44
 
26
- const context = makeContext(appConfig, true) // skip default auth
45
+ ```ts
46
+ import { contract, openProtocol, protocol, typed } from '@owlmeans/entrypoint'
47
+ import { route, RouteMethod } from '@owlmeans/route'
48
+ import { DEFAULT_GUARD } from '@owlmeans/auth-common'
27
49
 
28
- // Append auth with custom config
29
- appendAuthService(context)
50
+ const invoiceBase = openProtocol(route('my-app:api:invoice', '/invoice'), { guards: DEFAULT_GUARD })
51
+
52
+ export const invoiceProtocols = {
53
+ base: invoiceBase,
54
+ list: protocol(
55
+ route('my-app:api:invoice:list', '/', { parent: invoiceBase, method: RouteMethod.GET }),
56
+ contract(typed<Invoice[]>()),
57
+ ),
58
+ }
30
59
  ```
31
60
 
32
- Reference the guard by alias when protecting routes:
61
+ ```ts
62
+ import { handlers } from '@owlmeans/server-app'
63
+ import { requireEntityKey } from '@owlmeans/auth-common'
33
64
 
34
- ```typescript
35
- import { DAUTH_GUARD } from '@owlmeans/server-app'
36
- import { contract, protocol, typed } from '@owlmeans/entrypoint'
37
- import { route } from '@owlmeans/route'
38
- // same as DEFAULT_ALIAS from @owlmeans/server-auth
65
+ const api = handlers<Context>()
39
66
 
40
- const adminProtocol = protocol(
41
- route('admin', '/admin'),
42
- contract(typed<AdminResponse>()),
43
- { guards: DAUTH_GUARD },
44
- )
67
+ export const list = api.request(invoiceProtocols.list, async (request, context) => {
68
+ // request.auth is the verified Auth: userId, profileId, role, scopes, entitySlug
69
+ const { items } = await context.invoice().list({ entityId: requireEntityKey(request) })
70
+ return items
71
+ })
45
72
  ```
46
73
 
47
- ## API
74
+ ### Scaled deployment
75
+
76
+ Replace the in-process cache with a shared one before registering the guard, and declare the trusted
77
+ keys the guard verifies against.
78
+
79
+ ```ts
80
+ import { makeContext as makeAppContext } from '@owlmeans/server-app'
81
+ import { appendRedis } from '@owlmeans/redis'
82
+ import { makeRedisResource } from '@owlmeans/redis-resource'
83
+ import { appendAuthService, AUTH_CACHE, AUTH_SRV_KEY } from '@owlmeans/server-auth'
84
+ import { appendAuthIdentityResources } from '@owlmeans/server-auth-identity'
85
+
86
+ // config.ts — keys are loaded from the environment, never committed
87
+ cfg.trusted.push({
88
+ id: process.env.AUTH_SERVICE_DID!, name: AUTH_SRV_KEY,
89
+ credential: process.env.AUTH_SERVICE_PUB!, scopes: ['*'],
90
+ })
91
+ cfg.trusted.push({
92
+ id: process.env.MY_APP_API_DID!, name: MY_APP_API,
93
+ credential: process.env.MY_APP_API_PUB!, secret: process.env.MY_APP_API_PK!, scopes: ['*'],
94
+ })
95
+
96
+ // context.ts
97
+ export const makeContext = <C extends Config, T extends Context<C>>(cfg: C): T => {
98
+ const context = makeAppContext<C, T>(cfg, true)
99
+ appendRedis<C, T>(context)
100
+
101
+ context.registerResource(makeRedisResource(AUTH_CACHE))
102
+ appendAuthService<C, T>(context)
103
+ appendAuthIdentityResources(context)
104
+
105
+ return context
106
+ }
107
+ ```
48
108
 
49
- ### `appendAuthService<C, T>(context): void`
109
+ ### Verify a token outside the HTTP pipeline
50
110
 
51
- Registers the Ed25519 auth service and its nonce cache resource into the context.
111
+ ```ts
112
+ import type { AuthServiceAppend } from '@owlmeans/server-auth'
52
113
 
53
- ### `makeAuthService(alias?): AuthService`
114
+ const readAuth = async (context: Context & AuthServiceAppend, authorization: string) =>
115
+ context.auth().unpack(authorization) // 'ED25519-BASIC-TOKEN <envelope>' -> Auth, or throws
116
+ ```
54
117
 
55
- Creates the auth service directly (used internally by `appendAuthService`).
118
+ ### Run the auth manager
56
119
 
57
- ### `DEFAULT_ALIAS`
120
+ ```ts
121
+ import { config, makeContext, main } from '@owlmeans/server-auth/manager'
122
+ import type { AppConfig } from '@owlmeans/server-auth/manager'
123
+ import '@owlmeans/server-oidc-rp/auth/plugins' // optional: OIDC and Google login plugins
58
124
 
59
- The auth service alias: `'auth'`. Re-exported as `DAUTH_GUARD` from `@owlmeans/server-app`.
125
+ const cfg = config<AppConfig>(MY_APP_AUTH, commonConfig as AppConfig)
126
+ const context = makeContext(cfg)
60
127
 
61
- ### `AUTH_CACHE`
128
+ await main(context)
129
+ ```
62
130
 
63
- Resource alias for the nonce replay-prevention cache.
131
+ ### Add an authentication method
64
132
 
65
- ## Product-Viable Integration Notes
133
+ ```ts
134
+ import { registerPlugin } from '@owlmeans/server-auth/manager/plugins'
135
+ import type { AuthPlugin } from '@owlmeans/server-auth/manager/plugins'
66
136
 
67
- - Register `appendAuthService(context)` before `appendAuthIdentityResources(context)` and product-specific gate services.
68
- - Register `AUTH_CACHE` explicitly as a Redis resource when customizing the backend context.
69
- - This package verifies bearer tokens and populates `request.auth`; authorization remains the job of entrypoint gates and handler-level organization entity checks.
70
- - Pair it with `@owlmeans/server-auth-identity` when external provider login should produce durable local account/profile/credentials records.
137
+ registerPlugin('my-method', context => ({
138
+ type: 'my-method',
139
+ init: async request => ({ challenge: /* unique per request */ '' }),
140
+ authenticate: async credential => {
141
+ // verify credential.credential, then set userId / profileId / entitySlug / role / scopes
142
+ return { token: '' } // '' keeps the manager's own challenge as the credential token
143
+ },
144
+ }) as AuthPlugin)
145
+ ```
71
146
 
72
- ## Related Packages
147
+ ## API
73
148
 
74
- - [`@owlmeans/auth-common`](../auth-common) — `GUARD_ED25519` constant and auth entrypoints
149
+ ### Root export
150
+
151
+ | Symbol | Kind | Purpose |
152
+ |---|---|---|
153
+ | `appendAuthService(ctx, alias = DEFAULT_ALIAS)` | function | Register the guard as `context.auth()`; registers a static `AUTH_CACHE` when the context has none |
154
+ | `makeAuthService(alias?)` | function | The guard: `match`, `handle`, `unpack(token)`, `authenticate(token)` |
155
+ | `entrypoints` | const | Server bindings of `authProtocols.dispatcher` and `authProtocols.dispatcherAuthenticate` |
156
+ | `DEFAULT_ALIAS` | const | `'auth'` — equals `DEFAULT_GUARD`; re-exported as `DAUTH_GUARD` by `@owlmeans/server-app` |
157
+ | `AUTH_CACHE` | const | `'auth-cache'` — the single-use challenge store |
158
+ | `AUTH_SRV_KEY` | const | `'auth-service'` — the trusted record whose key signs credential envelopes |
159
+ | `AUTHEN_TIMEFRAME` | const | `15 * 60 * 1000` ms — challenge lifetime and anti-replay window |
160
+ | `makeRelyModel`, `makeProviderRely`, `makeConsumerRely` | function | Rely envelope models |
161
+ | `AuthService`, `AuthServiceAppend`, `AuthSpent`, `RelyOptions` | type | Guard service, context mixin, cache record, rely options |
162
+
163
+ ### `./manager`
164
+
165
+ | Symbol | Kind | Purpose |
166
+ |---|---|---|
167
+ | `makeContext(cfg, customize?)` | function | Server context with API server, API client, socket service, rely guard and static `AUTH_CACHE` |
168
+ | `main(ctx)` | function | Register the manager entrypoints, init and listen |
169
+ | `entrypoints` | const | Bindings for authen, init, authenticate, rely, api-config and reCAPTCHA siteverify |
170
+ | `authenticationInit`, `authenticate`, `rely` | function | Implementations bound to the auth protocols |
171
+ | `plugins`, `registerPlugin(type, factory)` | const / function | The plugin registry |
172
+ | `appendSupervisorAuth(ctx, opts?)`, `setupInternalTokenCoguard(entrypoints, guard?)` | function | PK supervisor login (development only by default) |
173
+ | `createRelyService(alias?)`, `createRelyFlow(context, conn, auth?)` | function | Rely guard service and socket authentication flow |
174
+ | `DEFAULT_RELY`, `RELY_TUNNEL` | const | `'auth-rely'`, `'rely-tunnel'` |
175
+ | `config`, `service`, `TRUSTED`, `addWebService`, `AppType`, `assertContext`, `klusterize`, `stab`, `backend`, `filterObject` | re-export | Config and context helpers for a manager app |
176
+ | `handleIntermediate`, `handleBody`, `handleRequest`, `handleParams` | re-export | `@owlmeans/server-api` wrappers |
177
+ | `AUTHEN`, `AUTHEN_AUTHEN`, `AUTHEN_INIT`, `GUARD_ED25519`, `BED255_CASHE_RESOURCE` | re-export | Auth aliases |
178
+ | `AppConfig`, `AppContext`, `AuthModel`, `RelyService`, `RelyAllowanceRequest`, `RelyLinker`, `RelyCarrier` | type | Manager shapes |
179
+ | `SupervisorAuthOptions`, `SupervisorUserResolver`, `SupervisorUserResolution`, `SupervisorPluginOptions` | type | Supervisor options |
180
+ | `TrustedRecord`, `RefedEntrypointHandler`, `AbstractRequest`, `AbstractResponse` | type | Re-exported types |
181
+
182
+ ### `./manager/plugins`
183
+
184
+ | Symbol | Kind | Purpose |
185
+ |---|---|---|
186
+ | `registerPlugin(type, factory)`, `plugins` | function / const | The module-level registry |
187
+ | `getPlugin(type, context)`, `assertType(type, plugin)` | function | Resolution; `getPlugin` throws `AuthUnknown(type)` for an unregistered type |
188
+ | `basicEd25519`, `reCaptcha`, `basicRely` | function | Built-in plugins |
189
+ | `makeSupervisorPlugin(context, opts)` | function | PK supervisor plugin factory |
190
+ | `AuthPlugin`, `RecpatchaResponse`, `RecaptchaRequest`, `RelyRecord`, `AuthRedisResource` | type | Plugin shapes |
191
+
192
+ ## Common pitfalls
193
+
194
+ - The guard verifies a bearer and sets `request.auth`; it decides nothing about ownership. Pair it
195
+ with gates and keep a handler-level organization check.
196
+ - The default static `AUTH_CACHE` is correct for one replica only. Register a Redis resource under
197
+ the same alias before `appendAuthService` in any scaled deployment.
198
+ - A plugin's `init` must return a challenge unique per request; a repeated challenge collides in
199
+ `AUTH_CACHE` and fails as `AuthenFailed('challenge')`.
200
+ - The manager replaces whatever slug a plugin sets on `credential.entitySlug` with the current slug;
201
+ an unresolvable value fails as `AuthenFailed('entity')`. A token carries `entitySlug`, never an
202
+ entity id.
203
+ - Register `appendAuthService` before `appendAuthIdentityResources` and product gate services.
204
+ - Import `./manager` only in the auth manager process.
205
+
206
+ ## Related packages
207
+
208
+ - [`@owlmeans/auth`](../auth) — `Auth`, `AuthCredentials`, error classes
209
+ - [`@owlmeans/auth-common`](../auth-common) — `DEFAULT_GUARD`, `authProtocols`, `trust()`, entity helpers
210
+ - [`@owlmeans/server-auth-identity`](../server-auth-identity) — local identity records and the entity resolver
211
+ - [`@owlmeans/server-oidc-rp`](../server-oidc-rp) — OIDC login plugins and guard
212
+ - [`@owlmeans/server-auth-otp`](../server-auth-otp) — email OTP plugin
213
+ - [`@owlmeans/server-auth-token`](../server-auth-token) — long-lived access tokens
75
214
  - [`@owlmeans/server-app`](../server-app) — `makeContext` calls `appendAuthService` by default
215
+ - [`@owlmeans/client-auth`](../client-auth) — the browser side
76
216
 
77
217
  <!-- owlmeans:agent-guidance:start -->
78
218
  ## Agent guidance
@@ -82,7 +222,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
82
222
  your project's skill store (`.agents/skills/`):
83
223
 
84
224
  ```sh
85
- npx @owlmeans/agent-skills@^0.1.18-rc.19
225
+ npx @owlmeans/agent-skills@^0.1.18-rc.21
86
226
  ```
87
227
 
88
228
  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",
4
- "version": "0.1.18-rc.26",
5
- "generatedAt": "2026-09-12T13:51:53.529Z",
4
+ "version": "0.1.18-rc.28",
5
+ "generatedAt": "2026-09-15T12:21:31.902Z",
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
9
9
 
10
10
  **Layer:** Server
11
- **Install:** `"@owlmeans/server-auth": "^0.1.18-rc.26"` in `dependencies`
11
+ **Install:** `"@owlmeans/server-auth": "^0.1.18-rc.28"` in `dependencies`
12
12
 
13
13
  Two halves, deliberately split by subpath:
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/server-auth",
3
- "version": "0.1.18-rc.26",
3
+ "version": "0.1.18-rc.28",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -37,29 +37,29 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@noble/hashes": "^1.5.0",
40
- "@owlmeans/api": "^0.1.18-rc.20",
41
- "@owlmeans/api-config-server": "^0.1.18-rc.24",
42
- "@owlmeans/auth": "^0.1.18-rc.16",
43
- "@owlmeans/auth-common": "^0.1.18-rc.20",
44
- "@owlmeans/basic-envelope": "^0.1.18-rc.19",
45
- "@owlmeans/basic-ids": "^0.1.18-rc.16",
46
- "@owlmeans/basic-keys": "^0.1.18-rc.19",
47
- "@owlmeans/client-config": "^0.1.18-rc.19",
48
- "@owlmeans/client-entrypoint": "^0.1.18-rc.20",
49
- "@owlmeans/config": "^0.1.18-rc.19",
50
- "@owlmeans/context": "^0.1.18-rc.15",
51
- "@owlmeans/kluster": "^0.1.18-rc.19",
52
- "@owlmeans/entrypoint": "^0.1.18-rc.18",
53
- "@owlmeans/redis-resource": "^0.1.18-rc.19",
54
- "@owlmeans/resource": "^0.1.18-rc.16",
55
- "@owlmeans/route": "^0.1.18-rc.16",
56
- "@owlmeans/server-api": "^0.1.18-rc.24",
57
- "@owlmeans/server-context": "^0.1.18-rc.19",
58
- "@owlmeans/server-entrypoint": "^0.1.18-rc.18",
59
- "@owlmeans/server-route": "^0.1.18-rc.16",
60
- "@owlmeans/server-socket": "^0.1.18-rc.26",
61
- "@owlmeans/socket": "^0.1.18-rc.16",
62
- "@owlmeans/static-resource": "^0.1.18-rc.16",
40
+ "@owlmeans/api": "^0.1.18-rc.22",
41
+ "@owlmeans/api-config-server": "^0.1.18-rc.26",
42
+ "@owlmeans/auth": "^0.1.18-rc.18",
43
+ "@owlmeans/auth-common": "^0.1.18-rc.22",
44
+ "@owlmeans/basic-envelope": "^0.1.18-rc.21",
45
+ "@owlmeans/basic-ids": "^0.1.18-rc.18",
46
+ "@owlmeans/basic-keys": "^0.1.18-rc.21",
47
+ "@owlmeans/client-config": "^0.1.18-rc.21",
48
+ "@owlmeans/client-entrypoint": "^0.1.18-rc.22",
49
+ "@owlmeans/config": "^0.1.18-rc.21",
50
+ "@owlmeans/context": "^0.1.18-rc.17",
51
+ "@owlmeans/kluster": "^0.1.18-rc.21",
52
+ "@owlmeans/entrypoint": "^0.1.18-rc.20",
53
+ "@owlmeans/redis-resource": "^0.1.18-rc.21",
54
+ "@owlmeans/resource": "^0.1.18-rc.18",
55
+ "@owlmeans/route": "^0.1.18-rc.18",
56
+ "@owlmeans/server-api": "^0.1.18-rc.26",
57
+ "@owlmeans/server-context": "^0.1.18-rc.21",
58
+ "@owlmeans/server-entrypoint": "^0.1.18-rc.20",
59
+ "@owlmeans/server-route": "^0.1.18-rc.18",
60
+ "@owlmeans/server-socket": "^0.1.18-rc.28",
61
+ "@owlmeans/socket": "^0.1.18-rc.18",
62
+ "@owlmeans/static-resource": "^0.1.18-rc.18",
63
63
  "@scure/base": "^2.3.0"
64
64
  },
65
65
  "peerDependencies": {
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@owlmeans/dep-config": "workspace:*",
70
- "@owlmeans/test-auth": "^0.1.18-rc.20",
70
+ "@owlmeans/test-auth": "^0.1.18-rc.22",
71
71
  "@types/bun": "^1.4.0",
72
72
  "@types/node": "^26.1.0",
73
73
  "nodemon": "^3.1.14",
@@ -38,7 +38,7 @@ The manager is built on the OwlMeans entrypoint system, providing:
38
38
  This manager app is part of the `@owlmeans/server-auth` package:
39
39
 
40
40
  ```bash
41
- npm install @owlmeans/server-auth@^0.1.18-rc.17
41
+ npm install @owlmeans/server-auth@^0.1.18-rc.28
42
42
  ```
43
43
 
44
44
  ## API Reference