Repository navigation
[N10a] Route auth: jwt(), oidc(), basic() in an ordered list, principal per run - #307
Merged
Merged
Conversation
…al per run New subpath @lousho/build-ai-agent/auth (Web Crypto and fetch only): jwt(), oidc(), basic(), apiToken(), anonymous() and routeAuth(). The first entry that returns a Principal accepts; null skips; AuthError stops; everything skipping is a generic 401 with merged WWW-Authenticate challenges. createRouteHandler, serveFetch and createDeployedServer take the list; an agent directory's auth.ts guards the built node server. The principal reaches send()/stream()/session turns, RunConfigContext and MemoryScopeContext; Slack and Discord set the sender as principal. New error code LOUSHO_AUTH_CONFIG_INVALID; docs/auth.md. Closes #249 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…llow) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
N10a: route auth (
jwt(),oidc(),basic()) in an ordered list, principal per runCloses #249
What
@lousho/build-ai-agent/auth(src/auth/:index.ts,jwt.ts,oidc.ts,basic.ts,routeAuth.ts,types.ts,encoding.ts). It uses Web Crypto andfetchonly: nonode:import, no new dependency.package.jsonexports["./auth"]andtsup.config.ts'auth/index'.Principalis also exported from the root.routeAuth(request, list): the firstPrincipalwins,nullskips,AuthError(401|403)stops, any other throw is a logged 500 with a generic body, an empty list or all entries skipping gives a generic 401 with oneWWW-Authenticateper distinct challenge. Responses carrycache-control: no-store.createRouteHandler({ auth }),serveFetch(request, ctx, auth)andcreateDeployedServer(agent, { auth })take the list. The string and boolean-function forms behave as before. The node server appendsapiToken(LOUSHO_API_TOKEN). An agent directory'sauth.tsis loaded byresolveAgentDir()(auth,manifest.auth), bundled bynode-server-dir.ts, and passed by the generated server. The sdk runtime plugin resolves@lousho/build-ai-agent/authin bundles.SendOptions.principal, sessionsend()/stream(),SessionTurnCall,RunConfigContextandMemoryScopeContext. Slack and Discord set the sender as the principal.mountChannelspassesChannelInbound.principalalong.LOUSHO_AUTH_CONFIG_INVALID. In production,createRouteHandlerwithoutauthlogs a warning once.Security checks and their tests
node:importsrc/auth/routeAuth.test.ts"bundles for the browser platform ... no node: import"alg: nonerejected (any casing)jwt.test.ts"rejects alg 'none' in any casing"jwt.test.ts"rejects an alg outside the configured algorithms"; construction casesjwt.test.ts"refuses algorithm confusion"; construction cases (publicKey+HS256,jwksUrl+HS256); "never takes an HMAC key ... from a key set"jwt.test.ts"rejects a bad signature, and a token signed by another key"exp(required),nbf,iatwith a bounded skew (60 s default, 300 s max)jwt.test.ts"checks exp (required), nbf and iat with the clock tolerance"; construction caseclockToleranceSec: 3600iss/audjwt.test.ts"checks the issuer exactly and the audience";oidc.test.tsissuer mismatch and audiencebasic.test.ts"compares in constant time ..." and "accepts the bearer token ... in constant time"jwt.test.ts"fetches once, caches for 10 minutes, and refetches an unknown kid at most once per 30 s", "a failing endpoint is retried at most every 30 s", "concurrent first requests share one fetch";oidc.test.ts"retries a failed discovery at most every 30 s"jwt.test.ts"never fetches a key-set URL named by the token (jku, x5u, jwk ...)";oidc.test.tsdiscovery issuer mismatch never fetches the JWKSrouteAuth.test.ts"gives the same generic 401 whatever check failed"; "an unexpected throw is a 500 whose body does not carry the error"routeAuth.test.ts500-body testRemote sub-agents and remote evals against a server that uses an auth list:
remoteAgent.test.ts"works against a server whose auth is a list ... approvals included" and "adds the remote run's tokens behind an auth list too ..." (usage reporting, principalapi-token).remoteTarget.test.ts"passes against a server whose auth is a list".For the docs site (G9)
auth(docs/auth.md, "Route auth and principals"). It needs an English and an Arabic page, navigation indocs.jsonfor both languages, and an entry inPAGESinscripts/sync-sdk-docs.mjs. Headings:## The auth list,## Helpers(### jwt(),### oidc(),### basic(),### apiToken() and anonymous(),### Your own entry),## 401, 403 and WWW-Authenticate,## Where it runs,## createRouteHandler,## The node server and auth.ts,## Reading the principal in the run,## Security notes.## Auth(### LOUSHO_AUTH_CONFIG_INVALID), appended at the end oferrors.deployment(a paragraph under### Auth),nextjs(a paragraph under## Routes),memory(a paragraph under## Scopes),agent-directories(a layout line and the deploy paragraph),api-overview(a bullet),cloudflare-workers(a "Route auth" row in the limits table). The README docs table has a new row.Out of scope / notes
docs/auth.mdsays so in its Security notes. Tools, approval policies and paused static runs do not get the principal: that is N10b. The Cloudflare Worker target keeps the token only.telegramChannel()([N11a] telegramChannel(): Telegram bot webhooks, inline-keyboard approvals #302) andgithubChannel()([N11b] githubChannel(): the agent answers issue and pull-request comments #306) were merged while this was in progress and do not set a principal yet. Slack and Discord do. Each is a one-line follow-up.Verification (after merging origin/main at 996e4a0; then 5fe0b2f, a CHANGELOG-only tidy, after which docs:verify-snippets, docs:llms:check and the scripts tests were rerun)
npx tsc --noEmit: oknpm run lint: ok, 0 warningsnpm run build: ok.npm run build --workspace=packages/create-lousho-agent: oknpm run test:types: 68 passed, no type errorsnpm run docs:verify-snippets -- --skip-build: all 220 snippets type-check; 8 also runnpm run docs:llms:check: oknpm run test:coverage: 241 files passed, 1 skipped; 3517 tests passed, 6 skipped. An earlier run, before the second sync, hit a 5 s timeout inNodeWorkspace.test.ts"runs in the root by default" under machine load. That test passes alone and in every later run.npm run fallow: no issues, 0 above thresholdtypecheckok,typecheck:serverok,test119 passed,test:server130 passednpm run pack-smoke: all checks passed (esm 17/17 and cjs 17/17 entries load, including./auth)🤖 Generated with Claude Code