feat(server): device flow stubs (Axum + Python) + NestJS controllers#530
Closed
DorianZheng wants to merge 1 commit into
Closed
feat(server): device flow stubs (Axum + Python) + NestJS controllers#530DorianZheng wants to merge 1 commit into
DorianZheng wants to merge 1 commit into
Conversation
…trollers
Server-side scaffolding for the dual-bearer + device-flow contract.
Three independent stacks land in one PR because they share only the
OpenAPI surface and don't touch each other:
Axum reference server (boxlite serve, src/cli/src/commands/serve/):
- handlers/auth.rs device_code / token / revoke stubs that
auto-complete every poll. Lets the CLI flow be
exercised end-to-end against a local target
without standing up the NestJS gateway.
- handlers/me.rs GET /v1/me — fixed local-anonymous Principal.
- handlers/mod.rs register auth + me modules.
- mod.rs / types.rs routing + small wire-type cleanup.
Python reference server (openapi/reference-server/server.py):
- Mirrors the Axum stubs for the FastAPI-based reference target.
NestJS gateway (apps/api/src/boxlite-rest/):
- BoxliteMeController maps OrganizationAuthContext → PrincipalDto.
- BoxliteOAuthController returns 503 temporarily_unavailable on all
/v1/oauth/* until @node-oauth/node-oauth2-server
+ Postgres entities + consent UI land (backend
follow-up). Keeps the spec contract honoured
while signalling "not ready" to clients.
- dto/principal.dto.ts Principal DTO matching the OpenAPI schema.
- Deletes boxlite-auth.controller.ts — the old client_credentials grant
handler that never reached production.
Dashboard (apps/dashboard/src/pages/Onboarding.tsx):
- Minor onboarding copy update to mention the new API key path.
Wire protocol on feat/auth-single-bearer-impl.
SDK / CLI consumers on feat/auth-rest-credential + feat/auth-cli-device-flow.
This was referenced May 14, 2026
| @app.post("/v1/oauth/device_code") | ||
| async def device_code(request: Request): | ||
| """RFC 8628 §3.1 — start a device authorization flow. Auto-completes.""" | ||
| _body = await request.form() |
| @app.post("/v1/oauth/revoke") | ||
| async def oauth_revoke(request: Request): | ||
| """RFC 7009 §2.2 — always 200, idempotent.""" | ||
| _body = await request.form() |
4 tasks
DorianZheng
added a commit
that referenced
this pull request
May 15, 2026
PR #527 added /v1/oauth/device_code, /v1/oauth/token, /v1/oauth/revoke endpoints + their request/response schemas to the spec. On review, those spec entries turn out to be redundant and contract-drifting: Zero code consumers in the BoxLite tree read the OAuth schemas: - Python/Node SDKs expose only api_key (PyBoxliteRestOptions / JsBoxliteRestOptions). Go/C SDKs have no REST surface at all. - The Rust SDK's refresh_oauth() in src/boxlite/src/rest/client.rs posts hand-coded RFC 8628 forms — never reads the spec. - The CLI's commands::auth::device::login() hand-codes RFC 8628 forms. Industry alignment — none of these put OAuth endpoints in their spec: Stripe, GitHub, DigitalOcean, Anthropic, OpenAI. Only identity-provider products (Auth0, Okta) co-locate. Their business is auth; ours isn't. The wire format is fully defined by published IETF RFCs: - RFC 8628 §3.1 — device authorization request/response - RFC 6749 §4.4 / §6 — token exchange + refresh - RFC 7009 — token revocation Restating those in OpenAPI adds maintenance churn with no precision gain — implementers follow the RFC either way. Contract-drift fix: PR #530's BoxliteOAuthController in apps/api returns 503 temporarily_unavailable on every /v1/oauth/* route because the real OAuth server isn't built. With the endpoints out of the spec, the gateway no longer promises something it can't deliver. When the @node-oauth/node-oauth2-server backend lands, that work will add both the controller and the spec paths in one coherent commit. Spec changes: - Remove /v1/oauth/device_code, /v1/oauth/token, /v1/oauth/revoke paths - Remove DeviceAuthorizationRequest/Response, OAuthTokenRequest/Response, OAuthRevokeRequest, OAuthError schemas - Update info.description to point at the IETF RFCs for the wire format - BearerAuth.description: keep the four-token-source list; soften blo_/blr_ prefix descriptions to reference the RFCs not specific paths
4 tasks
Member
Author
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.
Summary
Server-side scaffolding for the bearer auth contract + RFC 8628 device flow. Three independent stacks land in one PR because they share only the OpenAPI surface and don't touch each other.
Axum reference server (
boxlite serve)src/cli/src/commands/serve/handlers/auth.rs—device_code/token/revokestubs that auto-complete every poll. Implements RFC 8628 server-side, lets the CLI flow be exercised end-to-end against a local target without standing up the NestJS gateway.src/cli/src/commands/serve/handlers/me.rs—GET /v1/mereturns a fixed local-anonymous Principal.src/cli/src/commands/serve/{mod,types}.rs,handlers/mod.rs— routing + wire-type cleanup.Python reference server (
openapi/reference-server/server.py)Mirrors the Axum stubs for the FastAPI-based reference target.
NestJS gateway (
apps/api/src/boxlite-rest/)BoxliteMeController— mapsOrganizationAuthContext→PrincipalDto.dto/principal.dto.ts— DTO matching the OpenAPI schema.BoxliteAuthController(theclient_credentialshandler, never reached production).No NestJS OAuth controller in this PR. The device-flow wire endpoints (
/v1/oauth/*) are not in the OpenAPI spec (dropped in #531 follow-up to #527), so the gateway doesn't need to scaffold them. When the real@node-oauth/node-oauth2-serverbackend lands, that work will add the controller + the spec paths together in one coherent commit. Until then, spec and gateway stay in sync.The local-dev path still works end-to-end: the Axum reference server's RFC 8628 stubs absorb the CLI's device-flow client, so
boxlite auth login --web --url http://localhost:8080is fully exercisable without the real backend.Dashboard
apps/dashboard/src/pages/Onboarding.tsx— minor copy update for the API key path.Stacked PRs
Independent of the SDK / CLI PRs. Base is
main.Test plan
cargo check -p boxlite-cli(Axum stubs compile)cd apps/api && pnpm build(gateway compiles without BoxliteOAuthController)boxlite serve --port 8080+boxlite auth login --web --url http://localhost:8080end-to-end against Axum stubspython openapi/reference-server/server.py+curl -X POST localhost:8000/v1/oauth/device_codeGET /v1/mereturns a valid Principal for an authenticated request