@kb-labs/gateway-auth 2.103.0 → 2.105.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/README.md +14 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +20 -20
package/README.md
CHANGED
|
@@ -63,6 +63,16 @@ Client Gateway (/auth/*) ICache (store)
|
|
|
63
63
|
|
|
64
64
|
Registers a new client. Returns credentials that grant access to the gateway.
|
|
65
65
|
|
|
66
|
+
**Not self-service.** This endpoint is gated behind the `MACHINE_REGISTER`
|
|
67
|
+
permission — callers need an already-authenticated admin session (cookie
|
|
68
|
+
auth) or a machine token that itself carries `MACHINE_REGISTER`. A caller
|
|
69
|
+
with neither gets `401`/`403`, not a fresh registration. The one exception is
|
|
70
|
+
`ensureBootstrapCliCredentials` (`bootstrap-cli-credentials.ts`), which mints
|
|
71
|
+
the platform's first machine account in-process on gateway startup — no HTTP
|
|
72
|
+
round-trip, so the gate never applies to it. For everyone else: an admin logs
|
|
73
|
+
in first, then calls this endpoint (or `kb auth register`,
|
|
74
|
+
which requires the same prior login) on the new client's behalf.
|
|
75
|
+
|
|
66
76
|
**Request:**
|
|
67
77
|
```json
|
|
68
78
|
{
|
|
@@ -222,9 +232,12 @@ Refresh Token (30 days)
|
|
|
222
232
|
## Usage Example
|
|
223
233
|
|
|
224
234
|
```bash
|
|
225
|
-
# Register
|
|
235
|
+
# Register — requires an authenticated admin session (MACHINE_REGISTER, see
|
|
236
|
+
# above). $ADMIN_COOKIE is whatever session cookie `POST /auth/login`
|
|
237
|
+
# returned; without it this call gets 401/403, not a fresh registration.
|
|
226
238
|
CREDS=$(curl -s -X POST http://localhost:4000/auth/register \
|
|
227
239
|
-H "Content-Type: application/json" \
|
|
240
|
+
-H "Cookie: $ADMIN_COOKIE" \
|
|
228
241
|
-d '{"name":"my-agent","capabilities":[]}')
|
|
229
242
|
|
|
230
243
|
CLIENT_ID=$(echo $CREDS | jq -r '.clientId')
|
package/dist/index.js
CHANGED
|
@@ -1562,7 +1562,7 @@ var ensureBootstrapCliCredentials = async (opts) => {
|
|
|
1562
1562
|
} catch (err) {
|
|
1563
1563
|
if (err instanceof Error && err.code === "HANDLE_TAKEN") {
|
|
1564
1564
|
logger.warn(
|
|
1565
|
-
"bootstrap-cli-credentials: reserved handle already registered but credentials file is missing; not auto-recovering (client secret is not retrievable) \u2014 run `kb auth
|
|
1565
|
+
"bootstrap-cli-credentials: reserved handle already registered but credentials file is missing; not auto-recovering (client secret is not retrievable) \u2014 run `kb auth register` manually",
|
|
1566
1566
|
{ handle: CLI_BOOTSTRAP_HANDLE }
|
|
1567
1567
|
);
|
|
1568
1568
|
return;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/jwt.ts","../src/service.ts","../src/users-store.ts","../src/credentials-store.ts","../src/memberships-store.ts","../src/invites-store.ts","../src/sessions-store.ts","../src/password-policy.ts","../src/csrf.ts","../src/rate-limit.ts","../src/tenant-resolver.ts","../src/providers/email-password.ts","../src/providers/oidc.ts","../src/provider-registry.ts","../src/provider-loader.ts","../src/oauth-state-store.ts","../src/stub-pdp.ts","../src/bootstrap-admin.ts","../src/bootstrap-cli-credentials.ts","../src/user-auth-service.ts"],"names":["COLLECTION","randomBytes","hashToken","createHash","jwtVerify","randomUUID","bcrypt","fs","refresh"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Auth storage — ICache operations for credentials and tokens.\n *\n * Key schema:\n * auth:client:{clientId} → ClientRecord (permanent)\n * auth:refresh:{tokenHash} → { hostId } (TTL 30d)\n * auth:publickey:{hostId} → string (base64url X25519 public key)\n * auth:handle:{handle} → clientId (permanent, handle index)\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { ICache } from '@kb-labs/core-platform';\n\nconst REFRESH_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\nexport interface ClientRecord {\n clientId: string;\n /** bcrypt-style: we store sha256 hash of the secret for simplicity (no bcrypt dep) */\n secretHash: string;\n hostId: string;\n namespaceId: string;\n tier: 'free' | 'pro' | 'enterprise';\n name: string;\n capabilities: string[];\n /** Permissions embedded in issued JWTs. If absent, defaults to ['host:connect']. */\n permissions?: string[];\n publicKey?: string;\n createdAt: number;\n /** Unique human-readable handle for the marketplace (e.g. \"kirill\"). Immutable once set. */\n handle?: string;\n email?: string;\n}\n\nfunction hashSecret(secret: string): string {\n return createHash('sha256').update(secret).digest('hex');\n}\n\nfunction hashToken(token: string): string {\n return createHash('sha256').update(token).digest('hex');\n}\n\nexport function generateClientId(): string {\n return `clt_${randomBytes(16).toString('hex')}`;\n}\n\nexport function generateClientSecret(): string {\n return `cs_${randomBytes(32).toString('base64url')}`;\n}\n\nexport function generateHostId(): string {\n return `host_${randomBytes(12).toString('hex')}`;\n}\n\nexport function generateNamespaceId(): string {\n return `ns_${randomBytes(16).toString('hex')}`;\n}\n\n// ── Client CRUD ───────────────────────────────────────────────────────────────\n\nexport async function saveClient(cache: ICache, record: ClientRecord): Promise<void> {\n await cache.set(`auth:client:${record.clientId}`, record);\n await cache.set(`auth:hostindex:${record.hostId}`, record.clientId);\n if (record.handle) {\n await cache.set(`auth:handle:${record.handle}`, record.clientId);\n }\n}\n\nexport async function isHandleTaken(cache: ICache, handle: string): Promise<boolean> {\n return (await cache.get(`auth:handle:${handle}`)) !== null;\n}\n\nexport async function getClientByHandle(cache: ICache, handle: string): Promise<ClientRecord | null> {\n const clientId = await cache.get<string>(`auth:handle:${handle}`);\n if (!clientId) { return null; }\n return getClient(cache, clientId);\n}\n\nexport async function getClientByHostId(cache: ICache, hostId: string): Promise<ClientRecord | null> {\n const clientId = await cache.get<string>(`auth:hostindex:${hostId}`);\n if (!clientId) {return null;}\n return getClient(cache, clientId);\n}\n\nexport async function getClient(cache: ICache, clientId: string): Promise<ClientRecord | null> {\n return cache.get<ClientRecord>(`auth:client:${clientId}`);\n}\n\nexport async function verifyClientSecret(\n cache: ICache,\n clientId: string,\n secret: string,\n): Promise<ClientRecord | null> {\n const record = await getClient(cache, clientId);\n if (!record) {return null;}\n if (record.secretHash !== hashSecret(secret)) {return null;}\n return record;\n}\n\nexport function buildClientRecord(opts: {\n name: string;\n capabilities: string[];\n permissions?: string[];\n publicKey?: string;\n secret: string;\n namespaceId?: string;\n handle?: string;\n email?: string;\n}): ClientRecord {\n return {\n clientId: generateClientId(),\n secretHash: hashSecret(opts.secret),\n hostId: generateHostId(),\n namespaceId: opts.namespaceId ?? generateNamespaceId(),\n tier: 'free',\n name: opts.name,\n capabilities: opts.capabilities,\n permissions: opts.permissions,\n publicKey: opts.publicKey,\n createdAt: Date.now(),\n handle: opts.handle,\n email: opts.email,\n };\n}\n\n// ── Refresh tokens ────────────────────────────────────────────────────────────\n\nexport async function saveRefreshToken(\n cache: ICache,\n token: string,\n hostId: string,\n namespaceId: string,\n): Promise<void> {\n await cache.set(`auth:refresh:${hashToken(token)}`, { hostId, namespaceId }, REFRESH_TTL_MS);\n}\n\nexport async function consumeRefreshToken(\n cache: ICache,\n token: string,\n): Promise<{ hostId: string; namespaceId: string } | null> {\n const key = `auth:refresh:${hashToken(token)}`;\n const entry = await cache.get<{ hostId: string; namespaceId: string }>(key);\n if (!entry) {return null;}\n // Rotate: delete old token (new one will be issued)\n await cache.delete(key);\n return entry;\n}\n\n// ── Public keys (E2E encryption) ──────────────────────────────────────────────\n\nexport async function savePublicKey(\n cache: ICache,\n hostId: string,\n publicKey: string,\n): Promise<void> {\n await cache.set(`auth:publickey:${hostId}`, publicKey);\n}\n\nexport async function getPublicKey(\n cache: ICache,\n hostId: string,\n): Promise<string | null> {\n return cache.get<string>(`auth:publickey:${hostId}`);\n}\n","/**\n * JWT sign / verify / refresh logic.\n * Pure functions — no side effects, no ICache dependency.\n */\n\nimport { SignJWT, jwtVerify, type JWTPayload } from 'jose';\nimport { randomUUID } from 'node:crypto';\nimport type { JwtPayload, TokenType } from '@kb-labs/gateway-contracts';\n\nconst ACCESS_TOKEN_TTL = 15 * 60; // 15 minutes in seconds\nconst REFRESH_TOKEN_TTL = 30 * 24 * 3600; // 30 days in seconds\n\nexport interface JwtConfig {\n /** Raw secret string — converted to TextEncoder internally */\n secret: string;\n}\n\nfunction getSecretKey(secret: string): Uint8Array {\n return new TextEncoder().encode(secret);\n}\n\nexport interface SignAccessTokenOptions {\n hostId: string;\n namespaceId: string;\n tier: 'free' | 'pro' | 'enterprise';\n type: TokenType;\n /** Permissions to embed in the JWT payload. Defaults to [] when absent. */\n permissions?: string[];\n}\n\nexport async function signAccessToken(\n opts: SignAccessTokenOptions,\n config: JwtConfig,\n): Promise<{ token: string; expiresIn: number }> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n\n const token = await new SignJWT({\n namespaceId: opts.namespaceId,\n tier: opts.tier,\n type: opts.type,\n permissions: opts.permissions ?? [],\n })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.hostId)\n .setIssuedAt(now)\n .setExpirationTime(now + ACCESS_TOKEN_TTL)\n .sign(key);\n\n return { token, expiresIn: ACCESS_TOKEN_TTL };\n}\n\nexport async function signRefreshToken(\n hostId: string,\n config: JwtConfig,\n): Promise<string> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n const jti = randomUUID();\n\n return new SignJWT({ type: 'refresh', jti })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(hostId)\n .setIssuedAt(now)\n .setExpirationTime(now + REFRESH_TOKEN_TTL)\n .sign(key);\n}\n\nexport async function verifyAccessToken(\n token: string,\n config: JwtConfig,\n): Promise<JwtPayload | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n return payload as unknown as JwtPayload;\n } catch {\n return null;\n }\n}\n\nexport async function verifyRefreshToken(\n token: string,\n config: JwtConfig,\n): Promise<{ hostId: string } | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n if ((payload as JWTPayload & { type?: string }).type !== 'refresh') {\n return null;\n }\n if (!payload.sub) {return null;}\n return { hostId: payload.sub };\n } catch {\n return null;\n }\n}\n\n// ─── User-flavoured tokens (ADR-0020, Phase 1.13) ────────────────────────\n//\n// These live alongside the machine-token helpers above. The shape is\n// deliberately different: `type: 'user'` for access, `type: 'refresh'`\n// + tenantId + familyId for refresh. `fam` (familyId) is **mandatory**\n// in BOTH access and refresh — verifyUserAccessToken rejects tokens\n// without it even on otherwise valid signatures. Without `fam` in the\n// access cookie, /auth/password/change cannot identify the current\n// session family to spare on revoke-all-others (CD-2).\n//\n// Payload never contains role / scopes / permissions (principle #3).\n// Authorization is resolved per-request by the PDP.\n\nexport interface SignUserTokenOptions {\n userId: string;\n tenantId: string;\n familyId: string;\n /** TTL in seconds. */\n ttlSec: number;\n}\n\nexport interface UserAccessPayload {\n userId: string;\n tenantId: string;\n familyId: string;\n jti: string;\n /** Token type discriminator. Access tokens carry 'user'. */\n type: 'user';\n iat: number;\n exp: number;\n}\n\nexport interface UserRefreshPayload {\n userId: string;\n tenantId: string;\n familyId: string;\n jti: string;\n type: 'refresh';\n iat: number;\n exp: number;\n}\n\nexport interface UserAccessSignResult {\n token: string;\n expiresInSec: number;\n jti: string;\n}\n\nexport async function signUserAccessToken(\n opts: SignUserTokenOptions,\n config: JwtConfig,\n): Promise<UserAccessSignResult> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n const jti = randomUUID();\n const token = await new SignJWT({\n tenantId: opts.tenantId,\n fam: opts.familyId,\n type: 'user',\n jti,\n })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.userId)\n .setIssuedAt(now)\n .setExpirationTime(now + opts.ttlSec)\n .sign(key);\n return { token, expiresInSec: opts.ttlSec, jti };\n}\n\n/**\n * Sign a user refresh token.\n *\n * When the caller already owns the `jti` (typical: the sessions-store\n * minted it inside `createSession`/`rotateRefresh`), pass it via the\n * second argument so the JWT and the store agree on the identifier.\n * Otherwise the helper generates one — used by tests that don't go\n * through sessions-store.\n */\nexport async function signUserRefreshToken(\n opts: SignUserTokenOptions & { jti?: string },\n config: JwtConfig,\n): Promise<string> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n const jti = opts.jti ?? randomUUID();\n return new SignJWT({\n tenantId: opts.tenantId,\n fam: opts.familyId,\n type: 'refresh',\n jti,\n })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.userId)\n .setIssuedAt(now)\n .setExpirationTime(now + opts.ttlSec)\n .sign(key);\n}\n\ninterface RawUserClaims extends JWTPayload {\n tenantId?: string;\n fam?: string;\n type?: string;\n jti?: string;\n}\n\nconst normaliseAccess = (raw: RawUserClaims): UserAccessPayload | null => {\n if (raw.type !== 'user') {return null;}\n if (!raw.sub || !raw.tenantId || !raw.fam || !raw.jti) {return null;}\n if (typeof raw.iat !== 'number' || typeof raw.exp !== 'number') {return null;}\n return {\n userId: raw.sub,\n tenantId: raw.tenantId,\n familyId: raw.fam,\n jti: raw.jti,\n type: 'user',\n iat: raw.iat,\n exp: raw.exp,\n };\n};\n\nconst normaliseRefresh = (raw: RawUserClaims): UserRefreshPayload | null => {\n if (raw.type !== 'refresh') {return null;}\n if (!raw.sub || !raw.tenantId || !raw.fam || !raw.jti) {return null;}\n if (typeof raw.iat !== 'number' || typeof raw.exp !== 'number') {return null;}\n return {\n userId: raw.sub,\n tenantId: raw.tenantId,\n familyId: raw.fam,\n jti: raw.jti,\n type: 'refresh',\n iat: raw.iat,\n exp: raw.exp,\n };\n};\n\nexport async function verifyUserAccessToken(\n token: string,\n config: JwtConfig,\n): Promise<UserAccessPayload | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n return normaliseAccess(payload as RawUserClaims);\n } catch {\n return null;\n }\n}\n\nexport async function verifyUserRefreshToken(\n token: string,\n config: JwtConfig,\n): Promise<UserRefreshPayload | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n return normaliseRefresh(payload as RawUserClaims);\n } catch {\n return null;\n }\n}\n","/**\n * AuthService — orchestrates registration, token issuance, and refresh.\n * Depends on ICache (via store) and JWT config.\n */\n\nimport type { ICache } from '@kb-labs/core-platform';\nimport type {\n RegisterRequest,\n RegisterResponse,\n TokenResponse,\n AuthContext,\n} from '@kb-labs/gateway-contracts';\nimport {\n buildClientRecord,\n generateClientSecret,\n saveClient,\n verifyClientSecret,\n saveRefreshToken,\n consumeRefreshToken,\n savePublicKey,\n isHandleTaken,\n getClientByHostId,\n} from './store.js';\nimport {\n signAccessToken,\n signRefreshToken,\n verifyAccessToken,\n verifyRefreshToken,\n type JwtConfig,\n} from './jwt.js';\n\nexport class AuthService {\n constructor(\n private readonly cache: ICache,\n private readonly jwtConfig: JwtConfig,\n ) {}\n\n // ── Register ──────────────────────────────────────────────────────────────\n\n async register(req: RegisterRequest): Promise<RegisterResponse & { clientSecret: string }> {\n if (req.handle && await isHandleTaken(this.cache, req.handle)) {\n throw Object.assign(new Error(`Handle '${req.handle}' is already taken`), { code: 'HANDLE_TAKEN' });\n }\n\n const secret = generateClientSecret();\n const record = buildClientRecord({\n name: req.name,\n capabilities: req.capabilities ?? [],\n publicKey: req.publicKey,\n secret,\n handle: req.handle,\n email: req.email,\n });\n\n await saveClient(this.cache, record);\n\n if (req.publicKey) {\n await savePublicKey(this.cache, record.hostId, req.publicKey);\n }\n\n return {\n clientId: record.clientId,\n clientSecret: secret,\n hostId: record.hostId,\n namespaceId: record.namespaceId,\n handle: record.handle,\n };\n }\n\n // ── Issue token pair ──────────────────────────────────────────────────────\n\n async issueTokens(clientId: string, clientSecret: string): Promise<TokenResponse | null> {\n const record = await verifyClientSecret(this.cache, clientId, clientSecret);\n if (!record) {return null;}\n\n const [{ token: accessToken, expiresIn }, refreshToken] = await Promise.all([\n signAccessToken(\n {\n hostId: record.hostId,\n namespaceId: record.namespaceId,\n tier: record.tier,\n type: 'machine',\n permissions: record.permissions ?? ['host:connect'],\n },\n this.jwtConfig,\n ),\n signRefreshToken(record.hostId, this.jwtConfig),\n ]);\n\n await saveRefreshToken(this.cache, refreshToken, record.hostId, record.namespaceId);\n\n return { accessToken, refreshToken, expiresIn, tokenType: 'Bearer' };\n }\n\n // ── Refresh token pair ────────────────────────────────────────────────────\n\n async refreshTokens(refreshToken: string): Promise<TokenResponse | null> {\n // Verify JWT signature first\n const jwtResult = await verifyRefreshToken(refreshToken, this.jwtConfig);\n if (!jwtResult) {return null;}\n\n // Consume from store (rotation — old token invalidated)\n const stored = await consumeRefreshToken(this.cache, refreshToken);\n if (!stored) {return null;}\n\n // Reload client record to get current permissions + tier.\n // getClientByHostId does a two-key lookup: hostIndex → clientId → record.\n const hostId = stored.hostId;\n const record = await getClientByHostId(this.cache, hostId);\n\n // Issue new pair\n const [{ token: accessToken, expiresIn }, newRefreshToken] = await Promise.all([\n signAccessToken(\n {\n hostId,\n namespaceId: stored.namespaceId,\n tier: record?.tier ?? 'free',\n type: 'machine',\n permissions: record?.permissions ?? ['host:connect'],\n },\n this.jwtConfig,\n ),\n signRefreshToken(hostId, this.jwtConfig),\n ]);\n\n await saveRefreshToken(this.cache, newRefreshToken, hostId, stored.namespaceId);\n\n return { accessToken, refreshToken: newRefreshToken, expiresIn, tokenType: 'Bearer' };\n }\n\n // ── Profile lookup ────────────────────────────────────────────────────────\n\n async me(hostId: string): Promise<{ hostId: string; handle?: string; namespaceId: string } | null> {\n const record = await getClientByHostId(this.cache, hostId);\n if (!record) { return null; }\n return { hostId: record.hostId, handle: record.handle, namespaceId: record.namespaceId };\n }\n\n // ── Verify access token → AuthContext ────────────────────────────────────\n\n async verify(token: string): Promise<AuthContext | null> {\n const payload = await verifyAccessToken(token, this.jwtConfig);\n if (!payload) {return null;}\n\n return {\n type: payload.type,\n userId: payload.sub,\n namespaceId: payload.namespaceId,\n tier: payload.tier,\n permissions: payload.permissions ?? ['host:connect'],\n };\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/users-store\n *\n * Document-backed store for `User` records (ADR-0020, Phase 1.1).\n *\n * Schema invariants:\n * - `(tenantId, email)` is a unique compound — one user per email per\n * tenant. The same email can exist in different tenants.\n * - `email` is **always** stored lowercased + trimmed (CD-4). Callers\n * may pass any casing; the store normalises before write/read.\n * - **No** password hash on this document. Credentials live in the\n * separate `credentials` collection (CD-6) so a future Google/Okta\n * provider can attach to the same `User` without a schema migration.\n *\n * Pattern follows `plugins/gateway/core/src/stores/host-store.ts`:\n * one-shot `ensureSchema()` plus per-method idempotent `ensureCollection`\n * registration.\n */\n\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\n\nconst COLLECTION = 'users';\n\nexport type UserStatus = 'pending' | 'active' | 'disabled';\n\nexport interface User {\n userId: string;\n tenantId: string;\n /** Lowercase + trimmed (CD-4). */\n email: string;\n displayName?: string;\n status: UserStatus;\n createdAt: number;\n updatedAt?: number;\n}\n\ninterface UserDoc extends BaseDocument {\n userId: string;\n tenantId: string;\n /** Always lowercase + trimmed; used by the unique index. */\n email: string;\n displayName?: string;\n status: UserStatus;\n}\n\n/**\n * Canonical email form used everywhere this store touches.\n *\n * Exported so other modules (the email-password provider, invites-store)\n * normalise identically — having one function prevents drift where one\n * caller forgets to trim and lookups silently miss.\n */\nexport const canonicalizeEmail = (raw: string): string => raw.trim().toLowerCase();\n\nconst docToUser = (doc: UserDoc): User => ({\n userId: doc.userId,\n tenantId: doc.tenantId,\n email: doc.email,\n displayName: doc.displayName,\n status: doc.status,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport interface CreateUserInput {\n userId: string;\n tenantId: string;\n email: string;\n status: UserStatus;\n displayName?: string;\n}\n\nexport class UsersStore {\n private initialised: Promise<void> | null = null;\n\n constructor(private readonly docs: IDocumentDatabase) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n // Logical primary key for \"user inside tenant\" lookups.\n { path: ['tenantId', 'email'], unique: true },\n // userId is the surrogate key callers reference everywhere.\n { path: 'userId', unique: true },\n { path: 'tenantId' },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async create(input: CreateUserInput): Promise<User> {\n await this.ensureSchema();\n const email = canonicalizeEmail(input.email);\n // The unique compound index turns a race-y duplicate into a driver-level\n // throw; we don't need an additional pre-check.\n await this.docs.insertOne<UserDoc>(COLLECTION, {\n userId: input.userId,\n tenantId: input.tenantId,\n email,\n displayName: input.displayName,\n status: input.status,\n });\n const created = await this.getById(input.userId);\n if (!created) {\n // Shouldn't happen — insertOne just succeeded. Defensive throw makes\n // a corrupted-index situation visible immediately rather than at the\n // next read.\n throw new Error(`users-store: inserted user ${input.userId} but read-back returned null`);\n }\n return created;\n }\n\n async getById(userId: string): Promise<User | null> {\n await this.ensureSchema();\n const doc = await this.docs.findById<UserDoc>(COLLECTION, userId);\n if (doc && doc.userId === userId) {\n return docToUser(doc);\n }\n // findById uses the driver's id field; we also keep `userId` explicitly\n // so the store works even when the driver-id differs from userId.\n const [byField] = await this.docs.find<UserDoc>(\n COLLECTION,\n { userId: { $eq: userId } },\n { limit: 1 },\n );\n return byField ? docToUser(byField) : null;\n }\n\n async findByEmailTenant(email: string, tenantId: string): Promise<User | null> {\n await this.ensureSchema();\n const normalised = canonicalizeEmail(email);\n const [doc] = await this.docs.find<UserDoc>(\n COLLECTION,\n { $and: [{ tenantId: { $eq: tenantId } }, { email: { $eq: normalised } }] },\n { limit: 1 },\n );\n return doc ? docToUser(doc) : null;\n }\n\n async setStatus(userId: string, status: UserStatus): Promise<void> {\n await this.ensureSchema();\n const existing = await this.getById(userId);\n if (!existing) {\n throw new Error(`users-store: setStatus on unknown userId=${userId}`);\n }\n await this.docs.updateMany<UserDoc>(\n COLLECTION,\n { userId: { $eq: userId } },\n { $set: { status } },\n );\n }\n\n /** List all users for a given tenant. */\n async listByTenant(tenantId: string): Promise<User[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<UserDoc>(\n COLLECTION,\n { tenantId: { $eq: tenantId } },\n );\n return docs.map(docToUser);\n }\n\n async delete(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<UserDoc>(COLLECTION, { userId: { $eq: userId } });\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/credentials-store\n *\n * Document-backed store for per-provider credentials (ADR-0020, CD-6,\n * Phase 1.2).\n *\n * `User` does not carry a password hash. Credentials live here, keyed by\n * the compound `(userId, providerId)`. Adding a new identity provider\n * (Google, Okta, LDAP, ...) means writing a row with a new `providerId`\n * — no schema migration on the `users` collection, no `passwordHash?:\n * null` quirks.\n *\n * Hash format and algorithm are the provider's concern: this store only\n * persists opaque strings. The `email-password` provider chooses bcrypt\n * with a configured cost.\n */\n\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\n\nconst COLLECTION = 'credentials';\n\nexport interface Credential {\n userId: string;\n providerId: string;\n /** Opaque to this store — bcrypt hash for email-password, OAuth refresh blob for future providers, etc. */\n hash: string;\n createdAt: number;\n updatedAt?: number;\n}\n\ninterface CredentialDoc extends BaseDocument {\n userId: string;\n providerId: string;\n hash: string;\n}\n\nconst docToCredential = (doc: CredentialDoc): Credential => ({\n userId: doc.userId,\n providerId: doc.providerId,\n hash: doc.hash,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport interface SetCredentialInput {\n userId: string;\n providerId: string;\n hash: string;\n}\n\nexport class CredentialsStore {\n private initialised: Promise<void> | null = null;\n\n constructor(private readonly docs: IDocumentDatabase) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n // Logical primary key.\n { path: ['userId', 'providerId'], unique: true },\n // Cascade delete needs userId alone.\n { path: 'userId' },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async setCredential(input: SetCredentialInput): Promise<void> {\n await this.ensureSchema();\n await this.docs.updateOne<CredentialDoc>(\n COLLECTION,\n {\n $and: [\n { userId: { $eq: input.userId } },\n { providerId: { $eq: input.providerId } },\n ],\n },\n {\n $set: {\n userId: input.userId,\n providerId: input.providerId,\n hash: input.hash,\n },\n },\n { upsert: true },\n );\n }\n\n async getCredential(userId: string, providerId: string): Promise<Credential | null> {\n await this.ensureSchema();\n const [doc] = await this.docs.find<CredentialDoc>(\n COLLECTION,\n {\n $and: [\n { userId: { $eq: userId } },\n { providerId: { $eq: providerId } },\n ],\n },\n { limit: 1 },\n );\n return doc ? docToCredential(doc) : null;\n }\n\n async deleteCredential(userId: string, providerId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<CredentialDoc>(COLLECTION, {\n $and: [\n { userId: { $eq: userId } },\n { providerId: { $eq: providerId } },\n ],\n });\n }\n\n /**\n * Cascade removal helper — call from the parent flow when a `User` is\n * deleted so we never leave dangling credentials in the store.\n */\n async deleteAllForUser(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<CredentialDoc>(COLLECTION, {\n userId: { $eq: userId },\n });\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/memberships-store\n *\n * Document-backed store for `Membership` records (ADR-0020, Phase 1.3).\n *\n * A membership is one row per `(userId, tenantId)` with a single\n * `groupId`. Group strings are hardcoded for this iteration —\n * `tenant-admin` and `tenant-member` — and consumed by the stub PDP.\n *\n * When the real RBAC + ReBAC engine lands (ClickUp 869def338) the\n * meaning of `groupId` becomes richer (multiple group memberships,\n * group hierarchies, relations) but the store API stays — the engine\n * sits behind the PDP and reads from here. The \"one membership per\n * (userId, tenantId)\" invariant may need to flex later; for now we keep\n * it tight so the stub PDP has unambiguous lookups.\n */\n\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\n\nconst COLLECTION = 'memberships';\n\n/** Hardcoded group ids for the stub PDP iteration. */\nexport type GroupId = 'tenant-admin' | 'tenant-member';\n\nexport interface Membership {\n userId: string;\n tenantId: string;\n groupId: GroupId;\n createdAt: number;\n updatedAt?: number;\n}\n\ninterface MembershipDoc extends BaseDocument {\n userId: string;\n tenantId: string;\n groupId: GroupId;\n}\n\nconst docToMembership = (doc: MembershipDoc): Membership => ({\n userId: doc.userId,\n tenantId: doc.tenantId,\n groupId: doc.groupId,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport interface AddMembershipInput {\n userId: string;\n tenantId: string;\n groupId: GroupId;\n}\n\nexport class MembershipsStore {\n private initialised: Promise<void> | null = null;\n\n constructor(private readonly docs: IDocumentDatabase) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n { path: ['userId', 'tenantId'], unique: true },\n { path: 'userId' },\n { path: 'tenantId' },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async addMembership(input: AddMembershipInput): Promise<void> {\n await this.ensureSchema();\n await this.docs.insertOne<MembershipDoc>(COLLECTION, {\n userId: input.userId,\n tenantId: input.tenantId,\n groupId: input.groupId,\n });\n }\n\n async setGroup(userId: string, tenantId: string, groupId: GroupId): Promise<void> {\n await this.ensureSchema();\n const [existing] = await this.docs.find<MembershipDoc>(\n COLLECTION,\n { $and: [{ userId: { $eq: userId } }, { tenantId: { $eq: tenantId } }] },\n { limit: 1 },\n );\n if (!existing) {\n throw new Error(\n `memberships-store: setGroup on missing membership userId=${userId} tenantId=${tenantId}`,\n );\n }\n await this.docs.updateMany<MembershipDoc>(\n COLLECTION,\n { $and: [{ userId: { $eq: userId } }, { tenantId: { $eq: tenantId } }] },\n { $set: { groupId } },\n );\n }\n\n async listByUser(userId: string): Promise<Membership[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<MembershipDoc>(\n COLLECTION,\n { userId: { $eq: userId } },\n { sort: { tenantId: 1 } },\n );\n return docs.map(docToMembership);\n }\n\n async listByTenant(tenantId: string): Promise<Membership[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<MembershipDoc>(\n COLLECTION,\n { tenantId: { $eq: tenantId } },\n { sort: { userId: 1 } },\n );\n return docs.map(docToMembership);\n }\n\n async removeMembership(userId: string, tenantId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<MembershipDoc>(\n COLLECTION,\n { $and: [{ userId: { $eq: userId } }, { tenantId: { $eq: tenantId } }] },\n );\n }\n\n /** Cascade hook — call when a `User` is deleted. */\n async removeAllForUser(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<MembershipDoc>(COLLECTION, { userId: { $eq: userId } });\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/invites-store\n *\n * Document-backed store for activation invites (ADR-0020, Phase 1.4).\n *\n * Invites are admin-issued tokens that bootstrap a user account: the\n * recipient activates by POSTing the token along with a password.\n *\n * Security details worth keeping straight:\n *\n * - Token plaintext **never** lands in the database. We store\n * `tokenHash = sha256(plain)` and look up by hash. A snapshot of the\n * `invites` collection is useless without the original tokens.\n * - The unique compound `(tenantId, email, status='active')` is not\n * easy to express across drivers, so uniqueness is enforced in code\n * on top of a non-unique compound index. Concurrent races during\n * `createInvite` are not a real attack vector here (admin UI is\n * single-clicked) and we accept the small theoretical window.\n * - TTL is best-effort on the index; **every read path** explicitly\n * checks `expiresAt > now` (CD-9). Without that, a not-yet-swept row\n * would be returned and the activation flow would accept it.\n * - Emails are canonicalised on the way in (CD-4) so admins can paste\n * \"Alice@Example.COM\" without breaking lookups.\n */\n\nimport { randomBytes, createHash } from 'node:crypto';\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\nimport { canonicalizeEmail } from './users-store.js';\nimport type { GroupId } from './memberships-store.js';\n\nconst COLLECTION = 'invites';\n\nexport type InviteStatus = 'active' | 'used' | 'revoked';\n\nexport interface InviteRecord {\n inviteId: string;\n email: string;\n tenantId: string;\n groupId: GroupId;\n status: InviteStatus;\n createdBy: string;\n createdAt: number;\n expiresAt: number;\n updatedAt?: number;\n}\n\ninterface InviteDoc extends BaseDocument {\n inviteId: string;\n email: string;\n tenantId: string;\n groupId: GroupId;\n status: InviteStatus;\n createdBy: string;\n expiresAt: number;\n tokenHash: string;\n}\n\nexport interface CreateInviteInput {\n email: string;\n tenantId: string;\n groupId: GroupId;\n createdBy: string;\n ttlMs: number;\n}\n\nexport interface CreateInviteResult {\n inviteId: string;\n activationToken: string;\n expiresAt: number;\n}\n\nconst TOKEN_BYTES = 32;\n\nconst generateToken = (): string => randomBytes(TOKEN_BYTES).toString('base64url');\n\nconst hashToken = (plain: string): string =>\n createHash('sha256').update(plain).digest('hex');\n\nconst generateInviteId = (): string => randomBytes(16).toString('base64url');\n\nconst docToInvite = (doc: InviteDoc): InviteRecord => ({\n inviteId: doc.inviteId,\n email: doc.email,\n tenantId: doc.tenantId,\n groupId: doc.groupId,\n status: doc.status,\n createdBy: doc.createdBy,\n expiresAt: doc.expiresAt,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport class InvitesStore {\n private initialised: Promise<void> | null = null;\n\n constructor(\n private readonly docs: IDocumentDatabase,\n private readonly now: () => number = Date.now,\n ) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n { path: 'inviteId', unique: true },\n { path: 'tokenHash', unique: true },\n // Active-invite uniqueness is enforced in code, so this index\n // is non-unique and just speeds the \"is there an active invite\n // for (email, tenantId)\" lookup.\n { path: ['tenantId', 'email', 'status'] },\n // TTL is best-effort; `findByToken` also checks expiresAt > now.\n // `ttl: 0` means \"sweep as soon as `expiresAt` is in the past\".\n { path: 'expiresAt', ttl: 0 },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async createInvite(input: CreateInviteInput): Promise<CreateInviteResult> {\n await this.ensureSchema();\n const email = canonicalizeEmail(input.email);\n\n // Uniqueness check — best effort, see module docstring.\n const [existingActive] = await this.docs.find<InviteDoc>(\n COLLECTION,\n {\n $and: [\n { tenantId: { $eq: input.tenantId } },\n { email: { $eq: email } },\n { status: { $eq: 'active' as InviteStatus } },\n { expiresAt: { $gt: this.now() } },\n ],\n },\n { limit: 1 },\n );\n if (existingActive) {\n throw new Error(\n `invites-store: an active invite already exists for email=${email} tenantId=${input.tenantId}; revoke it before issuing a new one`,\n );\n }\n\n const inviteId = generateInviteId();\n const activationToken = generateToken();\n const tokenHash = hashToken(activationToken);\n const expiresAt = this.now() + input.ttlMs;\n\n await this.docs.insertOne<InviteDoc>(COLLECTION, {\n inviteId,\n email,\n tenantId: input.tenantId,\n groupId: input.groupId,\n status: 'active',\n createdBy: input.createdBy,\n expiresAt,\n tokenHash,\n });\n\n return { inviteId, activationToken, expiresAt };\n }\n\n async findById(inviteId: string): Promise<InviteRecord | null> {\n await this.ensureSchema();\n const [doc] = await this.docs.find<InviteDoc>(\n COLLECTION,\n { inviteId: { $eq: inviteId } },\n { limit: 1 },\n );\n return doc ? docToInvite(doc) : null;\n }\n\n /**\n * Look up an invite by the plaintext activation token. Returns `null`\n * for missing, expired, used, or revoked invites — callers do not\n * need to filter further.\n *\n * Returns a discriminated result so callers can distinguish:\n * - { kind: 'not_found' } — token hash not in DB (garbage/unknown token)\n * - { kind: 'invalid' } — token exists but expired, used, or revoked\n * - { kind: 'ok', invite } — valid, active invite ready for activation\n */\n async findByToken(plainToken: string): Promise<\n | { kind: 'not_found' }\n | { kind: 'invalid' }\n | { kind: 'ok'; invite: InviteRecord }\n > {\n await this.ensureSchema();\n const tokenHash = hashToken(plainToken);\n const [doc] = await this.docs.find<InviteDoc>(\n COLLECTION,\n { tokenHash: { $eq: tokenHash } },\n { limit: 1 },\n );\n if (!doc) {return { kind: 'not_found' };}\n if (doc.status !== 'active' || doc.expiresAt <= this.now()) {\n // CD-9 explicit expiry check — TTL sweep is best-effort.\n return { kind: 'invalid' };\n }\n return { kind: 'ok', invite: docToInvite(doc) };\n }\n\n /**\n * Atomically mark an active invite as used.\n *\n * Returns `true` if THIS call flipped the invite from `active` → `used`,\n * `false` if it was already used/revoked (or unknown). The filter pins\n * `status: 'active'`, so a concurrent double-activation of the same token\n * results in exactly one caller seeing `true` — the activation flow relies\n * on this to close the TOCTOU window between `findByToken` and account\n * creation (a second parallel request must NOT create a duplicate account).\n */\n async consume(inviteId: string): Promise<boolean> {\n await this.ensureSchema();\n const modified = await this.docs.updateMany<InviteDoc>(\n COLLECTION,\n { $and: [{ inviteId: { $eq: inviteId } }, { status: { $eq: 'active' as InviteStatus } }] },\n { $set: { status: 'used' } },\n );\n return modified > 0;\n }\n\n async revoke(inviteId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.updateMany<InviteDoc>(\n COLLECTION,\n { $and: [{ inviteId: { $eq: inviteId } }, { status: { $eq: 'active' as InviteStatus } }] },\n { $set: { status: 'revoked' } },\n );\n }\n\n /** List all invites for a tenant (active + used + revoked). */\n async listByTenant(tenantId: string): Promise<InviteRecord[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<InviteDoc>(\n COLLECTION,\n { tenantId: { $eq: tenantId } },\n );\n return docs.map(docToInvite);\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/sessions-store\n *\n * Session families + refresh tokens (ADR-0020, Phase 1.5).\n *\n * This module owns three things:\n *\n * 1. **Session families.** One row per device. The family is the unit\n * of revocation — \"log out this device\", \"kick this user from all\n * devices\", \"the password just changed, keep the current device\".\n * 2. **Refresh tokens.** One-shot rotation: every refresh request\n * consumes the old jti and issues a new one in the same family.\n * 3. **Reuse detection + 5-second grace window (CD-5).** Reusing an\n * already-consumed refresh is either a stolen cookie or a legit\n * multi-tab race. The grace window resolves the ambiguity: if the\n * client presents the same already-consumed jti within 5s, we\n * return the same replacement we issued first time. Beyond 5s, we\n * treat it as theft and kill the entire family.\n *\n * Notes on storage:\n *\n * - Refresh tokens carry an explicit `expiresAt` and we check it\n * manually on every read (CD-9) — the TTL index sweep is best-effort.\n * - `rotateRefresh` runs inside a `transaction(cb)` so a concurrent\n * second caller sees the consumed state and goes through the\n * grace/reuse branch instead of inserting a parallel replacement.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport type {\n BaseDocument,\n IDocumentDatabase,\n IDocumentTransaction,\n} from '@kb-labs/core-platform/adapters';\n\nconst FAMILIES = 'session_families';\nconst REFRESHES = 'refresh_tokens';\n\nexport interface SessionFamily {\n familyId: string;\n userId: string;\n tenantId: string;\n createdAt: number;\n lastUsedAt: number;\n userAgent?: string;\n ipFirst?: string;\n}\n\ninterface FamilyDoc extends BaseDocument {\n familyId: string;\n userId: string;\n tenantId: string;\n createdAt: number;\n lastUsedAt: number;\n userAgent?: string;\n ipFirst?: string;\n}\n\ninterface RefreshDoc extends BaseDocument {\n jti: string;\n familyId: string;\n userId: string;\n expiresAt: number;\n consumedAt?: number;\n replacedBy?: string;\n}\n\nexport class RefreshNotFoundError extends Error {\n constructor() { super('refresh token not found'); this.name = 'RefreshNotFoundError'; }\n}\nexport class RefreshExpiredError extends Error {\n constructor() { super('refresh token expired'); this.name = 'RefreshExpiredError'; }\n}\nexport class RefreshReuseDetectedError extends Error {\n constructor(public readonly familyId: string) {\n super(`refresh token reuse detected (family ${familyId} killed)`);\n this.name = 'RefreshReuseDetectedError';\n }\n}\n\nexport interface SessionsStoreOptions {\n /** Override `Date.now` — only used in tests. Defaults to `Date.now`. */\n now?: () => number;\n /** Refresh token TTL in ms. */\n refreshTtlMs: number;\n /** Grace window for legitimate retries (CD-5). Recommended 5_000ms. */\n graceWindowMs: number;\n}\n\nexport interface CreateSessionInput {\n userId: string;\n tenantId: string;\n deviceCtx: { userAgent?: string; ip?: string };\n}\n\nexport interface CreateSessionResult {\n familyId: string;\n refreshJti: string;\n refreshExpiresAt: number;\n}\n\nexport interface RotateRefreshResult {\n newJti: string;\n familyId: string;\n newRefreshExpiresAt: number;\n}\n\nconst newId = (bytes = 16): string => randomBytes(bytes).toString('base64url');\n\nexport class SessionsStore {\n private initialised: Promise<void> | null = null;\n private readonly now: () => number;\n private readonly refreshTtlMs: number;\n private readonly graceWindowMs: number;\n\n constructor(\n private readonly docs: IDocumentDatabase,\n opts: SessionsStoreOptions,\n ) {\n this.now = opts.now ?? Date.now;\n this.refreshTtlMs = opts.refreshTtlMs;\n this.graceWindowMs = opts.graceWindowMs;\n }\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(FAMILIES, {\n indexes: [\n { path: 'familyId', unique: true },\n { path: 'userId' },\n ],\n });\n await this.docs.ensureCollection(REFRESHES, {\n indexes: [\n { path: 'jti', unique: true },\n { path: 'familyId' },\n { path: 'userId' },\n // Best-effort sweep; `rotateRefresh` checks expiresAt > now (CD-9).\n { path: 'expiresAt', ttl: 0 },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n await this.ensureSchema();\n const familyId = newId();\n const refreshJti = newId();\n const now = this.now();\n const refreshExpiresAt = now + this.refreshTtlMs;\n\n await this.docs.transaction(async (tx: IDocumentTransaction) => {\n await tx.insertOne<FamilyDoc>(FAMILIES, {\n familyId,\n userId: input.userId,\n tenantId: input.tenantId,\n lastUsedAt: now,\n userAgent: input.deviceCtx.userAgent,\n ipFirst: input.deviceCtx.ip,\n });\n await tx.insertOne<RefreshDoc>(REFRESHES, {\n jti: refreshJti,\n familyId,\n userId: input.userId,\n expiresAt: refreshExpiresAt,\n });\n });\n\n return { familyId, refreshJti, refreshExpiresAt };\n }\n\n /**\n * Consume `oldJti` and issue a new refresh in the same family.\n *\n * Throws:\n * - `RefreshNotFoundError` if jti is unknown or its family was revoked.\n * - `RefreshExpiredError` if jti is past `expiresAt` (family stays alive).\n * - `RefreshReuseDetectedError` if jti was already consumed and the\n * grace window has passed — family is killed before throwing.\n *\n * On grace-window retry (same already-consumed jti within\n * `graceWindowMs`) the previously-issued replacement is returned and\n * no new refresh is created.\n */\n async rotateRefresh(oldJti: string): Promise<RotateRefreshResult> {\n await this.ensureSchema();\n const now = this.now();\n const newJti = newId();\n const newExpiresAt = now + this.refreshTtlMs;\n\n // Outcome tag: lets us decide the side-effect AFTER the transaction.\n // We cannot throw `RefreshReuseDetectedError` from inside the\n // transaction callback — that rolls back the family deletion. So\n // reuse is detected inside (atomically, against a consistent\n // snapshot) and the family is killed outside.\n type Outcome =\n | { tag: 'ok'; result: RotateRefreshResult }\n | { tag: 'reuse'; familyId: string }\n | { tag: 'not-found' }\n | { tag: 'expired' };\n\n const outcome = await this.docs.transaction<Outcome>(async (tx) => {\n const [current] = await tx.find<RefreshDoc>(\n REFRESHES,\n { jti: { $eq: oldJti } },\n { limit: 1 },\n );\n if (!current) {return { tag: 'not-found' };}\n\n // Reuse / grace branch.\n if (current.consumedAt !== undefined) {\n const elapsed = now - current.consumedAt;\n if (elapsed <= this.graceWindowMs && current.replacedBy) {\n // Legitimate retry — return what we issued before.\n const [replacement] = await tx.find<RefreshDoc>(\n REFRESHES,\n { jti: { $eq: current.replacedBy } },\n { limit: 1 },\n );\n if (replacement) {\n return {\n tag: 'ok',\n result: {\n newJti: replacement.jti,\n familyId: current.familyId,\n newRefreshExpiresAt: replacement.expiresAt,\n },\n };\n }\n // Replacement vanished (family revoked between consume and retry).\n // Fall through to reuse detection.\n }\n // If the token is also past its expiry, it was legitimately rotated long ago —\n // treat as stale retry, not theft. Family stays alive.\n if (current.expiresAt <= now) {\n return { tag: 'expired' };\n }\n return { tag: 'reuse', familyId: current.familyId };\n }\n\n // Natural expiry — family is fine, this refresh just timed out.\n if (current.expiresAt <= now) {\n return { tag: 'expired' };\n }\n\n // Happy path — consume old, insert new, bump family lastUsedAt.\n await tx.updateMany<RefreshDoc>(\n REFRESHES,\n { jti: { $eq: oldJti } },\n { $set: { consumedAt: now, replacedBy: newJti } },\n );\n await tx.insertOne<RefreshDoc>(REFRESHES, {\n jti: newJti,\n familyId: current.familyId,\n userId: current.userId,\n expiresAt: newExpiresAt,\n });\n await tx.updateMany<FamilyDoc>(\n FAMILIES,\n { familyId: { $eq: current.familyId } },\n { $set: { lastUsedAt: now } },\n );\n\n return {\n tag: 'ok',\n result: { newJti, familyId: current.familyId, newRefreshExpiresAt: newExpiresAt },\n };\n });\n\n switch (outcome.tag) {\n case 'ok':\n return outcome.result;\n case 'not-found':\n throw new RefreshNotFoundError();\n case 'expired':\n throw new RefreshExpiredError();\n case 'reuse':\n // Kill the family — outside the read transaction so the writes commit.\n await this.revokeFamily(outcome.familyId);\n throw new RefreshReuseDetectedError(outcome.familyId);\n }\n }\n\n async revokeFamily(familyId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.transaction(async (tx) => {\n await tx.deleteMany<RefreshDoc>(REFRESHES, { familyId: { $eq: familyId } });\n await tx.deleteMany<FamilyDoc>(FAMILIES, { familyId: { $eq: familyId } });\n });\n }\n\n async revokeAllUserSessions(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.transaction(async (tx) => {\n await tx.deleteMany<RefreshDoc>(REFRESHES, { userId: { $eq: userId } });\n await tx.deleteMany<FamilyDoc>(FAMILIES, { userId: { $eq: userId } });\n });\n }\n\n async revokeAllUserSessionsExcept(userId: string, exceptFamilyId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.transaction(async (tx) => {\n const families = await tx.find<FamilyDoc>(FAMILIES, { userId: { $eq: userId } });\n const targets = families.filter(f => f.familyId !== exceptFamilyId).map(f => f.familyId);\n if (targets.length === 0) {return;}\n await tx.deleteMany<RefreshDoc>(\n REFRESHES,\n { $and: [{ userId: { $eq: userId } }, { familyId: { $in: targets } }] },\n );\n await tx.deleteMany<FamilyDoc>(\n FAMILIES,\n { $and: [{ userId: { $eq: userId } }, { familyId: { $in: targets } }] },\n );\n });\n }\n\n async listFamiliesByUser(userId: string): Promise<SessionFamily[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<FamilyDoc>(\n FAMILIES,\n { userId: { $eq: userId } },\n { sort: { lastUsedAt: -1 } },\n );\n return docs.map(d => ({\n familyId: d.familyId,\n userId: d.userId,\n tenantId: d.tenantId,\n createdAt: d.createdAt,\n lastUsedAt: d.lastUsedAt,\n userAgent: d.userAgent,\n ipFirst: d.ipFirst,\n }));\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/password-policy\n *\n * Password validation rules (ADR-0020, Phase 1.6).\n *\n * Length: 8..256. No complexity rules — NIST 800-63B explicitly\n * recommends against them.\n *\n * HIBP check via k-anonymity (https://haveibeenpwned.com/API/v3#PwnedPasswords):\n * we send the first 5 hex chars of SHA-1(password) and grep the\n * suffix list locally. The full password never leaves the process.\n *\n * Availability vs. UX trade-off: HIBP outages return `{ ok: true,\n * warning: 'hibp_unavailable' }`. A security check that hard-fails on\n * network glitches breaks more user flows than it protects — we log\n * the warning and let the activation/change-password flow continue.\n */\n\nimport { createHash } from 'node:crypto';\n\nexport type ValidationResult =\n | { ok: true; warning?: 'hibp_unavailable' }\n | { ok: false; reason: 'too_short' | 'too_long' | 'pwned' };\n\nexport interface PasswordPolicyLogger {\n warn(...args: unknown[]): void;\n info(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport interface PasswordPolicyOptions {\n minLength: number;\n maxLength: number;\n hibpEnabled: boolean;\n /** Override for tests. Defaults to global `fetch`. */\n fetch?: typeof fetch;\n /** Override for tests / production logger. Defaults to console-shaped no-op. */\n logger?: PasswordPolicyLogger;\n}\n\nexport interface PasswordPolicy {\n validate(plain: string): Promise<ValidationResult>;\n}\n\nconst noopLogger: PasswordPolicyLogger = {\n warn: () => undefined,\n info: () => undefined,\n error: () => undefined,\n};\n\nconst sha1Hex = (input: string): string =>\n createHash('sha1').update(input).digest('hex').toUpperCase();\n\n/**\n * Returns the count of breach occurrences if the (sha1, suffix) is in\n * HIBP, or `0` if absent. Throws on network / non-2xx errors.\n */\nconst queryHibp = async (\n plain: string,\n doFetch: typeof fetch,\n): Promise<number> => {\n const hash = sha1Hex(plain);\n const prefix = hash.slice(0, 5);\n const wantedSuffix = hash.slice(5);\n const resp = await doFetch(`https://api.pwnedpasswords.com/range/${prefix}`);\n if (!resp.ok) {\n throw new Error(`HIBP ${resp.status}`);\n }\n const text = await resp.text();\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) {continue;}\n const sepIdx = trimmed.indexOf(':');\n if (sepIdx < 0) {continue;}\n const suffix = trimmed.slice(0, sepIdx).toUpperCase();\n if (suffix === wantedSuffix) {\n const count = Number.parseInt(trimmed.slice(sepIdx + 1), 10);\n return Number.isFinite(count) ? count : 1;\n }\n }\n return 0;\n};\n\nexport const createPasswordPolicy = (opts: PasswordPolicyOptions): PasswordPolicy => {\n const log = opts.logger ?? noopLogger;\n const doFetch = opts.fetch ?? fetch;\n\n return {\n async validate(plain: string): Promise<ValidationResult> {\n if (plain.length < opts.minLength) {\n return { ok: false, reason: 'too_short' };\n }\n if (plain.length > opts.maxLength) {\n return { ok: false, reason: 'too_long' };\n }\n if (!opts.hibpEnabled) {\n return { ok: true };\n }\n try {\n const count = await queryHibp(plain, doFetch);\n if (count > 0) {\n return { ok: false, reason: 'pwned' };\n }\n return { ok: true };\n } catch (err) {\n log.warn('password-policy: HIBP unavailable, accepting password', { err: String(err) });\n return { ok: true, warning: 'hibp_unavailable' };\n }\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/csrf\n *\n * Double-submit CSRF helpers (ADR-0020, Phase 1.7).\n *\n * The contract is intentionally minimal: a token is just an opaque\n * random string and verification is a constant-time string compare.\n * No server-side state, no rotation logic — the cookie + header pair\n * is the entire protocol.\n *\n * The `Path=/` non-HttpOnly cookie is set in the route layer\n * alongside `kb_access`. Studio JS reads it via `document.cookie` and\n * echoes it via `X-CSRF-Token` on every mutating request.\n */\n\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\n\n/**\n * Generate a fresh 32-byte token, base64url-encoded.\n */\nexport const issueCsrfToken = (): string => randomBytes(32).toString('base64url');\n\n/**\n * Verify the double-submit pair.\n *\n * Returns `true` only when both inputs are non-empty strings of the\n * same length and equal under a constant-time compare. Length\n * mismatches return `false` without involving timingSafeEqual (which\n * throws on length mismatch).\n */\nexport const verifyCsrfToken = (\n cookie: string | undefined,\n header: string | undefined,\n): boolean => {\n if (!cookie || !header) {return false;}\n if (cookie.length !== header.length) {return false;}\n const a = Buffer.from(cookie, 'utf8');\n const b = Buffer.from(header, 'utf8');\n // Buffers of identical length — safe to call timingSafeEqual.\n return timingSafeEqual(a, b);\n};\n","/**\n * @module @kb-labs/gateway-auth/rate-limit\n *\n * Fixed-window rate limiter on top of `IKVStore` (ADR-0020, Phase 1.8).\n *\n * Used for per-email login limits and activation rate-limiting.\n * Per-IP limits are configured at the gateway layer itself (its\n * existing rate-limit plugin) and are not touched here.\n *\n * Fixed window, not sliding: TTL is applied **once** on the first\n * increment and never extended. Subsequent increments within the\n * window just bump the counter. Once the key expires, the counter\n * starts over.\n *\n * The choice of fixed vs sliding matters: a sliding window would mean\n * \"5 attempts in any 60-second window\", which an attacker can game by\n * pacing exactly under the limit forever. Fixed window puts a hard\n * ceiling per window and the counter actually resets.\n */\n\nimport type { IKVStore } from '@kb-labs/core-platform/adapters';\n\nexport interface RateLimitConfig {\n max: number;\n windowMs: number;\n}\n\nexport type RateLimitResult =\n | { allowed: true; remaining: number }\n | { allowed: false; retryAfterSec: number };\n\nexport interface RateLimiter {\n /**\n * Increment the counter and report whether this hit is allowed.\n * Use on the event you want to count (e.g. a FAILED login attempt).\n */\n check(key: string, cfg: RateLimitConfig): Promise<RateLimitResult>;\n /**\n * Read the current counter WITHOUT incrementing and report whether the\n * next `check()` would be allowed.\n *\n * This exists so a caller can reject early — before doing expensive work\n * such as a bcrypt compare — once the window is already exhausted. Without\n * a peek, the only way to know the limit is hit is to `check()` (which\n * increments), forcing the expensive work to run first. peek() lets the\n * login handler gate bcrypt on the existing failure count, closing a\n * CPU-exhaustion DoS where an attacker spams requests past the limit and\n * still pays for a bcrypt round-trip on each.\n */\n peek(key: string, cfg: RateLimitConfig): Promise<RateLimitResult>;\n}\n\nconst retryAfterFor = async (kv: IKVStore, key: string, cfg: RateLimitConfig): Promise<number> => {\n const remainingTtlMs = await kv.ttl(key);\n return remainingTtlMs !== null\n ? Math.max(1, Math.ceil(remainingTtlMs / 1000))\n : Math.ceil(cfg.windowMs / 1000);\n};\n\nexport const createRateLimiter = (kv: IKVStore): RateLimiter => ({\n async check(key: string, cfg: RateLimitConfig): Promise<RateLimitResult> {\n const count = await kv.incr(key, 1);\n if (count === 1) {\n // First hit in this window — set the TTL. Subsequent hits will\n // just bump the counter without touching the TTL.\n await kv.expire(key, cfg.windowMs);\n }\n if (count > cfg.max) {\n return { allowed: false, retryAfterSec: await retryAfterFor(kv, key, cfg) };\n }\n return { allowed: true, remaining: cfg.max - count };\n },\n\n async peek(key: string, cfg: RateLimitConfig): Promise<RateLimitResult> {\n const raw = await kv.get<number>(key);\n const count = typeof raw === 'number' ? raw : 0;\n // The window is exhausted once the counter has already reached `max`:\n // the next check() would push it to max+1 and be denied. Reject now,\n // before any expensive work, without touching the counter.\n if (count >= cfg.max) {\n return { allowed: false, retryAfterSec: await retryAfterFor(kv, key, cfg) };\n }\n return { allowed: true, remaining: cfg.max - count };\n },\n});\n","/**\n * @module @kb-labs/gateway-auth/tenant-resolver\n *\n * Map `Host`-header → `tenantId` (ADR-0020, Phase 1.9).\n *\n * Pattern is configurable (`{tenant}.kblabs.ru` in cloud) so the same\n * resolver can be used in dev (`{tenant}.localhost`) or for on-prem\n * customers (`{tenant}.acme.example.com`).\n *\n * Returns `null` instead of throwing on bad/reserved hosts — the\n * middleware then renders the right 4xx and avoids surfacing an\n * internal error. Slug rules are strict (`[a-z0-9-]{2,40}`, no\n * leading/trailing dash) so a stray \"weird\" subdomain doesn't become a\n * valid tenant by accident.\n */\n\n// 2..40 chars; first and last must be alnum; middle can include hyphens.\nconst SLUG_RE = /^[a-z0-9][a-z0-9-]{0,38}[a-z0-9]$/;\n\nexport const RESERVED_SUBDOMAINS: ReadonlySet<string> = new Set([\n 'api',\n 'www',\n 'docs',\n 'studio',\n 'mail',\n 'admin',\n 'static',\n 'cdn',\n]);\n\nexport interface TenantResolverOptions {\n /** Host pattern containing exactly one `{tenant}` placeholder. */\n pattern: string;\n /** Optional override of the reserved set (must remain a superset of the default in production). */\n reserved?: ReadonlySet<string>;\n}\n\nexport interface TenantResolver {\n resolve(host: string | undefined): string | null;\n}\n\n/**\n * Compile the `{tenant}.example.com` pattern into a regex.\n *\n * Apex placeholder is escaped (so `.` matches a literal `.`), and the\n * `{tenant}` placeholder is replaced by a non-greedy match that the\n * outer code then validates against `SLUG_RE`.\n */\nconst compilePattern = (pattern: string): RegExp => {\n const escapedTail = pattern\n .replace('{tenant}.', '')\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return new RegExp(`^([^.]+)\\\\.${escapedTail}$`);\n};\n\nexport const createTenantResolver = (opts: TenantResolverOptions): TenantResolver => {\n const re = compilePattern(opts.pattern);\n const reserved = opts.reserved ?? RESERVED_SUBDOMAINS;\n const cache = new Map<string, string | null>();\n const CACHE_LIMIT = 100;\n\n return {\n resolve(host: string | undefined): string | null {\n if (!host) {return null;}\n const normalised = host.toLowerCase().split(':')[0] ?? '';\n if (!normalised) {return null;}\n if (cache.has(normalised)) {return cache.get(normalised)!;}\n\n let result: string | null = null;\n const m = normalised.match(re);\n if (m) {\n const candidate = m[1]!;\n if (SLUG_RE.test(candidate) && !reserved.has(candidate)) {\n result = candidate;\n }\n }\n\n if (cache.size >= CACHE_LIMIT) {\n // Crude LRU — drop the oldest insert.\n const firstKey = cache.keys().next().value;\n if (firstKey !== undefined) {cache.delete(firstKey);}\n }\n cache.set(normalised, result);\n return result;\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/providers/email-password\n *\n * Built-in identity provider for the local email-password flow\n * (ADR-0020, Phase 1.10). Implements `IIdentityProvider` exactly as a\n * future Google/Okta provider would — `email-password` has no special\n * status beyond being the one we ship.\n *\n * Two design notes worth keeping in mind:\n *\n * - **Constant-time response (CD-8).** Every not-ok path runs a real\n * bcrypt compare against a fixed dummy hash. Without this, a missing\n * user returns in microseconds while a wrong-password attempt takes\n * ~hundreds of milliseconds — a textbook user-enumeration timing\n * channel. The discriminated `reason` is for **logs only**; the HTTP\n * layer collapses it to a single opaque \"invalid_credentials\".\n *\n * - **No `setCredential` here.** This provider only *reads* credentials.\n * The activation flow writes the hash via the service layer, which\n * chooses the bcrypt cost from config. Keeping write logic out of\n * the provider means future Google/Okta providers don't carry a\n * meaningless `setCredential` method.\n */\n\nimport bcrypt from 'bcryptjs';\nimport type {\n IIdentityProvider,\n IdentityResult,\n IdentityUserPort,\n IdentityCredentialPort,\n} from '@kb-labs/core-contracts';\nimport { canonicalizeEmail } from '../users-store.js';\n\nexport interface EmailPasswordProviderOptions {\n /**\n * Narrow read port over users (DD-2). The gateway's `UsersStore`\n * satisfies this structurally; the provider only ever reads.\n */\n users: IdentityUserPort;\n /** Narrow read port over credentials (DD-2). */\n credentials: IdentityCredentialPort;\n /** The tenant this provider instance authenticates against. */\n tenantId: string;\n /** bcrypt cost used for the dummy compare timing. Should match the\n * cost used by the activation flow so dummy/real timings are similar. */\n bcryptCost: number;\n}\n\n/**\n * A pre-computed bcrypt hash used for the dummy compare on every\n * not-ok path. The actual plaintext is irrelevant — what matters is\n * that `bcrypt.compare` runs against a real hash of the right cost.\n *\n * NOTE: This is computed lazily once per provider instance from the\n * configured `bcryptCost` so dummy hashes match the cost the\n * activation flow will produce.\n */\nconst dummyHashFor = (cost: number): Promise<string> =>\n bcrypt.hash('dummy-password-for-timing', cost);\n\ninterface PasswordInput {\n email: string;\n password: string;\n}\n\nconst isPasswordInput = (raw: unknown): raw is PasswordInput => {\n if (!raw || typeof raw !== 'object') {return false;}\n const r = raw as Record<string, unknown>;\n return typeof r.email === 'string' && typeof r.password === 'string'\n && r.email.length > 0 && r.password.length > 0;\n};\n\nexport const createEmailPasswordProvider = (\n opts: EmailPasswordProviderOptions,\n): IIdentityProvider => {\n // Compute the dummy hash once; reused on every not-ok path.\n const dummyHashPromise = dummyHashFor(opts.bcryptCost);\n\n return {\n id: 'email-password',\n kind: 'password',\n\n async authenticate(input: unknown): Promise<IdentityResult> {\n if (!isPasswordInput(input)) {\n // Even on shape-invalid input, do a dummy bcrypt so timing\n // does not give away \"well-formed vs malformed payload\".\n await bcrypt.compare('x', await dummyHashPromise);\n return { ok: false, reason: 'invalid' };\n }\n\n const email = canonicalizeEmail(input.email);\n const user = await opts.users.findByEmailTenant(email, opts.tenantId);\n\n if (!user) {\n await bcrypt.compare(input.password, await dummyHashPromise);\n return { ok: false, reason: 'unknown' };\n }\n\n if (user.status !== 'active') {\n await bcrypt.compare(input.password, await dummyHashPromise);\n return { ok: false, reason: 'disabled' };\n }\n\n const cred = await opts.credentials.getCredential(user.userId, 'email-password');\n if (!cred) {\n await bcrypt.compare(input.password, await dummyHashPromise);\n return { ok: false, reason: 'unknown' };\n }\n\n const match = await bcrypt.compare(input.password, cred.hash);\n if (!match) {\n return { ok: false, reason: 'invalid' };\n }\n\n return { ok: true, email: user.email, externalId: user.userId };\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/providers/oidc\n *\n * Built-in generic OIDC redirect provider (ADR-0020, Step 5).\n *\n * Implements the authorization-code flow against any spec-compliant OIDC\n * IdP (Google / Okta / Auth0 / Keycloak / Azure AD): `startAuthorization`\n * builds the upstream authorize URL plus a per-attempt `nonce` (and optional\n * PKCE `code_verifier`); `authenticate` exchanges the code at the token\n * endpoint and verifies the returned ID token before returning the canonical\n * identity. The gateway then does email→user→session as for any provider.\n *\n * Security guarantees this provider owns (not \"quality of a custom\" — these\n * are the floor we promise):\n *\n * - **alg allowlist** — `jwtVerify(..., { algorithms: ['RS256','ES256'] })`.\n * `none` and HS* are rejected (alg-confusion / key-confusion). Same lesson\n * as pinning HS256 in jwt.ts: never let the token choose its own algorithm\n * class.\n * - **iss / aud** — verified against the configured issuer and clientId.\n * - **exp / nonce** — exp enforced by jose; nonce must echo the per-attempt\n * value we stashed in the state session (replay / token-injection guard).\n * - **email_verified === true** — required unless `allowUnverifiedEmail`,\n * else an IdP that federates unverified emails becomes an account-takeover\n * vector.\n * - **issuer https-only** — discovery / JWKS targets are admin-controlled but\n * we still refuse plaintext.\n * - **secret hygiene** — `clientSecret` is read from an env var\n * (`clientSecretEnv`) by default; nothing secret is ever logged (no code /\n * id_token / access_token / client_secret in any log line).\n *\n * `fetch` is injected (DD-8) so the whole flow is testable against a locally\n * signed token with a fake transport — no network, no global mocks. JWKS are\n * fetched via the same injected `fetch` and verified with a local JWK set,\n * with a single refetch on key rotation.\n */\n\nimport { z } from 'zod';\nimport {\n jwtVerify,\n createLocalJWKSet,\n type JSONWebKeySet,\n type JWTPayload,\n} from 'jose';\nimport { randomBytes, createHash } from 'node:crypto';\nimport type {\n IRedirectIdentityProvider,\n IdentityResult,\n IdentityProviderDeps,\n RedirectStartContext,\n RedirectStartResult,\n} from '@kb-labs/core-contracts';\nimport { canonicalizeEmail } from '../users-store.js';\n\n// ── Config schema ───────────────────────────────────────────────────────────\n\nconst OidcConfigSchema = z\n .object({\n type: z.literal('oidc'),\n id: z.string().min(1),\n issuer: z\n .string()\n .url()\n .refine((u) => u.startsWith('https://'), { message: 'issuer must be https' }),\n clientId: z.string().min(1),\n /** Direct secret (discouraged — prefer clientSecretEnv). */\n clientSecret: z.string().min(1).optional(),\n /** Name of the env var holding the secret (preferred). */\n clientSecretEnv: z.string().min(1).optional(),\n /** OAuth scopes; `openid` is always included. Default `['openid','email']`. */\n scopes: z.array(z.string()).optional(),\n /** Enable PKCE (S256). */\n pkce: z.boolean().optional(),\n /** Accept tokens whose email_verified is false (default false). */\n allowUnverifiedEmail: z.boolean().optional(),\n })\n .passthrough();\n\nexport type OidcProviderConfig = z.infer<typeof OidcConfigSchema>;\n\n// ── Discovery / JWKS ──────────────────────────────────────────────────────────\n\ninterface Discovery {\n authorization_endpoint: string;\n token_endpoint: string;\n jwks_uri: string;\n}\n\nconst VERIFY_ALGS = ['RS256', 'ES256'];\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction resolveSecret(config: OidcProviderConfig): string {\n if (config.clientSecretEnv) {\n const fromEnv = process.env[config.clientSecretEnv];\n if (!fromEnv) {\n throw new Error(\n `oidc provider \"${config.id}\": clientSecretEnv \"${config.clientSecretEnv}\" is not set`,\n );\n }\n return fromEnv;\n }\n if (config.clientSecret) {\n return config.clientSecret;\n }\n throw new Error(\n `oidc provider \"${config.id}\": no client secret — set clientSecretEnv (preferred) or clientSecret`,\n );\n}\n\ninterface CallbackBundle {\n code?: string;\n error?: string;\n session?: { nonce?: string; codeVerifier?: string };\n redirectUri?: string;\n}\n\nfunction asBundle(input: unknown): CallbackBundle {\n return (input && typeof input === 'object' ? input : {}) as CallbackBundle;\n}\n\n// ── Factory ─────────────────────────────────────────────────────────────────\n\nexport function createOidcProvider(\n rawConfig: unknown,\n deps: IdentityProviderDeps,\n): IRedirectIdentityProvider {\n const config = OidcConfigSchema.parse(rawConfig);\n const secret = resolveSecret(config);\n const scopes = config.scopes && config.scopes.length > 0 ? config.scopes : ['openid', 'email'];\n // `openid` is mandatory for OIDC.\n const scopeParam = [...new Set(['openid', ...scopes])].join(' ');\n const doFetch = deps.fetch ?? globalThis.fetch;\n\n let discoveryCache: Discovery | null = null;\n let jwksCache: ReturnType<typeof createLocalJWKSet> | null = null;\n\n async function discover(): Promise<Discovery> {\n if (discoveryCache) {\n return discoveryCache;\n }\n const url = `${config.issuer.replace(/\\/$/, '')}/.well-known/openid-configuration`;\n const res = await doFetch(url);\n if (!res.ok) {\n throw new Error(`oidc \"${config.id}\": discovery failed (${res.status})`);\n }\n const json = (await res.json()) as Discovery;\n discoveryCache = json;\n return json;\n }\n\n async function getJwks(force = false): Promise<ReturnType<typeof createLocalJWKSet>> {\n if (jwksCache && !force) {\n return jwksCache;\n }\n const { jwks_uri } = await discover();\n const res = await doFetch(jwks_uri);\n if (!res.ok) {\n throw new Error(`oidc \"${config.id}\": JWKS fetch failed (${res.status})`);\n }\n const jwks = (await res.json()) as JSONWebKeySet;\n jwksCache = createLocalJWKSet(jwks);\n return jwksCache;\n }\n\n async function verifyIdToken(idToken: string): Promise<JWTPayload> {\n const opts = { algorithms: VERIFY_ALGS, issuer: config.issuer, audience: config.clientId };\n try {\n const { payload } = await jwtVerify(idToken, await getJwks(), opts);\n return payload;\n } catch (err) {\n // A key-rotation miss is the one recoverable case: refetch JWKS once and\n // retry. Any other failure (bad sig, alg, iss, aud, exp) propagates.\n const code = (err as { code?: string }).code;\n if (code === 'ERR_JWKS_NO_MATCHING_KEY') {\n const { payload } = await jwtVerify(idToken, await getJwks(true), opts);\n return payload;\n }\n throw err;\n }\n }\n\n const provider: IRedirectIdentityProvider = {\n id: config.id,\n kind: 'redirect',\n\n async startAuthorization(ctx: RedirectStartContext): Promise<RedirectStartResult> {\n const { authorization_endpoint } = await discover();\n const nonce = base64url(randomBytes(16));\n const url = new URL(authorization_endpoint);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', config.clientId);\n url.searchParams.set('redirect_uri', ctx.redirectUri);\n url.searchParams.set('scope', scopeParam);\n url.searchParams.set('state', ctx.state);\n url.searchParams.set('nonce', nonce);\n\n const session: Record<string, unknown> = { nonce };\n\n if (config.pkce) {\n const codeVerifier = base64url(randomBytes(32));\n const challenge = base64url(createHash('sha256').update(codeVerifier).digest());\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n session.codeVerifier = codeVerifier;\n }\n\n return { redirectUrl: url.toString(), session };\n },\n\n async authenticate(input: unknown): Promise<IdentityResult> {\n const bundle = asBundle(input);\n\n // The IdP redirected back with an explicit error (consent denied, etc.).\n // Never attempt a token exchange.\n if (bundle.error) {\n return { ok: false, reason: 'invalid' };\n }\n if (!bundle.code || !bundle.redirectUri) {\n return { ok: false, reason: 'invalid' };\n }\n\n const { token_endpoint } = await discover();\n\n // Exchange the authorization code for tokens.\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code: bundle.code,\n redirect_uri: bundle.redirectUri,\n client_id: config.clientId,\n client_secret: secret,\n });\n if (config.pkce && bundle.session?.codeVerifier) {\n body.set('code_verifier', bundle.session.codeVerifier);\n }\n\n const tokenRes = await doFetch(token_endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },\n body: body.toString(),\n });\n if (!tokenRes.ok) {\n // 4xx/5xx from the token endpoint is a genuine infra/protocol error —\n // surface it so the route returns a 500-class redirect, not a silent\n // \"invalid credentials\". Status only; never log the response body.\n throw new Error(`oidc \"${config.id}\": token endpoint error (${tokenRes.status})`);\n }\n\n const tokenJson = (await tokenRes.json()) as { id_token?: string };\n const idToken = tokenJson.id_token;\n if (!idToken) {\n return { ok: false, reason: 'invalid' };\n }\n\n let payload: JWTPayload;\n try {\n payload = await verifyIdToken(idToken);\n } catch {\n // Signature / alg / iss / aud / exp failures all collapse to a coarse\n // not-ok (CD-8). No token material is logged.\n return { ok: false, reason: 'invalid' };\n }\n\n // Nonce binding: the token's nonce must echo what we minted at /start.\n const expectedNonce = bundle.session?.nonce;\n if (!expectedNonce || payload.nonce !== expectedNonce) {\n return { ok: false, reason: 'invalid' };\n }\n\n const email = typeof payload.email === 'string' ? payload.email : undefined;\n if (!email) {\n return { ok: false, reason: 'invalid' };\n }\n\n const emailVerified = payload.email_verified === true;\n if (!emailVerified && !config.allowUnverifiedEmail) {\n return { ok: false, reason: 'invalid' };\n }\n\n return {\n ok: true,\n email: canonicalizeEmail(email),\n externalId: typeof payload.sub === 'string' ? payload.sub : undefined,\n };\n },\n };\n\n return provider;\n}\n","/**\n * @module @kb-labs/gateway-auth/provider-registry\n *\n * Tiny registry for `IIdentityProvider` instances (ADR-0020, Phase 1.11).\n *\n * Two invariants enforced beyond what a raw Map would give us:\n * - Re-registering the same `id` throws — usually a bootstrap bug,\n * never intentional.\n * - `list()` returns only the public `{ id, kind }` shape so the\n * `GET /auth/providers` route cannot accidentally serialise an\n * `authenticate` function over the wire.\n */\n\nimport type { IIdentityProvider } from '@kb-labs/core-contracts';\n\nexport interface ProviderInfo {\n id: string;\n kind: IIdentityProvider['kind'];\n}\n\nexport class ProviderRegistry {\n private readonly providers = new Map<string, IIdentityProvider>();\n\n register(provider: IIdentityProvider): void {\n if (this.providers.has(provider.id)) {\n throw new Error(`provider-registry: id \"${provider.id}\" is already registered`);\n }\n this.providers.set(provider.id, provider);\n }\n\n get(id: string): IIdentityProvider | undefined {\n return this.providers.get(id);\n }\n\n has(id: string): boolean {\n return this.providers.has(id);\n }\n\n list(): ProviderInfo[] {\n return [...this.providers.values()].map(p => ({ id: p.id, kind: p.kind }));\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/provider-loader\n *\n * Config-driven loader for identity providers (ADR-0020, DD-3).\n *\n * This is the gateway-side analogue of the platform adapter loader\n * (`core/runtime/src/loader.ts` notifier sub-channels): a type→factory\n * map for the built-ins, plus dynamic `import()` of a package for any\n * non-builtin `type`. Two deliberate departures from the platform\n * loader:\n *\n * - **Fail-fast, not warn-skip.** A misconfigured *platform* adapter can\n * degrade gracefully; a misconfigured *auth door* must never boot\n * half-open. If a configured provider cannot be loaded, we throw.\n * - **Gateway-owned (Pattern B).** Providers need gateway-domain ports\n * (users/credentials) and the tenant context, which don't exist until\n * gateway bootstrap — well after platform assembly. So the loader lives\n * here, not in core.\n *\n * The built-in `email-password` provider is the zero-config default: an\n * empty/absent `providers` config registers it and nothing else. It has\n * no special status beyond being the one we ship — it goes through the\n * exact same factory path a third-party provider would.\n */\n\nimport type {\n IdentityProviderDeps,\n IdentityProviderFactory,\n} from '@kb-labs/core-contracts';\nimport { ProviderRegistry } from './provider-registry.js';\nimport { createEmailPasswordProvider } from './providers/email-password.js';\nimport { createOidcProvider } from './providers/oidc.js';\n\n/**\n * A single provider entry from `auth.providers` config. `type` is the\n * discriminator (built-in name or package name); everything else is\n * provider-specific and validated by the provider's own zod schema.\n */\nexport interface ProviderConfigEntry {\n type: string;\n [key: string]: unknown;\n}\n\nexport type ProvidersConfig = Record<string, ProviderConfigEntry> | undefined;\n\n/**\n * Thin adapter from the `(config, deps)` factory shape to the existing\n * `createEmailPasswordProvider(opts)`. The provider only reads the narrow\n * ports, so deps map straight through (DD-3).\n */\nconst emailPasswordFactory: IdentityProviderFactory = (_config, deps) =>\n createEmailPasswordProvider({\n users: deps.users,\n credentials: deps.credentials,\n tenantId: deps.tenantId,\n bcryptCost: deps.bcryptCost,\n });\n\n/**\n * Built-in factories keyed by `type`. Anything not here is resolved as a\n * package name. `oidc` is the generic OIDC redirect provider (Google / Okta /\n * Auth0 / Keycloak / Azure AD); it owns its own zod config schema.\n */\nexport const BUILTIN_FACTORIES: Record<string, IdentityProviderFactory> = {\n 'email-password': emailPasswordFactory,\n oidc: createOidcProvider,\n};\n\ninterface ExternalProviderModule {\n createIdentityProvider?: IdentityProviderFactory;\n default?: IdentityProviderFactory;\n}\n\nasync function resolveFactory(\n id: string,\n type: string,\n): Promise<IdentityProviderFactory> {\n const builtin = BUILTIN_FACTORIES[type];\n if (builtin) {\n return builtin;\n }\n\n // Non-builtin type → treat as a package name and import it (DD-3).\n // Any failure is fatal: a configured auth door that cannot load must\n // stop the boot rather than silently disappear.\n let mod: ExternalProviderModule;\n try {\n mod = (await import(type)) as ExternalProviderModule;\n } catch (err) {\n throw new Error(\n `identity provider \"${id}\": failed to load package \"${type}\": ${(err as Error).message}`,\n );\n }\n\n const factory = mod.createIdentityProvider ?? mod.default;\n if (typeof factory !== 'function') {\n throw new Error(\n `identity provider \"${id}\": package \"${type}\" exports neither ` +\n 'createIdentityProvider nor a default factory',\n );\n }\n return factory;\n}\n\n/**\n * Build a {@link ProviderRegistry} from the `auth.providers` config.\n *\n * @param config The `auth.providers` map (or undefined).\n * @param deps Shared dependencies handed to every provider factory.\n * @returns A populated registry. Throws (fail-fast) on any load error.\n */\nexport async function loadIdentityProviders(\n config: ProvidersConfig,\n deps: IdentityProviderDeps,\n): Promise<ProviderRegistry> {\n const registry = new ProviderRegistry();\n const entries = Object.entries(config ?? {});\n\n if (entries.length === 0) {\n // Zero-config default: the built-in email-password door (DD-3).\n const provider = await emailPasswordFactory(\n { type: 'email-password', id: 'email-password' },\n deps,\n );\n registry.register(provider);\n return registry;\n }\n\n for (const [id, entry] of entries) {\n const factory = await resolveFactory(id, entry.type);\n // Inject the instance id so providers (e.g. oidc) can identify\n // themselves and build per-instance callback URLs.\n const provider = await factory({ ...entry, id }, deps);\n registry.register(provider);\n }\n\n return registry;\n}\n","/**\n * @module @kb-labs/gateway-auth/oauth-state-store\n *\n * Per-attempt OAuth `state` storage (ADR-0020, DD-5).\n *\n * Between `GET /auth/oauth/:id/start` and the IdP callback we must hold a\n * short-lived binding from the opaque `state` token to the flow context\n * (which provider issued it, which tenant, where to return, and the\n * per-attempt secrets like nonce / PKCE verifier). This is a thin,\n * namespaced wrapper over `IKVStore` — a real abstraction, not a stub:\n *\n * - **Namespaced keys** (`oauth:state:{state}`) so it can share the same\n * KV as the rate limiter without collision.\n * - **TTL** so abandoned flows self-clean.\n * - **One-shot `consume`** (get → delete, gated on the delete result) so\n * a replayed callback cannot be processed twice. With a shared Redis KV\n * the gating on `delete()` is what provides the atomic single-winner\n * guarantee under concurrency.\n *\n * NOTE: in a multi-process / HA deployment this MUST be backed by a\n * shared KV (Redis); an in-memory KV is per-process and a callback may\n * land on a different worker than the one that issued the state. The\n * bootstrap warns when a redirect provider is configured against an\n * in-memory KV (see OAuth hardening, Step 4b).\n */\n\nimport type { IKVStore } from '@kb-labs/core-platform/adapters';\n\n/**\n * The flow context bound to a single `state` token. `session` carries the\n * provider's per-attempt secrets (OIDC nonce, PKCE verifier) verbatim and\n * is never logged.\n */\nexport interface OAuthStateRecord {\n /** The provider instance id that started this flow (mix-up guard). */\n providerId: string;\n /** The tenant the flow was started for (cross-tenant guard). */\n tenantId: string;\n /** Validated relative path to return to after success. */\n returnTo: string;\n /** Opaque per-attempt secrets the provider needs back at callback. */\n session?: Record<string, unknown>;\n /** Issue timestamp (ms). */\n createdAt: number;\n}\n\nexport interface OAuthStateStoreOptions {\n /** State TTL in milliseconds. Default 10 minutes. */\n ttlMs?: number;\n}\n\nconst DEFAULT_TTL_MS = 10 * 60 * 1000;\nconst KEY_PREFIX = 'oauth:state:';\n\nexport class OAuthStateStore {\n private readonly ttlMs: number;\n\n constructor(\n private readonly kv: IKVStore,\n opts: OAuthStateStoreOptions = {},\n ) {\n this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;\n }\n\n private key(state: string): string {\n return `${KEY_PREFIX}${state}`;\n }\n\n /** Persist a state record with the configured TTL. */\n async put(state: string, record: OAuthStateRecord): Promise<void> {\n await this.kv.set(this.key(state), record, { ttlMs: this.ttlMs });\n }\n\n /**\n * One-shot read. Returns the record to the first caller and deletes it;\n * concurrent / subsequent callers get `null`. The `delete` result gates\n * the return so exactly one caller wins even under a shared KV.\n */\n async consume(state: string): Promise<OAuthStateRecord | null> {\n const key = this.key(state);\n const record = await this.kv.get<OAuthStateRecord>(key);\n if (!record) {\n return null;\n }\n const removed = await this.kv.delete(key);\n if (!removed) {\n // Lost the race — another caller already consumed this state.\n return null;\n }\n return record;\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/stub-pdp\n *\n * Stub `IPolicyDecisionPoint` (ADR-0020, Phase 1.12). Mock RBAC over\n * the memberships store: tenant-admin gets every canonical permission,\n * tenant-member gets none. Machine identities are temporarily allowed\n * everything (matches the existing machine-token behaviour pre-RBAC).\n *\n * This module exists so the real handlers can call `policy.check(...)`\n * and `enumeratePermissions(...)` from day one. The real RBAC + ReBAC\n * engine (ClickUp 869def338) replaces this implementation; callers do\n * not change.\n *\n * Closed-world: actions outside the canonical PERMISSIONS enum return\n * `allow: false` even for tenant-admin. This is deliberate — until we\n * have per-plugin permission registration, the central enum is the\n * full universe of known actions.\n */\n\nimport type {\n Identity,\n IPolicyDecisionPoint,\n PolicyDecision,\n Resource,\n PolicyContext,\n} from '@kb-labs/core-contracts';\nimport { PERMISSIONS } from '@kb-labs/core-contracts';\nimport type { MembershipsStore, GroupId } from './memberships-store.js';\n\nconst ALL_PERMISSIONS: ReadonlyArray<string> = Object.freeze(Object.values(PERMISSIONS));\nconst MACHINE_PERMISSIONS: ReadonlyArray<string> = Object.freeze([PERMISSIONS.MACHINE_REGISTER]);\n\nconst GROUP_PERMISSIONS: Readonly<Record<GroupId, ReadonlyArray<string>>> = Object.freeze({\n 'tenant-admin': ALL_PERMISSIONS,\n 'tenant-member': Object.freeze([]),\n});\n\nexport interface StubPDPOptions {\n memberships: MembershipsStore;\n}\n\nexport const createStubPDP = (opts: StubPDPOptions): IPolicyDecisionPoint => {\n const permissionsForUser = async (identity: Identity): Promise<ReadonlyArray<string>> => {\n const list = await opts.memberships.listByUser(identity.userId);\n const inTenant = list.find(m => m.tenantId === identity.tenantId);\n if (!inTenant) {return [];}\n return GROUP_PERMISSIONS[inTenant.groupId];\n };\n\n return {\n async check(\n identity: Identity,\n action: string,\n _resource?: Resource,\n _ctx?: PolicyContext,\n ): Promise<PolicyDecision> {\n if (identity.type === 'machine') {\n // Machine identities currently retain their pre-PDP permissions.\n // Will tighten when the real PDP lands.\n if ((MACHINE_PERMISSIONS as string[]).includes(action)) {\n return { allow: true };\n }\n // Machines still allowed for now — keeps gateway proxy flows intact.\n return { allow: true };\n }\n\n const perms = await permissionsForUser(identity);\n if (perms.length === 0) {\n return { allow: false, reason: 'no_membership' };\n }\n if (!perms.includes(action)) {\n return { allow: false, reason: 'permission_denied' };\n }\n return { allow: true };\n },\n\n async enumeratePermissions(identity: Identity): Promise<string[]> {\n if (identity.type === 'machine') {\n return [...MACHINE_PERMISSIONS];\n }\n const perms = await permissionsForUser(identity);\n return [...perms];\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/bootstrap-admin\n *\n * First-run admin provisioning (ADR-0020, Phase 1.14).\n *\n * On a fresh deployment the operator can't log in unless someone has\n * already been provisioned — and we deliberately removed the public\n * `/auth/register` for humans. `ensureBootstrapAdmin` reads bootstrap\n * config from env and creates a single tenant-admin User on first\n * start. Idempotent: subsequent runs see the existing User and do\n * nothing.\n *\n * Conflict handling is conservative: if a User with the configured\n * email already exists in the configured tenant but in a different\n * status (e.g. someone manually `disabled` them and forgot the env\n * was still set), we log a warning and **do not touch** the existing\n * record. Silently re-activating or re-passwording is the kind of\n * surprise we want to avoid on a production system.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport bcrypt from 'bcryptjs';\nimport type { UsersStore } from './users-store.js';\nimport type { CredentialsStore } from './credentials-store.js';\nimport type { MembershipsStore } from './memberships-store.js';\n\nexport interface BootstrapConfig {\n adminEmail: string;\n adminPassword: string;\n tenantId: string;\n}\n\nexport interface BootstrapAdminLogger {\n warn(...args: unknown[]): void;\n info(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport interface EnsureBootstrapAdminOptions {\n bootstrap: BootstrapConfig | undefined;\n users: UsersStore;\n credentials: CredentialsStore;\n memberships: MembershipsStore;\n bcryptCost: number;\n logger: BootstrapAdminLogger;\n}\n\nconst isNonEmptyString = (v: unknown): v is string => typeof v === 'string' && v.length > 0;\n\nexport const ensureBootstrapAdmin = async (\n opts: EnsureBootstrapAdminOptions,\n): Promise<void> => {\n const { bootstrap, users, credentials, memberships, bcryptCost, logger } = opts;\n\n if (!bootstrap) {\n logger.info('bootstrap-admin: no bootstrap config provided, skipping');\n return;\n }\n\n if (\n !isNonEmptyString(bootstrap.adminEmail) ||\n !isNonEmptyString(bootstrap.adminPassword) ||\n !isNonEmptyString(bootstrap.tenantId)\n ) {\n throw new Error(\n 'bootstrap-admin: incomplete bootstrap config (need adminEmail, adminPassword, tenantId)',\n );\n }\n\n const existing = await users.findByEmailTenant(bootstrap.adminEmail, bootstrap.tenantId);\n\n if (existing) {\n if (existing.status === 'active') {\n logger.info('bootstrap-admin: admin already exists and is active, skipping');\n return;\n }\n logger.warn(\n 'bootstrap-admin: a user with the bootstrap email already exists in a non-active state; not touching it',\n { userId: existing.userId, status: existing.status, tenantId: existing.tenantId },\n );\n return;\n }\n\n const userId = randomUUID();\n await users.create({\n userId,\n tenantId: bootstrap.tenantId,\n email: bootstrap.adminEmail,\n status: 'active',\n });\n\n const hash = await bcrypt.hash(bootstrap.adminPassword, bcryptCost);\n await credentials.setCredential({\n userId,\n providerId: 'email-password',\n hash,\n });\n\n await memberships.addMembership({\n userId,\n tenantId: bootstrap.tenantId,\n groupId: 'tenant-admin',\n });\n\n logger.info('bootstrap-admin: provisioned tenant-admin', {\n userId,\n tenantId: bootstrap.tenantId,\n });\n};\n","/**\n * @module @kb-labs/gateway-auth/bootstrap-cli-credentials\n *\n * First-run CLI credential provisioning (#271).\n *\n * Non-local (`--yes`/server-mode) installs run with auth enabled but seed no\n * machine credential — `kb auth login` needs a client-id/secret nobody has,\n * and `/auth/register` is gated behind MACHINE_REGISTER which requires an\n * already-authenticated admin. `ensureBootstrapCliCredentials` closes that\n * gap by minting a fixed-handle machine service account in-process (no HTTP\n * round-trip, so the MACHINE_REGISTER gate never applies) and writing the\n * resulting JWT pair to disk in the same shape the CLI's own\n * `CredentialsManager` (cli/runtime/src/gateway/credentials.ts) expects.\n *\n * Idempotent: skipped entirely once the credentials file exists. If the file\n * is missing but the reserved handle is already registered (e.g. the file was\n * deleted after a previous successful run), this logs a warning and does\n * nothing further — same conservative \"never touch existing state, never\n * guess\" stance as ensureBootstrapAdmin.\n *\n * Deliberately does not import cli/runtime (a CLI-layer package) — a server\n * process depending on it would invert the dependency graph. The on-disk\n * shape is duplicated here instead (a few lines).\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport type { AuthService } from './service.js';\n\n/** Reserved handle for the auto-provisioned CLI bootstrap client. */\nexport const CLI_BOOTSTRAP_HANDLE = 'cli-bootstrap';\n\nexport interface BootstrapCliCredentialsLogger {\n warn(...args: unknown[]): void;\n info(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport interface EnsureBootstrapCliCredentialsOptions {\n /** Gate — set from config.auth.bootstrap.provisionCliCredentials. */\n enabled: boolean;\n authService: AuthService;\n /** Written into the credentials file so the CLI knows where to talk to. */\n gatewayUrl: string;\n /** Defaults to ~/.kb/credentials.json. Overridable for tests. */\n credentialsPath?: string;\n logger: BootstrapCliCredentialsLogger;\n}\n\nconst defaultCredentialsPath = (): string =>\n path.join(os.homedir(), '.kb', 'credentials.json');\n\nconst fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const ensureBootstrapCliCredentials = async (\n opts: EnsureBootstrapCliCredentialsOptions,\n): Promise<void> => {\n const { enabled, authService, gatewayUrl, logger } = opts;\n const credentialsPath = opts.credentialsPath ?? defaultCredentialsPath();\n\n if (!enabled) {\n logger.info('bootstrap-cli-credentials: not enabled, skipping');\n return;\n }\n\n if (await fileExists(credentialsPath)) {\n logger.info('bootstrap-cli-credentials: already provisioned, skipping', { credentialsPath });\n return;\n }\n\n let registered: Awaited<ReturnType<AuthService['register']>>;\n try {\n registered = await authService.register({\n name: 'CLI Bootstrap',\n handle: CLI_BOOTSTRAP_HANDLE,\n capabilities: [],\n });\n } catch (err) {\n if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'HANDLE_TAKEN') {\n logger.warn(\n 'bootstrap-cli-credentials: reserved handle already registered but credentials file is missing; not auto-recovering (client secret is not retrievable) — run `kb auth create-service-account` manually',\n { handle: CLI_BOOTSTRAP_HANDLE },\n );\n return;\n }\n throw err;\n }\n\n const tokens = await authService.issueTokens(registered.clientId, registered.clientSecret);\n if (!tokens) {\n logger.warn('bootstrap-cli-credentials: token issuance failed right after registration');\n return;\n }\n\n const credentials = {\n gatewayUrl,\n accessToken: tokens.accessToken,\n refreshToken: tokens.refreshToken,\n expiresAt: Date.now() + tokens.expiresIn * 1000,\n };\n\n await fs.mkdir(path.dirname(credentialsPath), { recursive: true });\n await fs.writeFile(credentialsPath, JSON.stringify(credentials, null, 2), 'utf-8');\n await fs.chmod(credentialsPath, 0o600);\n\n logger.info('bootstrap-cli-credentials: provisioned CLI bootstrap credential', {\n credentialsPath,\n hostId: registered.hostId,\n });\n};\n","/**\n * @module @kb-labs/gateway-auth/user-auth-service\n *\n * Orchestration layer for the user authentication flow (ADR-0020,\n * Phase 1.15).\n *\n * Pulls together every store + provider + jwt + policy and exposes the\n * five operations the HTTP layer calls:\n *\n * - `login`\n * - `refresh` (CD-1 enforced here — disabled user revokes family)\n * - `logout`\n * - `changePassword` (revokes other families, keeps current)\n * - `activate` (consumes invite, auto-logs in)\n *\n * All not-ok paths throw `AuthError(code)`. The HTTP layer collapses\n * most codes to opaque `invalid_credentials` for the caller; `code` is\n * preserved for logs and tests.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport bcrypt from 'bcryptjs';\nimport type { UsersStore, User } from './users-store.js';\nimport { canonicalizeEmail } from './users-store.js';\nimport type { CredentialsStore } from './credentials-store.js';\nimport type { MembershipsStore } from './memberships-store.js';\nimport type { InvitesStore } from './invites-store.js';\nimport type {\n SessionsStore} from './sessions-store.js';\nimport {\n RefreshExpiredError,\n RefreshNotFoundError,\n RefreshReuseDetectedError,\n} from './sessions-store.js';\nimport type { ProviderRegistry } from './provider-registry.js';\nimport type { PasswordPolicy, ValidationResult } from './password-policy.js';\nimport {\n type JwtConfig,\n signUserAccessToken,\n signUserRefreshToken,\n verifyUserRefreshToken,\n} from './jwt.js';\nimport { issueCsrfToken } from './csrf.js';\n\nexport type AuthErrorCode =\n | 'invalid_credentials'\n | 'unknown_provider'\n | 'invalid_refresh'\n | 'refresh_reuse'\n | 'user_disabled'\n | 'invalid_current_password'\n | 'weak_password'\n | 'invalid_invite'\n | 'unknown_invite';\n\nexport class AuthError extends Error {\n public override readonly name = 'AuthError';\n constructor(public readonly code: AuthErrorCode, public readonly reason?: string) {\n super(code);\n }\n}\n\nexport interface UserAuthServiceOptions {\n users: UsersStore;\n credentials: CredentialsStore;\n memberships: MembershipsStore;\n invites: InvitesStore;\n sessions: SessionsStore;\n providers: ProviderRegistry;\n passwordPolicy: PasswordPolicy;\n jwtConfig: JwtConfig;\n accessTtlSec: number;\n refreshTtlSec: number;\n bcryptCost: number;\n /** Override for tests. Defaults to Date.now. */\n now?: () => number;\n}\n\nexport interface PublicUser {\n userId: string;\n email: string;\n tenantId: string;\n}\n\nexport interface TokenInfo {\n token: string;\n expiresInSec: number;\n}\n\nexport interface SessionResult {\n user: PublicUser;\n access: TokenInfo;\n refresh: TokenInfo;\n csrf: string;\n familyId: string;\n}\n\nexport interface LoginInput {\n providerId: string;\n input: unknown;\n}\n\nexport interface DeviceCtx {\n userAgent?: string;\n ip?: string;\n}\n\nexport interface ChangePasswordInput {\n userId: string;\n currentFamilyId: string;\n currentPassword: string;\n newPassword: string;\n}\n\nexport interface ActivateInput {\n activationToken: string;\n password: string;\n deviceCtx: DeviceCtx;\n}\n\nconst toPublicUser = (u: User): PublicUser => ({\n userId: u.userId,\n email: u.email,\n tenantId: u.tenantId,\n});\n\nconst mapPolicyResultToError = (r: ValidationResult): AuthError | null => {\n if (r.ok) {return null;}\n return new AuthError('weak_password', r.reason);\n};\n\nexport const createUserAuthService = (opts: UserAuthServiceOptions) => {\n const now = opts.now ?? Date.now;\n\n /**\n * Build the (access, refresh, csrf) triple. The caller passes the\n * `refreshJti` that sessions-store already minted so JWT and store\n * agree on the same identifier. Access has its own independent jti.\n */\n const issueSessionTokens = async (\n userId: string,\n tenantId: string,\n familyId: string,\n refreshJti: string,\n ): Promise<{ access: TokenInfo; refresh: TokenInfo; csrf: string }> => {\n const access = await signUserAccessToken(\n { userId, tenantId, familyId, ttlSec: opts.accessTtlSec },\n opts.jwtConfig,\n );\n const refresh = await signUserRefreshToken(\n { userId, tenantId, familyId, ttlSec: opts.refreshTtlSec, jti: refreshJti },\n opts.jwtConfig,\n );\n return {\n access: { token: access.token, expiresInSec: opts.accessTtlSec },\n refresh: { token: refresh, expiresInSec: opts.refreshTtlSec },\n csrf: issueCsrfToken(),\n };\n };\n\n // ── login ─────────────────────────────────────────────────────────\n const login = async (\n input: LoginInput,\n tenantId: string,\n deviceCtx: DeviceCtx,\n ): Promise<SessionResult> => {\n const provider = opts.providers.get(input.providerId);\n if (!provider) {\n throw new AuthError('unknown_provider');\n }\n\n const result = await provider.authenticate(input.input);\n if (!result.ok) {\n throw new AuthError('invalid_credentials');\n }\n\n // Provider says ok — pin to a User in OUR tenant. Provider has\n // already checked status=active for the local provider; remote\n // IdPs in the future will not, so we re-check here.\n const user = await opts.users.findByEmailTenant(canonicalizeEmail(result.email), tenantId);\n if (!user || user.status !== 'active') {\n throw new AuthError('invalid_credentials');\n }\n\n const sess = await opts.sessions.createSession({\n userId: user.userId,\n tenantId,\n deviceCtx,\n });\n\n const tokens = await issueSessionTokens(user.userId, tenantId, sess.familyId, sess.refreshJti);\n return { user: toPublicUser(user), ...tokens, familyId: sess.familyId };\n };\n\n // ── refresh ───────────────────────────────────────────────────────\n const refresh = async (refreshToken: string): Promise<SessionResult> => {\n const payload = await verifyUserRefreshToken(refreshToken, opts.jwtConfig);\n if (!payload) {\n throw new AuthError('invalid_refresh');\n }\n\n // CD-1: middleware checks user.status on every request, but the\n // refresh endpoint is public (no access cookie to inspect). Check\n // again here so a disabled user cannot extend their session via a\n // still-live refresh cookie.\n const user = await opts.users.getById(payload.userId);\n if (!user || user.status !== 'active') {\n // Revoke whatever family they presented so the dead refresh is\n // also gone from the store.\n await opts.sessions.revokeFamily(payload.familyId);\n throw new AuthError('user_disabled');\n }\n\n let rotated;\n try {\n rotated = await opts.sessions.rotateRefresh(payload.jti);\n } catch (err) {\n if (err instanceof RefreshReuseDetectedError) {throw new AuthError('refresh_reuse');}\n if (err instanceof RefreshExpiredError) {throw new AuthError('invalid_refresh');}\n if (err instanceof RefreshNotFoundError) {throw new AuthError('invalid_refresh');}\n throw err;\n }\n\n const tokens = await issueSessionTokens(user.userId, user.tenantId, rotated.familyId, rotated.newJti);\n return { user: toPublicUser(user), ...tokens, familyId: rotated.familyId };\n };\n\n // ── logout ────────────────────────────────────────────────────────\n const logout = async (refreshToken: string): Promise<void> => {\n const payload = await verifyUserRefreshToken(refreshToken, opts.jwtConfig);\n if (!payload) {return;} // idempotent for garbage tokens\n await opts.sessions.revokeFamily(payload.familyId);\n };\n\n // ── changePassword ────────────────────────────────────────────────\n const changePassword = async (input: ChangePasswordInput): Promise<void> => {\n const user = await opts.users.getById(input.userId);\n if (!user || user.status !== 'active') {\n // Treat as a credentials problem from the caller's POV — they\n // shouldn't be reaching this endpoint as a disabled user anyway.\n throw new AuthError('invalid_current_password');\n }\n\n const cred = await opts.credentials.getCredential(user.userId, 'email-password');\n if (!cred) {\n throw new AuthError('invalid_current_password');\n }\n const ok = await bcrypt.compare(input.currentPassword, cred.hash);\n if (!ok) {throw new AuthError('invalid_current_password');}\n\n const policyResult = await opts.passwordPolicy.validate(input.newPassword);\n const polErr = mapPolicyResultToError(policyResult);\n if (polErr) {throw polErr;}\n\n const hash = await bcrypt.hash(input.newPassword, opts.bcryptCost);\n await opts.credentials.setCredential({\n userId: user.userId,\n providerId: 'email-password',\n hash,\n });\n\n // Kill every other device — the security expectation around a\n // password change is \"all my other sessions are gone now\".\n await opts.sessions.revokeAllUserSessionsExcept(user.userId, input.currentFamilyId);\n };\n\n // ── activate ──────────────────────────────────────────────────────\n const activate = async (input: ActivateInput): Promise<SessionResult> => {\n const tokenResult = await opts.invites.findByToken(input.activationToken);\n if (tokenResult.kind === 'not_found') {throw new AuthError('unknown_invite');}\n if (tokenResult.kind === 'invalid') {throw new AuthError('invalid_invite');}\n const invite = tokenResult.invite;\n\n const policyResult = await opts.passwordPolicy.validate(input.password);\n const polErr = mapPolicyResultToError(policyResult);\n if (polErr) {throw polErr;}\n\n // Consume the invite ATOMICALLY before creating any account records.\n // findByToken → create is a TOCTOU window: two parallel activations of\n // the same valid token both pass findByToken, and without an atomic gate\n // both would call users.create() — one succeeds, the other throws on the\n // unique (tenantId, email) index, leaving an orphaned credential/session.\n // consume() flips status active→used in a single compare-and-set; only the\n // winner gets `true`, the loser gets `false` and bails out cleanly. Done\n // AFTER the password-policy check so a weak password never burns the invite.\n const consumed = await opts.invites.consume(invite.inviteId);\n if (!consumed) {\n // Lost the race (or invite was used/revoked between findByToken and now).\n throw new AuthError('invalid_invite');\n }\n\n // Create the user, credential, and membership. The invite is already\n // consumed, so this path runs exactly once per token.\n const userId = randomUUID();\n await opts.users.create({\n userId,\n tenantId: invite.tenantId,\n email: invite.email,\n status: 'active',\n });\n const hash = await bcrypt.hash(input.password, opts.bcryptCost);\n await opts.credentials.setCredential({\n userId,\n providerId: 'email-password',\n hash,\n });\n await opts.memberships.addMembership({\n userId,\n tenantId: invite.tenantId,\n groupId: invite.groupId,\n });\n // Note: the invite was already consumed atomically above, before account\n // creation — do not consume again here.\n\n const sess = await opts.sessions.createSession({\n userId,\n tenantId: invite.tenantId,\n deviceCtx: input.deviceCtx,\n });\n\n const tokens = await issueSessionTokens(userId, invite.tenantId, sess.familyId, sess.refreshJti);\n return {\n user: { userId, email: invite.email, tenantId: invite.tenantId },\n ...tokens,\n familyId: sess.familyId,\n };\n };\n\n void now; // Reserved for future audit-event timestamps; currently unused.\n\n return { login, refresh, logout, changePassword, activate };\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/jwt.ts","../src/service.ts","../src/users-store.ts","../src/credentials-store.ts","../src/memberships-store.ts","../src/invites-store.ts","../src/sessions-store.ts","../src/password-policy.ts","../src/csrf.ts","../src/rate-limit.ts","../src/tenant-resolver.ts","../src/providers/email-password.ts","../src/providers/oidc.ts","../src/provider-registry.ts","../src/provider-loader.ts","../src/oauth-state-store.ts","../src/stub-pdp.ts","../src/bootstrap-admin.ts","../src/bootstrap-cli-credentials.ts","../src/user-auth-service.ts"],"names":["COLLECTION","randomBytes","hashToken","createHash","jwtVerify","randomUUID","bcrypt","fs","refresh"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Auth storage — ICache operations for credentials and tokens.\n *\n * Key schema:\n * auth:client:{clientId} → ClientRecord (permanent)\n * auth:refresh:{tokenHash} → { hostId } (TTL 30d)\n * auth:publickey:{hostId} → string (base64url X25519 public key)\n * auth:handle:{handle} → clientId (permanent, handle index)\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { ICache } from '@kb-labs/core-platform';\n\nconst REFRESH_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days\n\nexport interface ClientRecord {\n clientId: string;\n /** bcrypt-style: we store sha256 hash of the secret for simplicity (no bcrypt dep) */\n secretHash: string;\n hostId: string;\n namespaceId: string;\n tier: 'free' | 'pro' | 'enterprise';\n name: string;\n capabilities: string[];\n /** Permissions embedded in issued JWTs. If absent, defaults to ['host:connect']. */\n permissions?: string[];\n publicKey?: string;\n createdAt: number;\n /** Unique human-readable handle for the marketplace (e.g. \"kirill\"). Immutable once set. */\n handle?: string;\n email?: string;\n}\n\nfunction hashSecret(secret: string): string {\n return createHash('sha256').update(secret).digest('hex');\n}\n\nfunction hashToken(token: string): string {\n return createHash('sha256').update(token).digest('hex');\n}\n\nexport function generateClientId(): string {\n return `clt_${randomBytes(16).toString('hex')}`;\n}\n\nexport function generateClientSecret(): string {\n return `cs_${randomBytes(32).toString('base64url')}`;\n}\n\nexport function generateHostId(): string {\n return `host_${randomBytes(12).toString('hex')}`;\n}\n\nexport function generateNamespaceId(): string {\n return `ns_${randomBytes(16).toString('hex')}`;\n}\n\n// ── Client CRUD ───────────────────────────────────────────────────────────────\n\nexport async function saveClient(cache: ICache, record: ClientRecord): Promise<void> {\n await cache.set(`auth:client:${record.clientId}`, record);\n await cache.set(`auth:hostindex:${record.hostId}`, record.clientId);\n if (record.handle) {\n await cache.set(`auth:handle:${record.handle}`, record.clientId);\n }\n}\n\nexport async function isHandleTaken(cache: ICache, handle: string): Promise<boolean> {\n return (await cache.get(`auth:handle:${handle}`)) !== null;\n}\n\nexport async function getClientByHandle(cache: ICache, handle: string): Promise<ClientRecord | null> {\n const clientId = await cache.get<string>(`auth:handle:${handle}`);\n if (!clientId) { return null; }\n return getClient(cache, clientId);\n}\n\nexport async function getClientByHostId(cache: ICache, hostId: string): Promise<ClientRecord | null> {\n const clientId = await cache.get<string>(`auth:hostindex:${hostId}`);\n if (!clientId) {return null;}\n return getClient(cache, clientId);\n}\n\nexport async function getClient(cache: ICache, clientId: string): Promise<ClientRecord | null> {\n return cache.get<ClientRecord>(`auth:client:${clientId}`);\n}\n\nexport async function verifyClientSecret(\n cache: ICache,\n clientId: string,\n secret: string,\n): Promise<ClientRecord | null> {\n const record = await getClient(cache, clientId);\n if (!record) {return null;}\n if (record.secretHash !== hashSecret(secret)) {return null;}\n return record;\n}\n\nexport function buildClientRecord(opts: {\n name: string;\n capabilities: string[];\n permissions?: string[];\n publicKey?: string;\n secret: string;\n namespaceId?: string;\n handle?: string;\n email?: string;\n}): ClientRecord {\n return {\n clientId: generateClientId(),\n secretHash: hashSecret(opts.secret),\n hostId: generateHostId(),\n namespaceId: opts.namespaceId ?? generateNamespaceId(),\n tier: 'free',\n name: opts.name,\n capabilities: opts.capabilities,\n permissions: opts.permissions,\n publicKey: opts.publicKey,\n createdAt: Date.now(),\n handle: opts.handle,\n email: opts.email,\n };\n}\n\n// ── Refresh tokens ────────────────────────────────────────────────────────────\n\nexport async function saveRefreshToken(\n cache: ICache,\n token: string,\n hostId: string,\n namespaceId: string,\n): Promise<void> {\n await cache.set(`auth:refresh:${hashToken(token)}`, { hostId, namespaceId }, REFRESH_TTL_MS);\n}\n\nexport async function consumeRefreshToken(\n cache: ICache,\n token: string,\n): Promise<{ hostId: string; namespaceId: string } | null> {\n const key = `auth:refresh:${hashToken(token)}`;\n const entry = await cache.get<{ hostId: string; namespaceId: string }>(key);\n if (!entry) {return null;}\n // Rotate: delete old token (new one will be issued)\n await cache.delete(key);\n return entry;\n}\n\n// ── Public keys (E2E encryption) ──────────────────────────────────────────────\n\nexport async function savePublicKey(\n cache: ICache,\n hostId: string,\n publicKey: string,\n): Promise<void> {\n await cache.set(`auth:publickey:${hostId}`, publicKey);\n}\n\nexport async function getPublicKey(\n cache: ICache,\n hostId: string,\n): Promise<string | null> {\n return cache.get<string>(`auth:publickey:${hostId}`);\n}\n","/**\n * JWT sign / verify / refresh logic.\n * Pure functions — no side effects, no ICache dependency.\n */\n\nimport { SignJWT, jwtVerify, type JWTPayload } from 'jose';\nimport { randomUUID } from 'node:crypto';\nimport type { JwtPayload, TokenType } from '@kb-labs/gateway-contracts';\n\nconst ACCESS_TOKEN_TTL = 15 * 60; // 15 minutes in seconds\nconst REFRESH_TOKEN_TTL = 30 * 24 * 3600; // 30 days in seconds\n\nexport interface JwtConfig {\n /** Raw secret string — converted to TextEncoder internally */\n secret: string;\n}\n\nfunction getSecretKey(secret: string): Uint8Array {\n return new TextEncoder().encode(secret);\n}\n\nexport interface SignAccessTokenOptions {\n hostId: string;\n namespaceId: string;\n tier: 'free' | 'pro' | 'enterprise';\n type: TokenType;\n /** Permissions to embed in the JWT payload. Defaults to [] when absent. */\n permissions?: string[];\n}\n\nexport async function signAccessToken(\n opts: SignAccessTokenOptions,\n config: JwtConfig,\n): Promise<{ token: string; expiresIn: number }> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n\n const token = await new SignJWT({\n namespaceId: opts.namespaceId,\n tier: opts.tier,\n type: opts.type,\n permissions: opts.permissions ?? [],\n })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.hostId)\n .setIssuedAt(now)\n .setExpirationTime(now + ACCESS_TOKEN_TTL)\n .sign(key);\n\n return { token, expiresIn: ACCESS_TOKEN_TTL };\n}\n\nexport async function signRefreshToken(\n hostId: string,\n config: JwtConfig,\n): Promise<string> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n const jti = randomUUID();\n\n return new SignJWT({ type: 'refresh', jti })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(hostId)\n .setIssuedAt(now)\n .setExpirationTime(now + REFRESH_TOKEN_TTL)\n .sign(key);\n}\n\nexport async function verifyAccessToken(\n token: string,\n config: JwtConfig,\n): Promise<JwtPayload | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n return payload as unknown as JwtPayload;\n } catch {\n return null;\n }\n}\n\nexport async function verifyRefreshToken(\n token: string,\n config: JwtConfig,\n): Promise<{ hostId: string } | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n if ((payload as JWTPayload & { type?: string }).type !== 'refresh') {\n return null;\n }\n if (!payload.sub) {return null;}\n return { hostId: payload.sub };\n } catch {\n return null;\n }\n}\n\n// ─── User-flavoured tokens (ADR-0020, Phase 1.13) ────────────────────────\n//\n// These live alongside the machine-token helpers above. The shape is\n// deliberately different: `type: 'user'` for access, `type: 'refresh'`\n// + tenantId + familyId for refresh. `fam` (familyId) is **mandatory**\n// in BOTH access and refresh — verifyUserAccessToken rejects tokens\n// without it even on otherwise valid signatures. Without `fam` in the\n// access cookie, /auth/password/change cannot identify the current\n// session family to spare on revoke-all-others (CD-2).\n//\n// Payload never contains role / scopes / permissions (principle #3).\n// Authorization is resolved per-request by the PDP.\n\nexport interface SignUserTokenOptions {\n userId: string;\n tenantId: string;\n familyId: string;\n /** TTL in seconds. */\n ttlSec: number;\n}\n\nexport interface UserAccessPayload {\n userId: string;\n tenantId: string;\n familyId: string;\n jti: string;\n /** Token type discriminator. Access tokens carry 'user'. */\n type: 'user';\n iat: number;\n exp: number;\n}\n\nexport interface UserRefreshPayload {\n userId: string;\n tenantId: string;\n familyId: string;\n jti: string;\n type: 'refresh';\n iat: number;\n exp: number;\n}\n\nexport interface UserAccessSignResult {\n token: string;\n expiresInSec: number;\n jti: string;\n}\n\nexport async function signUserAccessToken(\n opts: SignUserTokenOptions,\n config: JwtConfig,\n): Promise<UserAccessSignResult> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n const jti = randomUUID();\n const token = await new SignJWT({\n tenantId: opts.tenantId,\n fam: opts.familyId,\n type: 'user',\n jti,\n })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.userId)\n .setIssuedAt(now)\n .setExpirationTime(now + opts.ttlSec)\n .sign(key);\n return { token, expiresInSec: opts.ttlSec, jti };\n}\n\n/**\n * Sign a user refresh token.\n *\n * When the caller already owns the `jti` (typical: the sessions-store\n * minted it inside `createSession`/`rotateRefresh`), pass it via the\n * second argument so the JWT and the store agree on the identifier.\n * Otherwise the helper generates one — used by tests that don't go\n * through sessions-store.\n */\nexport async function signUserRefreshToken(\n opts: SignUserTokenOptions & { jti?: string },\n config: JwtConfig,\n): Promise<string> {\n const key = getSecretKey(config.secret);\n const now = Math.floor(Date.now() / 1000);\n const jti = opts.jti ?? randomUUID();\n return new SignJWT({\n tenantId: opts.tenantId,\n fam: opts.familyId,\n type: 'refresh',\n jti,\n })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.userId)\n .setIssuedAt(now)\n .setExpirationTime(now + opts.ttlSec)\n .sign(key);\n}\n\ninterface RawUserClaims extends JWTPayload {\n tenantId?: string;\n fam?: string;\n type?: string;\n jti?: string;\n}\n\nconst normaliseAccess = (raw: RawUserClaims): UserAccessPayload | null => {\n if (raw.type !== 'user') {return null;}\n if (!raw.sub || !raw.tenantId || !raw.fam || !raw.jti) {return null;}\n if (typeof raw.iat !== 'number' || typeof raw.exp !== 'number') {return null;}\n return {\n userId: raw.sub,\n tenantId: raw.tenantId,\n familyId: raw.fam,\n jti: raw.jti,\n type: 'user',\n iat: raw.iat,\n exp: raw.exp,\n };\n};\n\nconst normaliseRefresh = (raw: RawUserClaims): UserRefreshPayload | null => {\n if (raw.type !== 'refresh') {return null;}\n if (!raw.sub || !raw.tenantId || !raw.fam || !raw.jti) {return null;}\n if (typeof raw.iat !== 'number' || typeof raw.exp !== 'number') {return null;}\n return {\n userId: raw.sub,\n tenantId: raw.tenantId,\n familyId: raw.fam,\n jti: raw.jti,\n type: 'refresh',\n iat: raw.iat,\n exp: raw.exp,\n };\n};\n\nexport async function verifyUserAccessToken(\n token: string,\n config: JwtConfig,\n): Promise<UserAccessPayload | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n return normaliseAccess(payload as RawUserClaims);\n } catch {\n return null;\n }\n}\n\nexport async function verifyUserRefreshToken(\n token: string,\n config: JwtConfig,\n): Promise<UserRefreshPayload | null> {\n try {\n const key = getSecretKey(config.secret);\n const { payload } = await jwtVerify(token, key, { algorithms: ['HS256'] });\n return normaliseRefresh(payload as RawUserClaims);\n } catch {\n return null;\n }\n}\n","/**\n * AuthService — orchestrates registration, token issuance, and refresh.\n * Depends on ICache (via store) and JWT config.\n */\n\nimport type { ICache } from '@kb-labs/core-platform';\nimport type {\n RegisterRequest,\n RegisterResponse,\n TokenResponse,\n AuthContext,\n} from '@kb-labs/gateway-contracts';\nimport {\n buildClientRecord,\n generateClientSecret,\n saveClient,\n verifyClientSecret,\n saveRefreshToken,\n consumeRefreshToken,\n savePublicKey,\n isHandleTaken,\n getClientByHostId,\n} from './store.js';\nimport {\n signAccessToken,\n signRefreshToken,\n verifyAccessToken,\n verifyRefreshToken,\n type JwtConfig,\n} from './jwt.js';\n\nexport class AuthService {\n constructor(\n private readonly cache: ICache,\n private readonly jwtConfig: JwtConfig,\n ) {}\n\n // ── Register ──────────────────────────────────────────────────────────────\n\n async register(req: RegisterRequest): Promise<RegisterResponse & { clientSecret: string }> {\n if (req.handle && await isHandleTaken(this.cache, req.handle)) {\n throw Object.assign(new Error(`Handle '${req.handle}' is already taken`), { code: 'HANDLE_TAKEN' });\n }\n\n const secret = generateClientSecret();\n const record = buildClientRecord({\n name: req.name,\n capabilities: req.capabilities ?? [],\n publicKey: req.publicKey,\n secret,\n handle: req.handle,\n email: req.email,\n });\n\n await saveClient(this.cache, record);\n\n if (req.publicKey) {\n await savePublicKey(this.cache, record.hostId, req.publicKey);\n }\n\n return {\n clientId: record.clientId,\n clientSecret: secret,\n hostId: record.hostId,\n namespaceId: record.namespaceId,\n handle: record.handle,\n };\n }\n\n // ── Issue token pair ──────────────────────────────────────────────────────\n\n async issueTokens(clientId: string, clientSecret: string): Promise<TokenResponse | null> {\n const record = await verifyClientSecret(this.cache, clientId, clientSecret);\n if (!record) {return null;}\n\n const [{ token: accessToken, expiresIn }, refreshToken] = await Promise.all([\n signAccessToken(\n {\n hostId: record.hostId,\n namespaceId: record.namespaceId,\n tier: record.tier,\n type: 'machine',\n permissions: record.permissions ?? ['host:connect'],\n },\n this.jwtConfig,\n ),\n signRefreshToken(record.hostId, this.jwtConfig),\n ]);\n\n await saveRefreshToken(this.cache, refreshToken, record.hostId, record.namespaceId);\n\n return { accessToken, refreshToken, expiresIn, tokenType: 'Bearer' };\n }\n\n // ── Refresh token pair ────────────────────────────────────────────────────\n\n async refreshTokens(refreshToken: string): Promise<TokenResponse | null> {\n // Verify JWT signature first\n const jwtResult = await verifyRefreshToken(refreshToken, this.jwtConfig);\n if (!jwtResult) {return null;}\n\n // Consume from store (rotation — old token invalidated)\n const stored = await consumeRefreshToken(this.cache, refreshToken);\n if (!stored) {return null;}\n\n // Reload client record to get current permissions + tier.\n // getClientByHostId does a two-key lookup: hostIndex → clientId → record.\n const hostId = stored.hostId;\n const record = await getClientByHostId(this.cache, hostId);\n\n // Issue new pair\n const [{ token: accessToken, expiresIn }, newRefreshToken] = await Promise.all([\n signAccessToken(\n {\n hostId,\n namespaceId: stored.namespaceId,\n tier: record?.tier ?? 'free',\n type: 'machine',\n permissions: record?.permissions ?? ['host:connect'],\n },\n this.jwtConfig,\n ),\n signRefreshToken(hostId, this.jwtConfig),\n ]);\n\n await saveRefreshToken(this.cache, newRefreshToken, hostId, stored.namespaceId);\n\n return { accessToken, refreshToken: newRefreshToken, expiresIn, tokenType: 'Bearer' };\n }\n\n // ── Profile lookup ────────────────────────────────────────────────────────\n\n async me(hostId: string): Promise<{ hostId: string; handle?: string; namespaceId: string } | null> {\n const record = await getClientByHostId(this.cache, hostId);\n if (!record) { return null; }\n return { hostId: record.hostId, handle: record.handle, namespaceId: record.namespaceId };\n }\n\n // ── Verify access token → AuthContext ────────────────────────────────────\n\n async verify(token: string): Promise<AuthContext | null> {\n const payload = await verifyAccessToken(token, this.jwtConfig);\n if (!payload) {return null;}\n\n return {\n type: payload.type,\n userId: payload.sub,\n namespaceId: payload.namespaceId,\n tier: payload.tier,\n permissions: payload.permissions ?? ['host:connect'],\n };\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/users-store\n *\n * Document-backed store for `User` records (ADR-0020, Phase 1.1).\n *\n * Schema invariants:\n * - `(tenantId, email)` is a unique compound — one user per email per\n * tenant. The same email can exist in different tenants.\n * - `email` is **always** stored lowercased + trimmed (CD-4). Callers\n * may pass any casing; the store normalises before write/read.\n * - **No** password hash on this document. Credentials live in the\n * separate `credentials` collection (CD-6) so a future Google/Okta\n * provider can attach to the same `User` without a schema migration.\n *\n * Pattern follows `plugins/gateway/core/src/stores/host-store.ts`:\n * one-shot `ensureSchema()` plus per-method idempotent `ensureCollection`\n * registration.\n */\n\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\n\nconst COLLECTION = 'users';\n\nexport type UserStatus = 'pending' | 'active' | 'disabled';\n\nexport interface User {\n userId: string;\n tenantId: string;\n /** Lowercase + trimmed (CD-4). */\n email: string;\n displayName?: string;\n status: UserStatus;\n createdAt: number;\n updatedAt?: number;\n}\n\ninterface UserDoc extends BaseDocument {\n userId: string;\n tenantId: string;\n /** Always lowercase + trimmed; used by the unique index. */\n email: string;\n displayName?: string;\n status: UserStatus;\n}\n\n/**\n * Canonical email form used everywhere this store touches.\n *\n * Exported so other modules (the email-password provider, invites-store)\n * normalise identically — having one function prevents drift where one\n * caller forgets to trim and lookups silently miss.\n */\nexport const canonicalizeEmail = (raw: string): string => raw.trim().toLowerCase();\n\nconst docToUser = (doc: UserDoc): User => ({\n userId: doc.userId,\n tenantId: doc.tenantId,\n email: doc.email,\n displayName: doc.displayName,\n status: doc.status,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport interface CreateUserInput {\n userId: string;\n tenantId: string;\n email: string;\n status: UserStatus;\n displayName?: string;\n}\n\nexport class UsersStore {\n private initialised: Promise<void> | null = null;\n\n constructor(private readonly docs: IDocumentDatabase) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n // Logical primary key for \"user inside tenant\" lookups.\n { path: ['tenantId', 'email'], unique: true },\n // userId is the surrogate key callers reference everywhere.\n { path: 'userId', unique: true },\n { path: 'tenantId' },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async create(input: CreateUserInput): Promise<User> {\n await this.ensureSchema();\n const email = canonicalizeEmail(input.email);\n // The unique compound index turns a race-y duplicate into a driver-level\n // throw; we don't need an additional pre-check.\n await this.docs.insertOne<UserDoc>(COLLECTION, {\n userId: input.userId,\n tenantId: input.tenantId,\n email,\n displayName: input.displayName,\n status: input.status,\n });\n const created = await this.getById(input.userId);\n if (!created) {\n // Shouldn't happen — insertOne just succeeded. Defensive throw makes\n // a corrupted-index situation visible immediately rather than at the\n // next read.\n throw new Error(`users-store: inserted user ${input.userId} but read-back returned null`);\n }\n return created;\n }\n\n async getById(userId: string): Promise<User | null> {\n await this.ensureSchema();\n const doc = await this.docs.findById<UserDoc>(COLLECTION, userId);\n if (doc && doc.userId === userId) {\n return docToUser(doc);\n }\n // findById uses the driver's id field; we also keep `userId` explicitly\n // so the store works even when the driver-id differs from userId.\n const [byField] = await this.docs.find<UserDoc>(\n COLLECTION,\n { userId: { $eq: userId } },\n { limit: 1 },\n );\n return byField ? docToUser(byField) : null;\n }\n\n async findByEmailTenant(email: string, tenantId: string): Promise<User | null> {\n await this.ensureSchema();\n const normalised = canonicalizeEmail(email);\n const [doc] = await this.docs.find<UserDoc>(\n COLLECTION,\n { $and: [{ tenantId: { $eq: tenantId } }, { email: { $eq: normalised } }] },\n { limit: 1 },\n );\n return doc ? docToUser(doc) : null;\n }\n\n async setStatus(userId: string, status: UserStatus): Promise<void> {\n await this.ensureSchema();\n const existing = await this.getById(userId);\n if (!existing) {\n throw new Error(`users-store: setStatus on unknown userId=${userId}`);\n }\n await this.docs.updateMany<UserDoc>(\n COLLECTION,\n { userId: { $eq: userId } },\n { $set: { status } },\n );\n }\n\n /** List all users for a given tenant. */\n async listByTenant(tenantId: string): Promise<User[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<UserDoc>(\n COLLECTION,\n { tenantId: { $eq: tenantId } },\n );\n return docs.map(docToUser);\n }\n\n async delete(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<UserDoc>(COLLECTION, { userId: { $eq: userId } });\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/credentials-store\n *\n * Document-backed store for per-provider credentials (ADR-0020, CD-6,\n * Phase 1.2).\n *\n * `User` does not carry a password hash. Credentials live here, keyed by\n * the compound `(userId, providerId)`. Adding a new identity provider\n * (Google, Okta, LDAP, ...) means writing a row with a new `providerId`\n * — no schema migration on the `users` collection, no `passwordHash?:\n * null` quirks.\n *\n * Hash format and algorithm are the provider's concern: this store only\n * persists opaque strings. The `email-password` provider chooses bcrypt\n * with a configured cost.\n */\n\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\n\nconst COLLECTION = 'credentials';\n\nexport interface Credential {\n userId: string;\n providerId: string;\n /** Opaque to this store — bcrypt hash for email-password, OAuth refresh blob for future providers, etc. */\n hash: string;\n createdAt: number;\n updatedAt?: number;\n}\n\ninterface CredentialDoc extends BaseDocument {\n userId: string;\n providerId: string;\n hash: string;\n}\n\nconst docToCredential = (doc: CredentialDoc): Credential => ({\n userId: doc.userId,\n providerId: doc.providerId,\n hash: doc.hash,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport interface SetCredentialInput {\n userId: string;\n providerId: string;\n hash: string;\n}\n\nexport class CredentialsStore {\n private initialised: Promise<void> | null = null;\n\n constructor(private readonly docs: IDocumentDatabase) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n // Logical primary key.\n { path: ['userId', 'providerId'], unique: true },\n // Cascade delete needs userId alone.\n { path: 'userId' },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async setCredential(input: SetCredentialInput): Promise<void> {\n await this.ensureSchema();\n await this.docs.updateOne<CredentialDoc>(\n COLLECTION,\n {\n $and: [\n { userId: { $eq: input.userId } },\n { providerId: { $eq: input.providerId } },\n ],\n },\n {\n $set: {\n userId: input.userId,\n providerId: input.providerId,\n hash: input.hash,\n },\n },\n { upsert: true },\n );\n }\n\n async getCredential(userId: string, providerId: string): Promise<Credential | null> {\n await this.ensureSchema();\n const [doc] = await this.docs.find<CredentialDoc>(\n COLLECTION,\n {\n $and: [\n { userId: { $eq: userId } },\n { providerId: { $eq: providerId } },\n ],\n },\n { limit: 1 },\n );\n return doc ? docToCredential(doc) : null;\n }\n\n async deleteCredential(userId: string, providerId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<CredentialDoc>(COLLECTION, {\n $and: [\n { userId: { $eq: userId } },\n { providerId: { $eq: providerId } },\n ],\n });\n }\n\n /**\n * Cascade removal helper — call from the parent flow when a `User` is\n * deleted so we never leave dangling credentials in the store.\n */\n async deleteAllForUser(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<CredentialDoc>(COLLECTION, {\n userId: { $eq: userId },\n });\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/memberships-store\n *\n * Document-backed store for `Membership` records (ADR-0020, Phase 1.3).\n *\n * A membership is one row per `(userId, tenantId)` with a single\n * `groupId`. Group strings are hardcoded for this iteration —\n * `tenant-admin` and `tenant-member` — and consumed by the stub PDP.\n *\n * When the real RBAC + ReBAC engine lands (ClickUp 869def338) the\n * meaning of `groupId` becomes richer (multiple group memberships,\n * group hierarchies, relations) but the store API stays — the engine\n * sits behind the PDP and reads from here. The \"one membership per\n * (userId, tenantId)\" invariant may need to flex later; for now we keep\n * it tight so the stub PDP has unambiguous lookups.\n */\n\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\n\nconst COLLECTION = 'memberships';\n\n/** Hardcoded group ids for the stub PDP iteration. */\nexport type GroupId = 'tenant-admin' | 'tenant-member';\n\nexport interface Membership {\n userId: string;\n tenantId: string;\n groupId: GroupId;\n createdAt: number;\n updatedAt?: number;\n}\n\ninterface MembershipDoc extends BaseDocument {\n userId: string;\n tenantId: string;\n groupId: GroupId;\n}\n\nconst docToMembership = (doc: MembershipDoc): Membership => ({\n userId: doc.userId,\n tenantId: doc.tenantId,\n groupId: doc.groupId,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport interface AddMembershipInput {\n userId: string;\n tenantId: string;\n groupId: GroupId;\n}\n\nexport class MembershipsStore {\n private initialised: Promise<void> | null = null;\n\n constructor(private readonly docs: IDocumentDatabase) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n { path: ['userId', 'tenantId'], unique: true },\n { path: 'userId' },\n { path: 'tenantId' },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async addMembership(input: AddMembershipInput): Promise<void> {\n await this.ensureSchema();\n await this.docs.insertOne<MembershipDoc>(COLLECTION, {\n userId: input.userId,\n tenantId: input.tenantId,\n groupId: input.groupId,\n });\n }\n\n async setGroup(userId: string, tenantId: string, groupId: GroupId): Promise<void> {\n await this.ensureSchema();\n const [existing] = await this.docs.find<MembershipDoc>(\n COLLECTION,\n { $and: [{ userId: { $eq: userId } }, { tenantId: { $eq: tenantId } }] },\n { limit: 1 },\n );\n if (!existing) {\n throw new Error(\n `memberships-store: setGroup on missing membership userId=${userId} tenantId=${tenantId}`,\n );\n }\n await this.docs.updateMany<MembershipDoc>(\n COLLECTION,\n { $and: [{ userId: { $eq: userId } }, { tenantId: { $eq: tenantId } }] },\n { $set: { groupId } },\n );\n }\n\n async listByUser(userId: string): Promise<Membership[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<MembershipDoc>(\n COLLECTION,\n { userId: { $eq: userId } },\n { sort: { tenantId: 1 } },\n );\n return docs.map(docToMembership);\n }\n\n async listByTenant(tenantId: string): Promise<Membership[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<MembershipDoc>(\n COLLECTION,\n { tenantId: { $eq: tenantId } },\n { sort: { userId: 1 } },\n );\n return docs.map(docToMembership);\n }\n\n async removeMembership(userId: string, tenantId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<MembershipDoc>(\n COLLECTION,\n { $and: [{ userId: { $eq: userId } }, { tenantId: { $eq: tenantId } }] },\n );\n }\n\n /** Cascade hook — call when a `User` is deleted. */\n async removeAllForUser(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.deleteMany<MembershipDoc>(COLLECTION, { userId: { $eq: userId } });\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/invites-store\n *\n * Document-backed store for activation invites (ADR-0020, Phase 1.4).\n *\n * Invites are admin-issued tokens that bootstrap a user account: the\n * recipient activates by POSTing the token along with a password.\n *\n * Security details worth keeping straight:\n *\n * - Token plaintext **never** lands in the database. We store\n * `tokenHash = sha256(plain)` and look up by hash. A snapshot of the\n * `invites` collection is useless without the original tokens.\n * - The unique compound `(tenantId, email, status='active')` is not\n * easy to express across drivers, so uniqueness is enforced in code\n * on top of a non-unique compound index. Concurrent races during\n * `createInvite` are not a real attack vector here (admin UI is\n * single-clicked) and we accept the small theoretical window.\n * - TTL is best-effort on the index; **every read path** explicitly\n * checks `expiresAt > now` (CD-9). Without that, a not-yet-swept row\n * would be returned and the activation flow would accept it.\n * - Emails are canonicalised on the way in (CD-4) so admins can paste\n * \"Alice@Example.COM\" without breaking lookups.\n */\n\nimport { randomBytes, createHash } from 'node:crypto';\nimport type {\n BaseDocument,\n IDocumentDatabase,\n} from '@kb-labs/core-platform/adapters';\nimport { canonicalizeEmail } from './users-store.js';\nimport type { GroupId } from './memberships-store.js';\n\nconst COLLECTION = 'invites';\n\nexport type InviteStatus = 'active' | 'used' | 'revoked';\n\nexport interface InviteRecord {\n inviteId: string;\n email: string;\n tenantId: string;\n groupId: GroupId;\n status: InviteStatus;\n createdBy: string;\n createdAt: number;\n expiresAt: number;\n updatedAt?: number;\n}\n\ninterface InviteDoc extends BaseDocument {\n inviteId: string;\n email: string;\n tenantId: string;\n groupId: GroupId;\n status: InviteStatus;\n createdBy: string;\n expiresAt: number;\n tokenHash: string;\n}\n\nexport interface CreateInviteInput {\n email: string;\n tenantId: string;\n groupId: GroupId;\n createdBy: string;\n ttlMs: number;\n}\n\nexport interface CreateInviteResult {\n inviteId: string;\n activationToken: string;\n expiresAt: number;\n}\n\nconst TOKEN_BYTES = 32;\n\nconst generateToken = (): string => randomBytes(TOKEN_BYTES).toString('base64url');\n\nconst hashToken = (plain: string): string =>\n createHash('sha256').update(plain).digest('hex');\n\nconst generateInviteId = (): string => randomBytes(16).toString('base64url');\n\nconst docToInvite = (doc: InviteDoc): InviteRecord => ({\n inviteId: doc.inviteId,\n email: doc.email,\n tenantId: doc.tenantId,\n groupId: doc.groupId,\n status: doc.status,\n createdBy: doc.createdBy,\n expiresAt: doc.expiresAt,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n});\n\nexport class InvitesStore {\n private initialised: Promise<void> | null = null;\n\n constructor(\n private readonly docs: IDocumentDatabase,\n private readonly now: () => number = Date.now,\n ) {}\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(COLLECTION, {\n indexes: [\n { path: 'inviteId', unique: true },\n { path: 'tokenHash', unique: true },\n // Active-invite uniqueness is enforced in code, so this index\n // is non-unique and just speeds the \"is there an active invite\n // for (email, tenantId)\" lookup.\n { path: ['tenantId', 'email', 'status'] },\n // TTL is best-effort; `findByToken` also checks expiresAt > now.\n // `ttl: 0` means \"sweep as soon as `expiresAt` is in the past\".\n { path: 'expiresAt', ttl: 0 },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async createInvite(input: CreateInviteInput): Promise<CreateInviteResult> {\n await this.ensureSchema();\n const email = canonicalizeEmail(input.email);\n\n // Uniqueness check — best effort, see module docstring.\n const [existingActive] = await this.docs.find<InviteDoc>(\n COLLECTION,\n {\n $and: [\n { tenantId: { $eq: input.tenantId } },\n { email: { $eq: email } },\n { status: { $eq: 'active' as InviteStatus } },\n { expiresAt: { $gt: this.now() } },\n ],\n },\n { limit: 1 },\n );\n if (existingActive) {\n throw new Error(\n `invites-store: an active invite already exists for email=${email} tenantId=${input.tenantId}; revoke it before issuing a new one`,\n );\n }\n\n const inviteId = generateInviteId();\n const activationToken = generateToken();\n const tokenHash = hashToken(activationToken);\n const expiresAt = this.now() + input.ttlMs;\n\n await this.docs.insertOne<InviteDoc>(COLLECTION, {\n inviteId,\n email,\n tenantId: input.tenantId,\n groupId: input.groupId,\n status: 'active',\n createdBy: input.createdBy,\n expiresAt,\n tokenHash,\n });\n\n return { inviteId, activationToken, expiresAt };\n }\n\n async findById(inviteId: string): Promise<InviteRecord | null> {\n await this.ensureSchema();\n const [doc] = await this.docs.find<InviteDoc>(\n COLLECTION,\n { inviteId: { $eq: inviteId } },\n { limit: 1 },\n );\n return doc ? docToInvite(doc) : null;\n }\n\n /**\n * Look up an invite by the plaintext activation token. Returns `null`\n * for missing, expired, used, or revoked invites — callers do not\n * need to filter further.\n *\n * Returns a discriminated result so callers can distinguish:\n * - { kind: 'not_found' } — token hash not in DB (garbage/unknown token)\n * - { kind: 'invalid' } — token exists but expired, used, or revoked\n * - { kind: 'ok', invite } — valid, active invite ready for activation\n */\n async findByToken(plainToken: string): Promise<\n | { kind: 'not_found' }\n | { kind: 'invalid' }\n | { kind: 'ok'; invite: InviteRecord }\n > {\n await this.ensureSchema();\n const tokenHash = hashToken(plainToken);\n const [doc] = await this.docs.find<InviteDoc>(\n COLLECTION,\n { tokenHash: { $eq: tokenHash } },\n { limit: 1 },\n );\n if (!doc) {return { kind: 'not_found' };}\n if (doc.status !== 'active' || doc.expiresAt <= this.now()) {\n // CD-9 explicit expiry check — TTL sweep is best-effort.\n return { kind: 'invalid' };\n }\n return { kind: 'ok', invite: docToInvite(doc) };\n }\n\n /**\n * Atomically mark an active invite as used.\n *\n * Returns `true` if THIS call flipped the invite from `active` → `used`,\n * `false` if it was already used/revoked (or unknown). The filter pins\n * `status: 'active'`, so a concurrent double-activation of the same token\n * results in exactly one caller seeing `true` — the activation flow relies\n * on this to close the TOCTOU window between `findByToken` and account\n * creation (a second parallel request must NOT create a duplicate account).\n */\n async consume(inviteId: string): Promise<boolean> {\n await this.ensureSchema();\n const modified = await this.docs.updateMany<InviteDoc>(\n COLLECTION,\n { $and: [{ inviteId: { $eq: inviteId } }, { status: { $eq: 'active' as InviteStatus } }] },\n { $set: { status: 'used' } },\n );\n return modified > 0;\n }\n\n async revoke(inviteId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.updateMany<InviteDoc>(\n COLLECTION,\n { $and: [{ inviteId: { $eq: inviteId } }, { status: { $eq: 'active' as InviteStatus } }] },\n { $set: { status: 'revoked' } },\n );\n }\n\n /** List all invites for a tenant (active + used + revoked). */\n async listByTenant(tenantId: string): Promise<InviteRecord[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<InviteDoc>(\n COLLECTION,\n { tenantId: { $eq: tenantId } },\n );\n return docs.map(docToInvite);\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/sessions-store\n *\n * Session families + refresh tokens (ADR-0020, Phase 1.5).\n *\n * This module owns three things:\n *\n * 1. **Session families.** One row per device. The family is the unit\n * of revocation — \"log out this device\", \"kick this user from all\n * devices\", \"the password just changed, keep the current device\".\n * 2. **Refresh tokens.** One-shot rotation: every refresh request\n * consumes the old jti and issues a new one in the same family.\n * 3. **Reuse detection + 5-second grace window (CD-5).** Reusing an\n * already-consumed refresh is either a stolen cookie or a legit\n * multi-tab race. The grace window resolves the ambiguity: if the\n * client presents the same already-consumed jti within 5s, we\n * return the same replacement we issued first time. Beyond 5s, we\n * treat it as theft and kill the entire family.\n *\n * Notes on storage:\n *\n * - Refresh tokens carry an explicit `expiresAt` and we check it\n * manually on every read (CD-9) — the TTL index sweep is best-effort.\n * - `rotateRefresh` runs inside a `transaction(cb)` so a concurrent\n * second caller sees the consumed state and goes through the\n * grace/reuse branch instead of inserting a parallel replacement.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport type {\n BaseDocument,\n IDocumentDatabase,\n IDocumentTransaction,\n} from '@kb-labs/core-platform/adapters';\n\nconst FAMILIES = 'session_families';\nconst REFRESHES = 'refresh_tokens';\n\nexport interface SessionFamily {\n familyId: string;\n userId: string;\n tenantId: string;\n createdAt: number;\n lastUsedAt: number;\n userAgent?: string;\n ipFirst?: string;\n}\n\ninterface FamilyDoc extends BaseDocument {\n familyId: string;\n userId: string;\n tenantId: string;\n createdAt: number;\n lastUsedAt: number;\n userAgent?: string;\n ipFirst?: string;\n}\n\ninterface RefreshDoc extends BaseDocument {\n jti: string;\n familyId: string;\n userId: string;\n expiresAt: number;\n consumedAt?: number;\n replacedBy?: string;\n}\n\nexport class RefreshNotFoundError extends Error {\n constructor() { super('refresh token not found'); this.name = 'RefreshNotFoundError'; }\n}\nexport class RefreshExpiredError extends Error {\n constructor() { super('refresh token expired'); this.name = 'RefreshExpiredError'; }\n}\nexport class RefreshReuseDetectedError extends Error {\n constructor(public readonly familyId: string) {\n super(`refresh token reuse detected (family ${familyId} killed)`);\n this.name = 'RefreshReuseDetectedError';\n }\n}\n\nexport interface SessionsStoreOptions {\n /** Override `Date.now` — only used in tests. Defaults to `Date.now`. */\n now?: () => number;\n /** Refresh token TTL in ms. */\n refreshTtlMs: number;\n /** Grace window for legitimate retries (CD-5). Recommended 5_000ms. */\n graceWindowMs: number;\n}\n\nexport interface CreateSessionInput {\n userId: string;\n tenantId: string;\n deviceCtx: { userAgent?: string; ip?: string };\n}\n\nexport interface CreateSessionResult {\n familyId: string;\n refreshJti: string;\n refreshExpiresAt: number;\n}\n\nexport interface RotateRefreshResult {\n newJti: string;\n familyId: string;\n newRefreshExpiresAt: number;\n}\n\nconst newId = (bytes = 16): string => randomBytes(bytes).toString('base64url');\n\nexport class SessionsStore {\n private initialised: Promise<void> | null = null;\n private readonly now: () => number;\n private readonly refreshTtlMs: number;\n private readonly graceWindowMs: number;\n\n constructor(\n private readonly docs: IDocumentDatabase,\n opts: SessionsStoreOptions,\n ) {\n this.now = opts.now ?? Date.now;\n this.refreshTtlMs = opts.refreshTtlMs;\n this.graceWindowMs = opts.graceWindowMs;\n }\n\n private async ensureSchema(): Promise<void> {\n if (!this.initialised) {\n this.initialised = (async () => {\n await this.docs.ensureCollection(FAMILIES, {\n indexes: [\n { path: 'familyId', unique: true },\n { path: 'userId' },\n ],\n });\n await this.docs.ensureCollection(REFRESHES, {\n indexes: [\n { path: 'jti', unique: true },\n { path: 'familyId' },\n { path: 'userId' },\n // Best-effort sweep; `rotateRefresh` checks expiresAt > now (CD-9).\n { path: 'expiresAt', ttl: 0 },\n ],\n });\n })();\n }\n await this.initialised;\n }\n\n async createSession(input: CreateSessionInput): Promise<CreateSessionResult> {\n await this.ensureSchema();\n const familyId = newId();\n const refreshJti = newId();\n const now = this.now();\n const refreshExpiresAt = now + this.refreshTtlMs;\n\n await this.docs.transaction(async (tx: IDocumentTransaction) => {\n await tx.insertOne<FamilyDoc>(FAMILIES, {\n familyId,\n userId: input.userId,\n tenantId: input.tenantId,\n lastUsedAt: now,\n userAgent: input.deviceCtx.userAgent,\n ipFirst: input.deviceCtx.ip,\n });\n await tx.insertOne<RefreshDoc>(REFRESHES, {\n jti: refreshJti,\n familyId,\n userId: input.userId,\n expiresAt: refreshExpiresAt,\n });\n });\n\n return { familyId, refreshJti, refreshExpiresAt };\n }\n\n /**\n * Consume `oldJti` and issue a new refresh in the same family.\n *\n * Throws:\n * - `RefreshNotFoundError` if jti is unknown or its family was revoked.\n * - `RefreshExpiredError` if jti is past `expiresAt` (family stays alive).\n * - `RefreshReuseDetectedError` if jti was already consumed and the\n * grace window has passed — family is killed before throwing.\n *\n * On grace-window retry (same already-consumed jti within\n * `graceWindowMs`) the previously-issued replacement is returned and\n * no new refresh is created.\n */\n async rotateRefresh(oldJti: string): Promise<RotateRefreshResult> {\n await this.ensureSchema();\n const now = this.now();\n const newJti = newId();\n const newExpiresAt = now + this.refreshTtlMs;\n\n // Outcome tag: lets us decide the side-effect AFTER the transaction.\n // We cannot throw `RefreshReuseDetectedError` from inside the\n // transaction callback — that rolls back the family deletion. So\n // reuse is detected inside (atomically, against a consistent\n // snapshot) and the family is killed outside.\n type Outcome =\n | { tag: 'ok'; result: RotateRefreshResult }\n | { tag: 'reuse'; familyId: string }\n | { tag: 'not-found' }\n | { tag: 'expired' };\n\n const outcome = await this.docs.transaction<Outcome>(async (tx) => {\n const [current] = await tx.find<RefreshDoc>(\n REFRESHES,\n { jti: { $eq: oldJti } },\n { limit: 1 },\n );\n if (!current) {return { tag: 'not-found' };}\n\n // Reuse / grace branch.\n if (current.consumedAt !== undefined) {\n const elapsed = now - current.consumedAt;\n if (elapsed <= this.graceWindowMs && current.replacedBy) {\n // Legitimate retry — return what we issued before.\n const [replacement] = await tx.find<RefreshDoc>(\n REFRESHES,\n { jti: { $eq: current.replacedBy } },\n { limit: 1 },\n );\n if (replacement) {\n return {\n tag: 'ok',\n result: {\n newJti: replacement.jti,\n familyId: current.familyId,\n newRefreshExpiresAt: replacement.expiresAt,\n },\n };\n }\n // Replacement vanished (family revoked between consume and retry).\n // Fall through to reuse detection.\n }\n // If the token is also past its expiry, it was legitimately rotated long ago —\n // treat as stale retry, not theft. Family stays alive.\n if (current.expiresAt <= now) {\n return { tag: 'expired' };\n }\n return { tag: 'reuse', familyId: current.familyId };\n }\n\n // Natural expiry — family is fine, this refresh just timed out.\n if (current.expiresAt <= now) {\n return { tag: 'expired' };\n }\n\n // Happy path — consume old, insert new, bump family lastUsedAt.\n await tx.updateMany<RefreshDoc>(\n REFRESHES,\n { jti: { $eq: oldJti } },\n { $set: { consumedAt: now, replacedBy: newJti } },\n );\n await tx.insertOne<RefreshDoc>(REFRESHES, {\n jti: newJti,\n familyId: current.familyId,\n userId: current.userId,\n expiresAt: newExpiresAt,\n });\n await tx.updateMany<FamilyDoc>(\n FAMILIES,\n { familyId: { $eq: current.familyId } },\n { $set: { lastUsedAt: now } },\n );\n\n return {\n tag: 'ok',\n result: { newJti, familyId: current.familyId, newRefreshExpiresAt: newExpiresAt },\n };\n });\n\n switch (outcome.tag) {\n case 'ok':\n return outcome.result;\n case 'not-found':\n throw new RefreshNotFoundError();\n case 'expired':\n throw new RefreshExpiredError();\n case 'reuse':\n // Kill the family — outside the read transaction so the writes commit.\n await this.revokeFamily(outcome.familyId);\n throw new RefreshReuseDetectedError(outcome.familyId);\n }\n }\n\n async revokeFamily(familyId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.transaction(async (tx) => {\n await tx.deleteMany<RefreshDoc>(REFRESHES, { familyId: { $eq: familyId } });\n await tx.deleteMany<FamilyDoc>(FAMILIES, { familyId: { $eq: familyId } });\n });\n }\n\n async revokeAllUserSessions(userId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.transaction(async (tx) => {\n await tx.deleteMany<RefreshDoc>(REFRESHES, { userId: { $eq: userId } });\n await tx.deleteMany<FamilyDoc>(FAMILIES, { userId: { $eq: userId } });\n });\n }\n\n async revokeAllUserSessionsExcept(userId: string, exceptFamilyId: string): Promise<void> {\n await this.ensureSchema();\n await this.docs.transaction(async (tx) => {\n const families = await tx.find<FamilyDoc>(FAMILIES, { userId: { $eq: userId } });\n const targets = families.filter(f => f.familyId !== exceptFamilyId).map(f => f.familyId);\n if (targets.length === 0) {return;}\n await tx.deleteMany<RefreshDoc>(\n REFRESHES,\n { $and: [{ userId: { $eq: userId } }, { familyId: { $in: targets } }] },\n );\n await tx.deleteMany<FamilyDoc>(\n FAMILIES,\n { $and: [{ userId: { $eq: userId } }, { familyId: { $in: targets } }] },\n );\n });\n }\n\n async listFamiliesByUser(userId: string): Promise<SessionFamily[]> {\n await this.ensureSchema();\n const docs = await this.docs.find<FamilyDoc>(\n FAMILIES,\n { userId: { $eq: userId } },\n { sort: { lastUsedAt: -1 } },\n );\n return docs.map(d => ({\n familyId: d.familyId,\n userId: d.userId,\n tenantId: d.tenantId,\n createdAt: d.createdAt,\n lastUsedAt: d.lastUsedAt,\n userAgent: d.userAgent,\n ipFirst: d.ipFirst,\n }));\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/password-policy\n *\n * Password validation rules (ADR-0020, Phase 1.6).\n *\n * Length: 8..256. No complexity rules — NIST 800-63B explicitly\n * recommends against them.\n *\n * HIBP check via k-anonymity (https://haveibeenpwned.com/API/v3#PwnedPasswords):\n * we send the first 5 hex chars of SHA-1(password) and grep the\n * suffix list locally. The full password never leaves the process.\n *\n * Availability vs. UX trade-off: HIBP outages return `{ ok: true,\n * warning: 'hibp_unavailable' }`. A security check that hard-fails on\n * network glitches breaks more user flows than it protects — we log\n * the warning and let the activation/change-password flow continue.\n */\n\nimport { createHash } from 'node:crypto';\n\nexport type ValidationResult =\n | { ok: true; warning?: 'hibp_unavailable' }\n | { ok: false; reason: 'too_short' | 'too_long' | 'pwned' };\n\nexport interface PasswordPolicyLogger {\n warn(...args: unknown[]): void;\n info(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport interface PasswordPolicyOptions {\n minLength: number;\n maxLength: number;\n hibpEnabled: boolean;\n /** Override for tests. Defaults to global `fetch`. */\n fetch?: typeof fetch;\n /** Override for tests / production logger. Defaults to console-shaped no-op. */\n logger?: PasswordPolicyLogger;\n}\n\nexport interface PasswordPolicy {\n validate(plain: string): Promise<ValidationResult>;\n}\n\nconst noopLogger: PasswordPolicyLogger = {\n warn: () => undefined,\n info: () => undefined,\n error: () => undefined,\n};\n\nconst sha1Hex = (input: string): string =>\n createHash('sha1').update(input).digest('hex').toUpperCase();\n\n/**\n * Returns the count of breach occurrences if the (sha1, suffix) is in\n * HIBP, or `0` if absent. Throws on network / non-2xx errors.\n */\nconst queryHibp = async (\n plain: string,\n doFetch: typeof fetch,\n): Promise<number> => {\n const hash = sha1Hex(plain);\n const prefix = hash.slice(0, 5);\n const wantedSuffix = hash.slice(5);\n const resp = await doFetch(`https://api.pwnedpasswords.com/range/${prefix}`);\n if (!resp.ok) {\n throw new Error(`HIBP ${resp.status}`);\n }\n const text = await resp.text();\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) {continue;}\n const sepIdx = trimmed.indexOf(':');\n if (sepIdx < 0) {continue;}\n const suffix = trimmed.slice(0, sepIdx).toUpperCase();\n if (suffix === wantedSuffix) {\n const count = Number.parseInt(trimmed.slice(sepIdx + 1), 10);\n return Number.isFinite(count) ? count : 1;\n }\n }\n return 0;\n};\n\nexport const createPasswordPolicy = (opts: PasswordPolicyOptions): PasswordPolicy => {\n const log = opts.logger ?? noopLogger;\n const doFetch = opts.fetch ?? fetch;\n\n return {\n async validate(plain: string): Promise<ValidationResult> {\n if (plain.length < opts.minLength) {\n return { ok: false, reason: 'too_short' };\n }\n if (plain.length > opts.maxLength) {\n return { ok: false, reason: 'too_long' };\n }\n if (!opts.hibpEnabled) {\n return { ok: true };\n }\n try {\n const count = await queryHibp(plain, doFetch);\n if (count > 0) {\n return { ok: false, reason: 'pwned' };\n }\n return { ok: true };\n } catch (err) {\n log.warn('password-policy: HIBP unavailable, accepting password', { err: String(err) });\n return { ok: true, warning: 'hibp_unavailable' };\n }\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/csrf\n *\n * Double-submit CSRF helpers (ADR-0020, Phase 1.7).\n *\n * The contract is intentionally minimal: a token is just an opaque\n * random string and verification is a constant-time string compare.\n * No server-side state, no rotation logic — the cookie + header pair\n * is the entire protocol.\n *\n * The `Path=/` non-HttpOnly cookie is set in the route layer\n * alongside `kb_access`. Studio JS reads it via `document.cookie` and\n * echoes it via `X-CSRF-Token` on every mutating request.\n */\n\nimport { randomBytes, timingSafeEqual } from 'node:crypto';\n\n/**\n * Generate a fresh 32-byte token, base64url-encoded.\n */\nexport const issueCsrfToken = (): string => randomBytes(32).toString('base64url');\n\n/**\n * Verify the double-submit pair.\n *\n * Returns `true` only when both inputs are non-empty strings of the\n * same length and equal under a constant-time compare. Length\n * mismatches return `false` without involving timingSafeEqual (which\n * throws on length mismatch).\n */\nexport const verifyCsrfToken = (\n cookie: string | undefined,\n header: string | undefined,\n): boolean => {\n if (!cookie || !header) {return false;}\n if (cookie.length !== header.length) {return false;}\n const a = Buffer.from(cookie, 'utf8');\n const b = Buffer.from(header, 'utf8');\n // Buffers of identical length — safe to call timingSafeEqual.\n return timingSafeEqual(a, b);\n};\n","/**\n * @module @kb-labs/gateway-auth/rate-limit\n *\n * Fixed-window rate limiter on top of `IKVStore` (ADR-0020, Phase 1.8).\n *\n * Used for per-email login limits and activation rate-limiting.\n * Per-IP limits are configured at the gateway layer itself (its\n * existing rate-limit plugin) and are not touched here.\n *\n * Fixed window, not sliding: TTL is applied **once** on the first\n * increment and never extended. Subsequent increments within the\n * window just bump the counter. Once the key expires, the counter\n * starts over.\n *\n * The choice of fixed vs sliding matters: a sliding window would mean\n * \"5 attempts in any 60-second window\", which an attacker can game by\n * pacing exactly under the limit forever. Fixed window puts a hard\n * ceiling per window and the counter actually resets.\n */\n\nimport type { IKVStore } from '@kb-labs/core-platform/adapters';\n\nexport interface RateLimitConfig {\n max: number;\n windowMs: number;\n}\n\nexport type RateLimitResult =\n | { allowed: true; remaining: number }\n | { allowed: false; retryAfterSec: number };\n\nexport interface RateLimiter {\n /**\n * Increment the counter and report whether this hit is allowed.\n * Use on the event you want to count (e.g. a FAILED login attempt).\n */\n check(key: string, cfg: RateLimitConfig): Promise<RateLimitResult>;\n /**\n * Read the current counter WITHOUT incrementing and report whether the\n * next `check()` would be allowed.\n *\n * This exists so a caller can reject early — before doing expensive work\n * such as a bcrypt compare — once the window is already exhausted. Without\n * a peek, the only way to know the limit is hit is to `check()` (which\n * increments), forcing the expensive work to run first. peek() lets the\n * login handler gate bcrypt on the existing failure count, closing a\n * CPU-exhaustion DoS where an attacker spams requests past the limit and\n * still pays for a bcrypt round-trip on each.\n */\n peek(key: string, cfg: RateLimitConfig): Promise<RateLimitResult>;\n}\n\nconst retryAfterFor = async (kv: IKVStore, key: string, cfg: RateLimitConfig): Promise<number> => {\n const remainingTtlMs = await kv.ttl(key);\n return remainingTtlMs !== null\n ? Math.max(1, Math.ceil(remainingTtlMs / 1000))\n : Math.ceil(cfg.windowMs / 1000);\n};\n\nexport const createRateLimiter = (kv: IKVStore): RateLimiter => ({\n async check(key: string, cfg: RateLimitConfig): Promise<RateLimitResult> {\n const count = await kv.incr(key, 1);\n if (count === 1) {\n // First hit in this window — set the TTL. Subsequent hits will\n // just bump the counter without touching the TTL.\n await kv.expire(key, cfg.windowMs);\n }\n if (count > cfg.max) {\n return { allowed: false, retryAfterSec: await retryAfterFor(kv, key, cfg) };\n }\n return { allowed: true, remaining: cfg.max - count };\n },\n\n async peek(key: string, cfg: RateLimitConfig): Promise<RateLimitResult> {\n const raw = await kv.get<number>(key);\n const count = typeof raw === 'number' ? raw : 0;\n // The window is exhausted once the counter has already reached `max`:\n // the next check() would push it to max+1 and be denied. Reject now,\n // before any expensive work, without touching the counter.\n if (count >= cfg.max) {\n return { allowed: false, retryAfterSec: await retryAfterFor(kv, key, cfg) };\n }\n return { allowed: true, remaining: cfg.max - count };\n },\n});\n","/**\n * @module @kb-labs/gateway-auth/tenant-resolver\n *\n * Map `Host`-header → `tenantId` (ADR-0020, Phase 1.9).\n *\n * Pattern is configurable (`{tenant}.kblabs.ru` in cloud) so the same\n * resolver can be used in dev (`{tenant}.localhost`) or for on-prem\n * customers (`{tenant}.acme.example.com`).\n *\n * Returns `null` instead of throwing on bad/reserved hosts — the\n * middleware then renders the right 4xx and avoids surfacing an\n * internal error. Slug rules are strict (`[a-z0-9-]{2,40}`, no\n * leading/trailing dash) so a stray \"weird\" subdomain doesn't become a\n * valid tenant by accident.\n */\n\n// 2..40 chars; first and last must be alnum; middle can include hyphens.\nconst SLUG_RE = /^[a-z0-9][a-z0-9-]{0,38}[a-z0-9]$/;\n\nexport const RESERVED_SUBDOMAINS: ReadonlySet<string> = new Set([\n 'api',\n 'www',\n 'docs',\n 'studio',\n 'mail',\n 'admin',\n 'static',\n 'cdn',\n]);\n\nexport interface TenantResolverOptions {\n /** Host pattern containing exactly one `{tenant}` placeholder. */\n pattern: string;\n /** Optional override of the reserved set (must remain a superset of the default in production). */\n reserved?: ReadonlySet<string>;\n}\n\nexport interface TenantResolver {\n resolve(host: string | undefined): string | null;\n}\n\n/**\n * Compile the `{tenant}.example.com` pattern into a regex.\n *\n * Apex placeholder is escaped (so `.` matches a literal `.`), and the\n * `{tenant}` placeholder is replaced by a non-greedy match that the\n * outer code then validates against `SLUG_RE`.\n */\nconst compilePattern = (pattern: string): RegExp => {\n const escapedTail = pattern\n .replace('{tenant}.', '')\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&');\n return new RegExp(`^([^.]+)\\\\.${escapedTail}$`);\n};\n\nexport const createTenantResolver = (opts: TenantResolverOptions): TenantResolver => {\n const re = compilePattern(opts.pattern);\n const reserved = opts.reserved ?? RESERVED_SUBDOMAINS;\n const cache = new Map<string, string | null>();\n const CACHE_LIMIT = 100;\n\n return {\n resolve(host: string | undefined): string | null {\n if (!host) {return null;}\n const normalised = host.toLowerCase().split(':')[0] ?? '';\n if (!normalised) {return null;}\n if (cache.has(normalised)) {return cache.get(normalised)!;}\n\n let result: string | null = null;\n const m = normalised.match(re);\n if (m) {\n const candidate = m[1]!;\n if (SLUG_RE.test(candidate) && !reserved.has(candidate)) {\n result = candidate;\n }\n }\n\n if (cache.size >= CACHE_LIMIT) {\n // Crude LRU — drop the oldest insert.\n const firstKey = cache.keys().next().value;\n if (firstKey !== undefined) {cache.delete(firstKey);}\n }\n cache.set(normalised, result);\n return result;\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/providers/email-password\n *\n * Built-in identity provider for the local email-password flow\n * (ADR-0020, Phase 1.10). Implements `IIdentityProvider` exactly as a\n * future Google/Okta provider would — `email-password` has no special\n * status beyond being the one we ship.\n *\n * Two design notes worth keeping in mind:\n *\n * - **Constant-time response (CD-8).** Every not-ok path runs a real\n * bcrypt compare against a fixed dummy hash. Without this, a missing\n * user returns in microseconds while a wrong-password attempt takes\n * ~hundreds of milliseconds — a textbook user-enumeration timing\n * channel. The discriminated `reason` is for **logs only**; the HTTP\n * layer collapses it to a single opaque \"invalid_credentials\".\n *\n * - **No `setCredential` here.** This provider only *reads* credentials.\n * The activation flow writes the hash via the service layer, which\n * chooses the bcrypt cost from config. Keeping write logic out of\n * the provider means future Google/Okta providers don't carry a\n * meaningless `setCredential` method.\n */\n\nimport bcrypt from 'bcryptjs';\nimport type {\n IIdentityProvider,\n IdentityResult,\n IdentityUserPort,\n IdentityCredentialPort,\n} from '@kb-labs/core-contracts';\nimport { canonicalizeEmail } from '../users-store.js';\n\nexport interface EmailPasswordProviderOptions {\n /**\n * Narrow read port over users (DD-2). The gateway's `UsersStore`\n * satisfies this structurally; the provider only ever reads.\n */\n users: IdentityUserPort;\n /** Narrow read port over credentials (DD-2). */\n credentials: IdentityCredentialPort;\n /** The tenant this provider instance authenticates against. */\n tenantId: string;\n /** bcrypt cost used for the dummy compare timing. Should match the\n * cost used by the activation flow so dummy/real timings are similar. */\n bcryptCost: number;\n}\n\n/**\n * A pre-computed bcrypt hash used for the dummy compare on every\n * not-ok path. The actual plaintext is irrelevant — what matters is\n * that `bcrypt.compare` runs against a real hash of the right cost.\n *\n * NOTE: This is computed lazily once per provider instance from the\n * configured `bcryptCost` so dummy hashes match the cost the\n * activation flow will produce.\n */\nconst dummyHashFor = (cost: number): Promise<string> =>\n bcrypt.hash('dummy-password-for-timing', cost);\n\ninterface PasswordInput {\n email: string;\n password: string;\n}\n\nconst isPasswordInput = (raw: unknown): raw is PasswordInput => {\n if (!raw || typeof raw !== 'object') {return false;}\n const r = raw as Record<string, unknown>;\n return typeof r.email === 'string' && typeof r.password === 'string'\n && r.email.length > 0 && r.password.length > 0;\n};\n\nexport const createEmailPasswordProvider = (\n opts: EmailPasswordProviderOptions,\n): IIdentityProvider => {\n // Compute the dummy hash once; reused on every not-ok path.\n const dummyHashPromise = dummyHashFor(opts.bcryptCost);\n\n return {\n id: 'email-password',\n kind: 'password',\n\n async authenticate(input: unknown): Promise<IdentityResult> {\n if (!isPasswordInput(input)) {\n // Even on shape-invalid input, do a dummy bcrypt so timing\n // does not give away \"well-formed vs malformed payload\".\n await bcrypt.compare('x', await dummyHashPromise);\n return { ok: false, reason: 'invalid' };\n }\n\n const email = canonicalizeEmail(input.email);\n const user = await opts.users.findByEmailTenant(email, opts.tenantId);\n\n if (!user) {\n await bcrypt.compare(input.password, await dummyHashPromise);\n return { ok: false, reason: 'unknown' };\n }\n\n if (user.status !== 'active') {\n await bcrypt.compare(input.password, await dummyHashPromise);\n return { ok: false, reason: 'disabled' };\n }\n\n const cred = await opts.credentials.getCredential(user.userId, 'email-password');\n if (!cred) {\n await bcrypt.compare(input.password, await dummyHashPromise);\n return { ok: false, reason: 'unknown' };\n }\n\n const match = await bcrypt.compare(input.password, cred.hash);\n if (!match) {\n return { ok: false, reason: 'invalid' };\n }\n\n return { ok: true, email: user.email, externalId: user.userId };\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/providers/oidc\n *\n * Built-in generic OIDC redirect provider (ADR-0020, Step 5).\n *\n * Implements the authorization-code flow against any spec-compliant OIDC\n * IdP (Google / Okta / Auth0 / Keycloak / Azure AD): `startAuthorization`\n * builds the upstream authorize URL plus a per-attempt `nonce` (and optional\n * PKCE `code_verifier`); `authenticate` exchanges the code at the token\n * endpoint and verifies the returned ID token before returning the canonical\n * identity. The gateway then does email→user→session as for any provider.\n *\n * Security guarantees this provider owns (not \"quality of a custom\" — these\n * are the floor we promise):\n *\n * - **alg allowlist** — `jwtVerify(..., { algorithms: ['RS256','ES256'] })`.\n * `none` and HS* are rejected (alg-confusion / key-confusion). Same lesson\n * as pinning HS256 in jwt.ts: never let the token choose its own algorithm\n * class.\n * - **iss / aud** — verified against the configured issuer and clientId.\n * - **exp / nonce** — exp enforced by jose; nonce must echo the per-attempt\n * value we stashed in the state session (replay / token-injection guard).\n * - **email_verified === true** — required unless `allowUnverifiedEmail`,\n * else an IdP that federates unverified emails becomes an account-takeover\n * vector.\n * - **issuer https-only** — discovery / JWKS targets are admin-controlled but\n * we still refuse plaintext.\n * - **secret hygiene** — `clientSecret` is read from an env var\n * (`clientSecretEnv`) by default; nothing secret is ever logged (no code /\n * id_token / access_token / client_secret in any log line).\n *\n * `fetch` is injected (DD-8) so the whole flow is testable against a locally\n * signed token with a fake transport — no network, no global mocks. JWKS are\n * fetched via the same injected `fetch` and verified with a local JWK set,\n * with a single refetch on key rotation.\n */\n\nimport { z } from 'zod';\nimport {\n jwtVerify,\n createLocalJWKSet,\n type JSONWebKeySet,\n type JWTPayload,\n} from 'jose';\nimport { randomBytes, createHash } from 'node:crypto';\nimport type {\n IRedirectIdentityProvider,\n IdentityResult,\n IdentityProviderDeps,\n RedirectStartContext,\n RedirectStartResult,\n} from '@kb-labs/core-contracts';\nimport { canonicalizeEmail } from '../users-store.js';\n\n// ── Config schema ───────────────────────────────────────────────────────────\n\nconst OidcConfigSchema = z\n .object({\n type: z.literal('oidc'),\n id: z.string().min(1),\n issuer: z\n .string()\n .url()\n .refine((u) => u.startsWith('https://'), { message: 'issuer must be https' }),\n clientId: z.string().min(1),\n /** Direct secret (discouraged — prefer clientSecretEnv). */\n clientSecret: z.string().min(1).optional(),\n /** Name of the env var holding the secret (preferred). */\n clientSecretEnv: z.string().min(1).optional(),\n /** OAuth scopes; `openid` is always included. Default `['openid','email']`. */\n scopes: z.array(z.string()).optional(),\n /** Enable PKCE (S256). */\n pkce: z.boolean().optional(),\n /** Accept tokens whose email_verified is false (default false). */\n allowUnverifiedEmail: z.boolean().optional(),\n })\n .passthrough();\n\nexport type OidcProviderConfig = z.infer<typeof OidcConfigSchema>;\n\n// ── Discovery / JWKS ──────────────────────────────────────────────────────────\n\ninterface Discovery {\n authorization_endpoint: string;\n token_endpoint: string;\n jwks_uri: string;\n}\n\nconst VERIFY_ALGS = ['RS256', 'ES256'];\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction base64url(buf: Buffer): string {\n return buf.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction resolveSecret(config: OidcProviderConfig): string {\n if (config.clientSecretEnv) {\n const fromEnv = process.env[config.clientSecretEnv];\n if (!fromEnv) {\n throw new Error(\n `oidc provider \"${config.id}\": clientSecretEnv \"${config.clientSecretEnv}\" is not set`,\n );\n }\n return fromEnv;\n }\n if (config.clientSecret) {\n return config.clientSecret;\n }\n throw new Error(\n `oidc provider \"${config.id}\": no client secret — set clientSecretEnv (preferred) or clientSecret`,\n );\n}\n\ninterface CallbackBundle {\n code?: string;\n error?: string;\n session?: { nonce?: string; codeVerifier?: string };\n redirectUri?: string;\n}\n\nfunction asBundle(input: unknown): CallbackBundle {\n return (input && typeof input === 'object' ? input : {}) as CallbackBundle;\n}\n\n// ── Factory ─────────────────────────────────────────────────────────────────\n\nexport function createOidcProvider(\n rawConfig: unknown,\n deps: IdentityProviderDeps,\n): IRedirectIdentityProvider {\n const config = OidcConfigSchema.parse(rawConfig);\n const secret = resolveSecret(config);\n const scopes = config.scopes && config.scopes.length > 0 ? config.scopes : ['openid', 'email'];\n // `openid` is mandatory for OIDC.\n const scopeParam = [...new Set(['openid', ...scopes])].join(' ');\n const doFetch = deps.fetch ?? globalThis.fetch;\n\n let discoveryCache: Discovery | null = null;\n let jwksCache: ReturnType<typeof createLocalJWKSet> | null = null;\n\n async function discover(): Promise<Discovery> {\n if (discoveryCache) {\n return discoveryCache;\n }\n const url = `${config.issuer.replace(/\\/$/, '')}/.well-known/openid-configuration`;\n const res = await doFetch(url);\n if (!res.ok) {\n throw new Error(`oidc \"${config.id}\": discovery failed (${res.status})`);\n }\n const json = (await res.json()) as Discovery;\n discoveryCache = json;\n return json;\n }\n\n async function getJwks(force = false): Promise<ReturnType<typeof createLocalJWKSet>> {\n if (jwksCache && !force) {\n return jwksCache;\n }\n const { jwks_uri } = await discover();\n const res = await doFetch(jwks_uri);\n if (!res.ok) {\n throw new Error(`oidc \"${config.id}\": JWKS fetch failed (${res.status})`);\n }\n const jwks = (await res.json()) as JSONWebKeySet;\n jwksCache = createLocalJWKSet(jwks);\n return jwksCache;\n }\n\n async function verifyIdToken(idToken: string): Promise<JWTPayload> {\n const opts = { algorithms: VERIFY_ALGS, issuer: config.issuer, audience: config.clientId };\n try {\n const { payload } = await jwtVerify(idToken, await getJwks(), opts);\n return payload;\n } catch (err) {\n // A key-rotation miss is the one recoverable case: refetch JWKS once and\n // retry. Any other failure (bad sig, alg, iss, aud, exp) propagates.\n const code = (err as { code?: string }).code;\n if (code === 'ERR_JWKS_NO_MATCHING_KEY') {\n const { payload } = await jwtVerify(idToken, await getJwks(true), opts);\n return payload;\n }\n throw err;\n }\n }\n\n const provider: IRedirectIdentityProvider = {\n id: config.id,\n kind: 'redirect',\n\n async startAuthorization(ctx: RedirectStartContext): Promise<RedirectStartResult> {\n const { authorization_endpoint } = await discover();\n const nonce = base64url(randomBytes(16));\n const url = new URL(authorization_endpoint);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('client_id', config.clientId);\n url.searchParams.set('redirect_uri', ctx.redirectUri);\n url.searchParams.set('scope', scopeParam);\n url.searchParams.set('state', ctx.state);\n url.searchParams.set('nonce', nonce);\n\n const session: Record<string, unknown> = { nonce };\n\n if (config.pkce) {\n const codeVerifier = base64url(randomBytes(32));\n const challenge = base64url(createHash('sha256').update(codeVerifier).digest());\n url.searchParams.set('code_challenge', challenge);\n url.searchParams.set('code_challenge_method', 'S256');\n session.codeVerifier = codeVerifier;\n }\n\n return { redirectUrl: url.toString(), session };\n },\n\n async authenticate(input: unknown): Promise<IdentityResult> {\n const bundle = asBundle(input);\n\n // The IdP redirected back with an explicit error (consent denied, etc.).\n // Never attempt a token exchange.\n if (bundle.error) {\n return { ok: false, reason: 'invalid' };\n }\n if (!bundle.code || !bundle.redirectUri) {\n return { ok: false, reason: 'invalid' };\n }\n\n const { token_endpoint } = await discover();\n\n // Exchange the authorization code for tokens.\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code: bundle.code,\n redirect_uri: bundle.redirectUri,\n client_id: config.clientId,\n client_secret: secret,\n });\n if (config.pkce && bundle.session?.codeVerifier) {\n body.set('code_verifier', bundle.session.codeVerifier);\n }\n\n const tokenRes = await doFetch(token_endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },\n body: body.toString(),\n });\n if (!tokenRes.ok) {\n // 4xx/5xx from the token endpoint is a genuine infra/protocol error —\n // surface it so the route returns a 500-class redirect, not a silent\n // \"invalid credentials\". Status only; never log the response body.\n throw new Error(`oidc \"${config.id}\": token endpoint error (${tokenRes.status})`);\n }\n\n const tokenJson = (await tokenRes.json()) as { id_token?: string };\n const idToken = tokenJson.id_token;\n if (!idToken) {\n return { ok: false, reason: 'invalid' };\n }\n\n let payload: JWTPayload;\n try {\n payload = await verifyIdToken(idToken);\n } catch {\n // Signature / alg / iss / aud / exp failures all collapse to a coarse\n // not-ok (CD-8). No token material is logged.\n return { ok: false, reason: 'invalid' };\n }\n\n // Nonce binding: the token's nonce must echo what we minted at /start.\n const expectedNonce = bundle.session?.nonce;\n if (!expectedNonce || payload.nonce !== expectedNonce) {\n return { ok: false, reason: 'invalid' };\n }\n\n const email = typeof payload.email === 'string' ? payload.email : undefined;\n if (!email) {\n return { ok: false, reason: 'invalid' };\n }\n\n const emailVerified = payload.email_verified === true;\n if (!emailVerified && !config.allowUnverifiedEmail) {\n return { ok: false, reason: 'invalid' };\n }\n\n return {\n ok: true,\n email: canonicalizeEmail(email),\n externalId: typeof payload.sub === 'string' ? payload.sub : undefined,\n };\n },\n };\n\n return provider;\n}\n","/**\n * @module @kb-labs/gateway-auth/provider-registry\n *\n * Tiny registry for `IIdentityProvider` instances (ADR-0020, Phase 1.11).\n *\n * Two invariants enforced beyond what a raw Map would give us:\n * - Re-registering the same `id` throws — usually a bootstrap bug,\n * never intentional.\n * - `list()` returns only the public `{ id, kind }` shape so the\n * `GET /auth/providers` route cannot accidentally serialise an\n * `authenticate` function over the wire.\n */\n\nimport type { IIdentityProvider } from '@kb-labs/core-contracts';\n\nexport interface ProviderInfo {\n id: string;\n kind: IIdentityProvider['kind'];\n}\n\nexport class ProviderRegistry {\n private readonly providers = new Map<string, IIdentityProvider>();\n\n register(provider: IIdentityProvider): void {\n if (this.providers.has(provider.id)) {\n throw new Error(`provider-registry: id \"${provider.id}\" is already registered`);\n }\n this.providers.set(provider.id, provider);\n }\n\n get(id: string): IIdentityProvider | undefined {\n return this.providers.get(id);\n }\n\n has(id: string): boolean {\n return this.providers.has(id);\n }\n\n list(): ProviderInfo[] {\n return [...this.providers.values()].map(p => ({ id: p.id, kind: p.kind }));\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/provider-loader\n *\n * Config-driven loader for identity providers (ADR-0020, DD-3).\n *\n * This is the gateway-side analogue of the platform adapter loader\n * (`core/runtime/src/loader.ts` notifier sub-channels): a type→factory\n * map for the built-ins, plus dynamic `import()` of a package for any\n * non-builtin `type`. Two deliberate departures from the platform\n * loader:\n *\n * - **Fail-fast, not warn-skip.** A misconfigured *platform* adapter can\n * degrade gracefully; a misconfigured *auth door* must never boot\n * half-open. If a configured provider cannot be loaded, we throw.\n * - **Gateway-owned (Pattern B).** Providers need gateway-domain ports\n * (users/credentials) and the tenant context, which don't exist until\n * gateway bootstrap — well after platform assembly. So the loader lives\n * here, not in core.\n *\n * The built-in `email-password` provider is the zero-config default: an\n * empty/absent `providers` config registers it and nothing else. It has\n * no special status beyond being the one we ship — it goes through the\n * exact same factory path a third-party provider would.\n */\n\nimport type {\n IdentityProviderDeps,\n IdentityProviderFactory,\n} from '@kb-labs/core-contracts';\nimport { ProviderRegistry } from './provider-registry.js';\nimport { createEmailPasswordProvider } from './providers/email-password.js';\nimport { createOidcProvider } from './providers/oidc.js';\n\n/**\n * A single provider entry from `auth.providers` config. `type` is the\n * discriminator (built-in name or package name); everything else is\n * provider-specific and validated by the provider's own zod schema.\n */\nexport interface ProviderConfigEntry {\n type: string;\n [key: string]: unknown;\n}\n\nexport type ProvidersConfig = Record<string, ProviderConfigEntry> | undefined;\n\n/**\n * Thin adapter from the `(config, deps)` factory shape to the existing\n * `createEmailPasswordProvider(opts)`. The provider only reads the narrow\n * ports, so deps map straight through (DD-3).\n */\nconst emailPasswordFactory: IdentityProviderFactory = (_config, deps) =>\n createEmailPasswordProvider({\n users: deps.users,\n credentials: deps.credentials,\n tenantId: deps.tenantId,\n bcryptCost: deps.bcryptCost,\n });\n\n/**\n * Built-in factories keyed by `type`. Anything not here is resolved as a\n * package name. `oidc` is the generic OIDC redirect provider (Google / Okta /\n * Auth0 / Keycloak / Azure AD); it owns its own zod config schema.\n */\nexport const BUILTIN_FACTORIES: Record<string, IdentityProviderFactory> = {\n 'email-password': emailPasswordFactory,\n oidc: createOidcProvider,\n};\n\ninterface ExternalProviderModule {\n createIdentityProvider?: IdentityProviderFactory;\n default?: IdentityProviderFactory;\n}\n\nasync function resolveFactory(\n id: string,\n type: string,\n): Promise<IdentityProviderFactory> {\n const builtin = BUILTIN_FACTORIES[type];\n if (builtin) {\n return builtin;\n }\n\n // Non-builtin type → treat as a package name and import it (DD-3).\n // Any failure is fatal: a configured auth door that cannot load must\n // stop the boot rather than silently disappear.\n let mod: ExternalProviderModule;\n try {\n mod = (await import(type)) as ExternalProviderModule;\n } catch (err) {\n throw new Error(\n `identity provider \"${id}\": failed to load package \"${type}\": ${(err as Error).message}`,\n );\n }\n\n const factory = mod.createIdentityProvider ?? mod.default;\n if (typeof factory !== 'function') {\n throw new Error(\n `identity provider \"${id}\": package \"${type}\" exports neither ` +\n 'createIdentityProvider nor a default factory',\n );\n }\n return factory;\n}\n\n/**\n * Build a {@link ProviderRegistry} from the `auth.providers` config.\n *\n * @param config The `auth.providers` map (or undefined).\n * @param deps Shared dependencies handed to every provider factory.\n * @returns A populated registry. Throws (fail-fast) on any load error.\n */\nexport async function loadIdentityProviders(\n config: ProvidersConfig,\n deps: IdentityProviderDeps,\n): Promise<ProviderRegistry> {\n const registry = new ProviderRegistry();\n const entries = Object.entries(config ?? {});\n\n if (entries.length === 0) {\n // Zero-config default: the built-in email-password door (DD-3).\n const provider = await emailPasswordFactory(\n { type: 'email-password', id: 'email-password' },\n deps,\n );\n registry.register(provider);\n return registry;\n }\n\n for (const [id, entry] of entries) {\n const factory = await resolveFactory(id, entry.type);\n // Inject the instance id so providers (e.g. oidc) can identify\n // themselves and build per-instance callback URLs.\n const provider = await factory({ ...entry, id }, deps);\n registry.register(provider);\n }\n\n return registry;\n}\n","/**\n * @module @kb-labs/gateway-auth/oauth-state-store\n *\n * Per-attempt OAuth `state` storage (ADR-0020, DD-5).\n *\n * Between `GET /auth/oauth/:id/start` and the IdP callback we must hold a\n * short-lived binding from the opaque `state` token to the flow context\n * (which provider issued it, which tenant, where to return, and the\n * per-attempt secrets like nonce / PKCE verifier). This is a thin,\n * namespaced wrapper over `IKVStore` — a real abstraction, not a stub:\n *\n * - **Namespaced keys** (`oauth:state:{state}`) so it can share the same\n * KV as the rate limiter without collision.\n * - **TTL** so abandoned flows self-clean.\n * - **One-shot `consume`** (get → delete, gated on the delete result) so\n * a replayed callback cannot be processed twice. With a shared Redis KV\n * the gating on `delete()` is what provides the atomic single-winner\n * guarantee under concurrency.\n *\n * NOTE: in a multi-process / HA deployment this MUST be backed by a\n * shared KV (Redis); an in-memory KV is per-process and a callback may\n * land on a different worker than the one that issued the state. The\n * bootstrap warns when a redirect provider is configured against an\n * in-memory KV (see OAuth hardening, Step 4b).\n */\n\nimport type { IKVStore } from '@kb-labs/core-platform/adapters';\n\n/**\n * The flow context bound to a single `state` token. `session` carries the\n * provider's per-attempt secrets (OIDC nonce, PKCE verifier) verbatim and\n * is never logged.\n */\nexport interface OAuthStateRecord {\n /** The provider instance id that started this flow (mix-up guard). */\n providerId: string;\n /** The tenant the flow was started for (cross-tenant guard). */\n tenantId: string;\n /** Validated relative path to return to after success. */\n returnTo: string;\n /** Opaque per-attempt secrets the provider needs back at callback. */\n session?: Record<string, unknown>;\n /** Issue timestamp (ms). */\n createdAt: number;\n}\n\nexport interface OAuthStateStoreOptions {\n /** State TTL in milliseconds. Default 10 minutes. */\n ttlMs?: number;\n}\n\nconst DEFAULT_TTL_MS = 10 * 60 * 1000;\nconst KEY_PREFIX = 'oauth:state:';\n\nexport class OAuthStateStore {\n private readonly ttlMs: number;\n\n constructor(\n private readonly kv: IKVStore,\n opts: OAuthStateStoreOptions = {},\n ) {\n this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;\n }\n\n private key(state: string): string {\n return `${KEY_PREFIX}${state}`;\n }\n\n /** Persist a state record with the configured TTL. */\n async put(state: string, record: OAuthStateRecord): Promise<void> {\n await this.kv.set(this.key(state), record, { ttlMs: this.ttlMs });\n }\n\n /**\n * One-shot read. Returns the record to the first caller and deletes it;\n * concurrent / subsequent callers get `null`. The `delete` result gates\n * the return so exactly one caller wins even under a shared KV.\n */\n async consume(state: string): Promise<OAuthStateRecord | null> {\n const key = this.key(state);\n const record = await this.kv.get<OAuthStateRecord>(key);\n if (!record) {\n return null;\n }\n const removed = await this.kv.delete(key);\n if (!removed) {\n // Lost the race — another caller already consumed this state.\n return null;\n }\n return record;\n }\n}\n","/**\n * @module @kb-labs/gateway-auth/stub-pdp\n *\n * Stub `IPolicyDecisionPoint` (ADR-0020, Phase 1.12). Mock RBAC over\n * the memberships store: tenant-admin gets every canonical permission,\n * tenant-member gets none. Machine identities are temporarily allowed\n * everything (matches the existing machine-token behaviour pre-RBAC).\n *\n * This module exists so the real handlers can call `policy.check(...)`\n * and `enumeratePermissions(...)` from day one. The real RBAC + ReBAC\n * engine (ClickUp 869def338) replaces this implementation; callers do\n * not change.\n *\n * Closed-world: actions outside the canonical PERMISSIONS enum return\n * `allow: false` even for tenant-admin. This is deliberate — until we\n * have per-plugin permission registration, the central enum is the\n * full universe of known actions.\n */\n\nimport type {\n Identity,\n IPolicyDecisionPoint,\n PolicyDecision,\n Resource,\n PolicyContext,\n} from '@kb-labs/core-contracts';\nimport { PERMISSIONS } from '@kb-labs/core-contracts';\nimport type { MembershipsStore, GroupId } from './memberships-store.js';\n\nconst ALL_PERMISSIONS: ReadonlyArray<string> = Object.freeze(Object.values(PERMISSIONS));\nconst MACHINE_PERMISSIONS: ReadonlyArray<string> = Object.freeze([PERMISSIONS.MACHINE_REGISTER]);\n\nconst GROUP_PERMISSIONS: Readonly<Record<GroupId, ReadonlyArray<string>>> = Object.freeze({\n 'tenant-admin': ALL_PERMISSIONS,\n 'tenant-member': Object.freeze([]),\n});\n\nexport interface StubPDPOptions {\n memberships: MembershipsStore;\n}\n\nexport const createStubPDP = (opts: StubPDPOptions): IPolicyDecisionPoint => {\n const permissionsForUser = async (identity: Identity): Promise<ReadonlyArray<string>> => {\n const list = await opts.memberships.listByUser(identity.userId);\n const inTenant = list.find(m => m.tenantId === identity.tenantId);\n if (!inTenant) {return [];}\n return GROUP_PERMISSIONS[inTenant.groupId];\n };\n\n return {\n async check(\n identity: Identity,\n action: string,\n _resource?: Resource,\n _ctx?: PolicyContext,\n ): Promise<PolicyDecision> {\n if (identity.type === 'machine') {\n // Machine identities currently retain their pre-PDP permissions.\n // Will tighten when the real PDP lands.\n if ((MACHINE_PERMISSIONS as string[]).includes(action)) {\n return { allow: true };\n }\n // Machines still allowed for now — keeps gateway proxy flows intact.\n return { allow: true };\n }\n\n const perms = await permissionsForUser(identity);\n if (perms.length === 0) {\n return { allow: false, reason: 'no_membership' };\n }\n if (!perms.includes(action)) {\n return { allow: false, reason: 'permission_denied' };\n }\n return { allow: true };\n },\n\n async enumeratePermissions(identity: Identity): Promise<string[]> {\n if (identity.type === 'machine') {\n return [...MACHINE_PERMISSIONS];\n }\n const perms = await permissionsForUser(identity);\n return [...perms];\n },\n };\n};\n","/**\n * @module @kb-labs/gateway-auth/bootstrap-admin\n *\n * First-run admin provisioning (ADR-0020, Phase 1.14).\n *\n * On a fresh deployment the operator can't log in unless someone has\n * already been provisioned — and we deliberately removed the public\n * `/auth/register` for humans. `ensureBootstrapAdmin` reads bootstrap\n * config from env and creates a single tenant-admin User on first\n * start. Idempotent: subsequent runs see the existing User and do\n * nothing.\n *\n * Conflict handling is conservative: if a User with the configured\n * email already exists in the configured tenant but in a different\n * status (e.g. someone manually `disabled` them and forgot the env\n * was still set), we log a warning and **do not touch** the existing\n * record. Silently re-activating or re-passwording is the kind of\n * surprise we want to avoid on a production system.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport bcrypt from 'bcryptjs';\nimport type { UsersStore } from './users-store.js';\nimport type { CredentialsStore } from './credentials-store.js';\nimport type { MembershipsStore } from './memberships-store.js';\n\nexport interface BootstrapConfig {\n adminEmail: string;\n adminPassword: string;\n tenantId: string;\n}\n\nexport interface BootstrapAdminLogger {\n warn(...args: unknown[]): void;\n info(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport interface EnsureBootstrapAdminOptions {\n bootstrap: BootstrapConfig | undefined;\n users: UsersStore;\n credentials: CredentialsStore;\n memberships: MembershipsStore;\n bcryptCost: number;\n logger: BootstrapAdminLogger;\n}\n\nconst isNonEmptyString = (v: unknown): v is string => typeof v === 'string' && v.length > 0;\n\nexport const ensureBootstrapAdmin = async (\n opts: EnsureBootstrapAdminOptions,\n): Promise<void> => {\n const { bootstrap, users, credentials, memberships, bcryptCost, logger } = opts;\n\n if (!bootstrap) {\n logger.info('bootstrap-admin: no bootstrap config provided, skipping');\n return;\n }\n\n if (\n !isNonEmptyString(bootstrap.adminEmail) ||\n !isNonEmptyString(bootstrap.adminPassword) ||\n !isNonEmptyString(bootstrap.tenantId)\n ) {\n throw new Error(\n 'bootstrap-admin: incomplete bootstrap config (need adminEmail, adminPassword, tenantId)',\n );\n }\n\n const existing = await users.findByEmailTenant(bootstrap.adminEmail, bootstrap.tenantId);\n\n if (existing) {\n if (existing.status === 'active') {\n logger.info('bootstrap-admin: admin already exists and is active, skipping');\n return;\n }\n logger.warn(\n 'bootstrap-admin: a user with the bootstrap email already exists in a non-active state; not touching it',\n { userId: existing.userId, status: existing.status, tenantId: existing.tenantId },\n );\n return;\n }\n\n const userId = randomUUID();\n await users.create({\n userId,\n tenantId: bootstrap.tenantId,\n email: bootstrap.adminEmail,\n status: 'active',\n });\n\n const hash = await bcrypt.hash(bootstrap.adminPassword, bcryptCost);\n await credentials.setCredential({\n userId,\n providerId: 'email-password',\n hash,\n });\n\n await memberships.addMembership({\n userId,\n tenantId: bootstrap.tenantId,\n groupId: 'tenant-admin',\n });\n\n logger.info('bootstrap-admin: provisioned tenant-admin', {\n userId,\n tenantId: bootstrap.tenantId,\n });\n};\n","/**\n * @module @kb-labs/gateway-auth/bootstrap-cli-credentials\n *\n * First-run CLI credential provisioning (#271).\n *\n * Non-local (`--yes`/server-mode) installs run with auth enabled but seed no\n * machine credential — `kb auth login` needs a client-id/secret nobody has,\n * and `/auth/register` is gated behind MACHINE_REGISTER which requires an\n * already-authenticated admin. `ensureBootstrapCliCredentials` closes that\n * gap by minting a fixed-handle machine service account in-process (no HTTP\n * round-trip, so the MACHINE_REGISTER gate never applies) and writing the\n * resulting JWT pair to disk in the same shape the CLI's own\n * `CredentialsManager` (cli/runtime/src/gateway/credentials.ts) expects.\n *\n * Idempotent: skipped entirely once the credentials file exists. If the file\n * is missing but the reserved handle is already registered (e.g. the file was\n * deleted after a previous successful run), this logs a warning and does\n * nothing further — same conservative \"never touch existing state, never\n * guess\" stance as ensureBootstrapAdmin.\n *\n * Deliberately does not import cli/runtime (a CLI-layer package) — a server\n * process depending on it would invert the dependency graph. The on-disk\n * shape is duplicated here instead (a few lines).\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport type { AuthService } from './service.js';\n\n/** Reserved handle for the auto-provisioned CLI bootstrap client. */\nexport const CLI_BOOTSTRAP_HANDLE = 'cli-bootstrap';\n\nexport interface BootstrapCliCredentialsLogger {\n warn(...args: unknown[]): void;\n info(...args: unknown[]): void;\n error(...args: unknown[]): void;\n}\n\nexport interface EnsureBootstrapCliCredentialsOptions {\n /** Gate — set from config.auth.bootstrap.provisionCliCredentials. */\n enabled: boolean;\n authService: AuthService;\n /** Written into the credentials file so the CLI knows where to talk to. */\n gatewayUrl: string;\n /** Defaults to ~/.kb/credentials.json. Overridable for tests. */\n credentialsPath?: string;\n logger: BootstrapCliCredentialsLogger;\n}\n\nconst defaultCredentialsPath = (): string =>\n path.join(os.homedir(), '.kb', 'credentials.json');\n\nconst fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const ensureBootstrapCliCredentials = async (\n opts: EnsureBootstrapCliCredentialsOptions,\n): Promise<void> => {\n const { enabled, authService, gatewayUrl, logger } = opts;\n const credentialsPath = opts.credentialsPath ?? defaultCredentialsPath();\n\n if (!enabled) {\n logger.info('bootstrap-cli-credentials: not enabled, skipping');\n return;\n }\n\n if (await fileExists(credentialsPath)) {\n logger.info('bootstrap-cli-credentials: already provisioned, skipping', { credentialsPath });\n return;\n }\n\n let registered: Awaited<ReturnType<AuthService['register']>>;\n try {\n registered = await authService.register({\n name: 'CLI Bootstrap',\n handle: CLI_BOOTSTRAP_HANDLE,\n capabilities: [],\n });\n } catch (err) {\n if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'HANDLE_TAKEN') {\n logger.warn(\n 'bootstrap-cli-credentials: reserved handle already registered but credentials file is missing; not auto-recovering (client secret is not retrievable) — run `kb auth register` manually',\n { handle: CLI_BOOTSTRAP_HANDLE },\n );\n return;\n }\n throw err;\n }\n\n const tokens = await authService.issueTokens(registered.clientId, registered.clientSecret);\n if (!tokens) {\n logger.warn('bootstrap-cli-credentials: token issuance failed right after registration');\n return;\n }\n\n const credentials = {\n gatewayUrl,\n accessToken: tokens.accessToken,\n refreshToken: tokens.refreshToken,\n expiresAt: Date.now() + tokens.expiresIn * 1000,\n };\n\n await fs.mkdir(path.dirname(credentialsPath), { recursive: true });\n await fs.writeFile(credentialsPath, JSON.stringify(credentials, null, 2), 'utf-8');\n await fs.chmod(credentialsPath, 0o600);\n\n logger.info('bootstrap-cli-credentials: provisioned CLI bootstrap credential', {\n credentialsPath,\n hostId: registered.hostId,\n });\n};\n","/**\n * @module @kb-labs/gateway-auth/user-auth-service\n *\n * Orchestration layer for the user authentication flow (ADR-0020,\n * Phase 1.15).\n *\n * Pulls together every store + provider + jwt + policy and exposes the\n * five operations the HTTP layer calls:\n *\n * - `login`\n * - `refresh` (CD-1 enforced here — disabled user revokes family)\n * - `logout`\n * - `changePassword` (revokes other families, keeps current)\n * - `activate` (consumes invite, auto-logs in)\n *\n * All not-ok paths throw `AuthError(code)`. The HTTP layer collapses\n * most codes to opaque `invalid_credentials` for the caller; `code` is\n * preserved for logs and tests.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport bcrypt from 'bcryptjs';\nimport type { UsersStore, User } from './users-store.js';\nimport { canonicalizeEmail } from './users-store.js';\nimport type { CredentialsStore } from './credentials-store.js';\nimport type { MembershipsStore } from './memberships-store.js';\nimport type { InvitesStore } from './invites-store.js';\nimport type {\n SessionsStore} from './sessions-store.js';\nimport {\n RefreshExpiredError,\n RefreshNotFoundError,\n RefreshReuseDetectedError,\n} from './sessions-store.js';\nimport type { ProviderRegistry } from './provider-registry.js';\nimport type { PasswordPolicy, ValidationResult } from './password-policy.js';\nimport {\n type JwtConfig,\n signUserAccessToken,\n signUserRefreshToken,\n verifyUserRefreshToken,\n} from './jwt.js';\nimport { issueCsrfToken } from './csrf.js';\n\nexport type AuthErrorCode =\n | 'invalid_credentials'\n | 'unknown_provider'\n | 'invalid_refresh'\n | 'refresh_reuse'\n | 'user_disabled'\n | 'invalid_current_password'\n | 'weak_password'\n | 'invalid_invite'\n | 'unknown_invite';\n\nexport class AuthError extends Error {\n public override readonly name = 'AuthError';\n constructor(public readonly code: AuthErrorCode, public readonly reason?: string) {\n super(code);\n }\n}\n\nexport interface UserAuthServiceOptions {\n users: UsersStore;\n credentials: CredentialsStore;\n memberships: MembershipsStore;\n invites: InvitesStore;\n sessions: SessionsStore;\n providers: ProviderRegistry;\n passwordPolicy: PasswordPolicy;\n jwtConfig: JwtConfig;\n accessTtlSec: number;\n refreshTtlSec: number;\n bcryptCost: number;\n /** Override for tests. Defaults to Date.now. */\n now?: () => number;\n}\n\nexport interface PublicUser {\n userId: string;\n email: string;\n tenantId: string;\n}\n\nexport interface TokenInfo {\n token: string;\n expiresInSec: number;\n}\n\nexport interface SessionResult {\n user: PublicUser;\n access: TokenInfo;\n refresh: TokenInfo;\n csrf: string;\n familyId: string;\n}\n\nexport interface LoginInput {\n providerId: string;\n input: unknown;\n}\n\nexport interface DeviceCtx {\n userAgent?: string;\n ip?: string;\n}\n\nexport interface ChangePasswordInput {\n userId: string;\n currentFamilyId: string;\n currentPassword: string;\n newPassword: string;\n}\n\nexport interface ActivateInput {\n activationToken: string;\n password: string;\n deviceCtx: DeviceCtx;\n}\n\nconst toPublicUser = (u: User): PublicUser => ({\n userId: u.userId,\n email: u.email,\n tenantId: u.tenantId,\n});\n\nconst mapPolicyResultToError = (r: ValidationResult): AuthError | null => {\n if (r.ok) {return null;}\n return new AuthError('weak_password', r.reason);\n};\n\nexport const createUserAuthService = (opts: UserAuthServiceOptions) => {\n const now = opts.now ?? Date.now;\n\n /**\n * Build the (access, refresh, csrf) triple. The caller passes the\n * `refreshJti` that sessions-store already minted so JWT and store\n * agree on the same identifier. Access has its own independent jti.\n */\n const issueSessionTokens = async (\n userId: string,\n tenantId: string,\n familyId: string,\n refreshJti: string,\n ): Promise<{ access: TokenInfo; refresh: TokenInfo; csrf: string }> => {\n const access = await signUserAccessToken(\n { userId, tenantId, familyId, ttlSec: opts.accessTtlSec },\n opts.jwtConfig,\n );\n const refresh = await signUserRefreshToken(\n { userId, tenantId, familyId, ttlSec: opts.refreshTtlSec, jti: refreshJti },\n opts.jwtConfig,\n );\n return {\n access: { token: access.token, expiresInSec: opts.accessTtlSec },\n refresh: { token: refresh, expiresInSec: opts.refreshTtlSec },\n csrf: issueCsrfToken(),\n };\n };\n\n // ── login ─────────────────────────────────────────────────────────\n const login = async (\n input: LoginInput,\n tenantId: string,\n deviceCtx: DeviceCtx,\n ): Promise<SessionResult> => {\n const provider = opts.providers.get(input.providerId);\n if (!provider) {\n throw new AuthError('unknown_provider');\n }\n\n const result = await provider.authenticate(input.input);\n if (!result.ok) {\n throw new AuthError('invalid_credentials');\n }\n\n // Provider says ok — pin to a User in OUR tenant. Provider has\n // already checked status=active for the local provider; remote\n // IdPs in the future will not, so we re-check here.\n const user = await opts.users.findByEmailTenant(canonicalizeEmail(result.email), tenantId);\n if (!user || user.status !== 'active') {\n throw new AuthError('invalid_credentials');\n }\n\n const sess = await opts.sessions.createSession({\n userId: user.userId,\n tenantId,\n deviceCtx,\n });\n\n const tokens = await issueSessionTokens(user.userId, tenantId, sess.familyId, sess.refreshJti);\n return { user: toPublicUser(user), ...tokens, familyId: sess.familyId };\n };\n\n // ── refresh ───────────────────────────────────────────────────────\n const refresh = async (refreshToken: string): Promise<SessionResult> => {\n const payload = await verifyUserRefreshToken(refreshToken, opts.jwtConfig);\n if (!payload) {\n throw new AuthError('invalid_refresh');\n }\n\n // CD-1: middleware checks user.status on every request, but the\n // refresh endpoint is public (no access cookie to inspect). Check\n // again here so a disabled user cannot extend their session via a\n // still-live refresh cookie.\n const user = await opts.users.getById(payload.userId);\n if (!user || user.status !== 'active') {\n // Revoke whatever family they presented so the dead refresh is\n // also gone from the store.\n await opts.sessions.revokeFamily(payload.familyId);\n throw new AuthError('user_disabled');\n }\n\n let rotated;\n try {\n rotated = await opts.sessions.rotateRefresh(payload.jti);\n } catch (err) {\n if (err instanceof RefreshReuseDetectedError) {throw new AuthError('refresh_reuse');}\n if (err instanceof RefreshExpiredError) {throw new AuthError('invalid_refresh');}\n if (err instanceof RefreshNotFoundError) {throw new AuthError('invalid_refresh');}\n throw err;\n }\n\n const tokens = await issueSessionTokens(user.userId, user.tenantId, rotated.familyId, rotated.newJti);\n return { user: toPublicUser(user), ...tokens, familyId: rotated.familyId };\n };\n\n // ── logout ────────────────────────────────────────────────────────\n const logout = async (refreshToken: string): Promise<void> => {\n const payload = await verifyUserRefreshToken(refreshToken, opts.jwtConfig);\n if (!payload) {return;} // idempotent for garbage tokens\n await opts.sessions.revokeFamily(payload.familyId);\n };\n\n // ── changePassword ────────────────────────────────────────────────\n const changePassword = async (input: ChangePasswordInput): Promise<void> => {\n const user = await opts.users.getById(input.userId);\n if (!user || user.status !== 'active') {\n // Treat as a credentials problem from the caller's POV — they\n // shouldn't be reaching this endpoint as a disabled user anyway.\n throw new AuthError('invalid_current_password');\n }\n\n const cred = await opts.credentials.getCredential(user.userId, 'email-password');\n if (!cred) {\n throw new AuthError('invalid_current_password');\n }\n const ok = await bcrypt.compare(input.currentPassword, cred.hash);\n if (!ok) {throw new AuthError('invalid_current_password');}\n\n const policyResult = await opts.passwordPolicy.validate(input.newPassword);\n const polErr = mapPolicyResultToError(policyResult);\n if (polErr) {throw polErr;}\n\n const hash = await bcrypt.hash(input.newPassword, opts.bcryptCost);\n await opts.credentials.setCredential({\n userId: user.userId,\n providerId: 'email-password',\n hash,\n });\n\n // Kill every other device — the security expectation around a\n // password change is \"all my other sessions are gone now\".\n await opts.sessions.revokeAllUserSessionsExcept(user.userId, input.currentFamilyId);\n };\n\n // ── activate ──────────────────────────────────────────────────────\n const activate = async (input: ActivateInput): Promise<SessionResult> => {\n const tokenResult = await opts.invites.findByToken(input.activationToken);\n if (tokenResult.kind === 'not_found') {throw new AuthError('unknown_invite');}\n if (tokenResult.kind === 'invalid') {throw new AuthError('invalid_invite');}\n const invite = tokenResult.invite;\n\n const policyResult = await opts.passwordPolicy.validate(input.password);\n const polErr = mapPolicyResultToError(policyResult);\n if (polErr) {throw polErr;}\n\n // Consume the invite ATOMICALLY before creating any account records.\n // findByToken → create is a TOCTOU window: two parallel activations of\n // the same valid token both pass findByToken, and without an atomic gate\n // both would call users.create() — one succeeds, the other throws on the\n // unique (tenantId, email) index, leaving an orphaned credential/session.\n // consume() flips status active→used in a single compare-and-set; only the\n // winner gets `true`, the loser gets `false` and bails out cleanly. Done\n // AFTER the password-policy check so a weak password never burns the invite.\n const consumed = await opts.invites.consume(invite.inviteId);\n if (!consumed) {\n // Lost the race (or invite was used/revoked between findByToken and now).\n throw new AuthError('invalid_invite');\n }\n\n // Create the user, credential, and membership. The invite is already\n // consumed, so this path runs exactly once per token.\n const userId = randomUUID();\n await opts.users.create({\n userId,\n tenantId: invite.tenantId,\n email: invite.email,\n status: 'active',\n });\n const hash = await bcrypt.hash(input.password, opts.bcryptCost);\n await opts.credentials.setCredential({\n userId,\n providerId: 'email-password',\n hash,\n });\n await opts.memberships.addMembership({\n userId,\n tenantId: invite.tenantId,\n groupId: invite.groupId,\n });\n // Note: the invite was already consumed atomically above, before account\n // creation — do not consume again here.\n\n const sess = await opts.sessions.createSession({\n userId,\n tenantId: invite.tenantId,\n deviceCtx: input.deviceCtx,\n });\n\n const tokens = await issueSessionTokens(userId, invite.tenantId, sess.familyId, sess.refreshJti);\n return {\n user: { userId, email: invite.email, tenantId: invite.tenantId },\n ...tokens,\n familyId: sess.familyId,\n };\n };\n\n void now; // Reserved for future audit-event timestamps; currently unused.\n\n return { login, refresh, logout, changePassword, activate };\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kb-labs/gateway-auth",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.105.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -13,36 +13,36 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"dist"
|
|
15
15
|
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"clean": "rimraf dist",
|
|
19
|
+
"dev": "tsup --watch",
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"lint:fix": "eslint . --fix",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"type-check": "tsc --noEmit"
|
|
25
|
+
},
|
|
16
26
|
"dependencies": {
|
|
27
|
+
"@kb-labs/core-contracts": "^2.105.0",
|
|
28
|
+
"@kb-labs/core-platform": "^2.105.0",
|
|
29
|
+
"@kb-labs/gateway-contracts": "^2.105.0",
|
|
17
30
|
"bcryptjs": "^2.4.3",
|
|
18
31
|
"jose": "^5.9.6",
|
|
19
|
-
"zod": "^3.25.76"
|
|
20
|
-
"@kb-labs/core-platform": "2.103.0",
|
|
21
|
-
"@kb-labs/gateway-contracts": "2.103.0",
|
|
22
|
-
"@kb-labs/core-contracts": "2.103.0"
|
|
32
|
+
"zod": "^3.25.76"
|
|
23
33
|
},
|
|
24
34
|
"devDependencies": {
|
|
35
|
+
"@kb-labs/devkit": "workspace:*",
|
|
36
|
+
"@kb-labs/sdk": "workspace:*",
|
|
25
37
|
"@types/bcryptjs": "^2.4.6",
|
|
26
38
|
"@types/node": "^24.3.3",
|
|
27
39
|
"tsup": "^8.5.0",
|
|
28
40
|
"typescript": "^5",
|
|
29
|
-
"vitest": "^3.2.6"
|
|
30
|
-
"@kb-labs/devkit": "2.103.0",
|
|
31
|
-
"@kb-labs/sdk": "2.22.0"
|
|
41
|
+
"vitest": "^3.2.6"
|
|
32
42
|
},
|
|
33
43
|
"engines": {
|
|
34
44
|
"node": ">=20.0.0",
|
|
35
45
|
"pnpm": ">=9.0.0"
|
|
36
46
|
},
|
|
37
|
-
"sideEffects": false
|
|
38
|
-
|
|
39
|
-
"build": "tsup",
|
|
40
|
-
"clean": "rimraf dist",
|
|
41
|
-
"dev": "tsup --watch",
|
|
42
|
-
"lint": "eslint .",
|
|
43
|
-
"lint:fix": "eslint . --fix",
|
|
44
|
-
"test": "vitest run",
|
|
45
|
-
"test:watch": "vitest",
|
|
46
|
-
"type-check": "tsc --noEmit"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
47
|
+
"sideEffects": false
|
|
48
|
+
}
|