[PR #8485] feat: add CIBA plugin for backchannel authentication #24907

Open
opened 2026-04-15 22:37:32 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/8485
Author: @gustavovalverde
Created: 3/7/2026
Status: 🔄 Open

Base: next ← Head: feat/ciba-plugin


📝 Commits (4)

  • 78bb334 feat(core): add plugin extension registry and dependency validation
  • 9ceb4c0 feat(oauth-provider): extensible grant types, metadata, and token claims
  • c0d5aaf feat(ciba): add backchannel authentication plugin
  • 5bf68bf feat(ciba): add buildAccessTokenClaims hook for custom token claims

📊 Changes

37 files changed (+4288 additions, -93 deletions)

View changed files

📝 .cspell/auth-terms.txt (+2 -0)
📝 docs/content/docs/concepts/plugins.mdx (+47 -0)
➕ docs/content/docs/guides/extending-plugins.mdx (+260 -0)
➕ docs/content/docs/plugins/ciba.mdx (+667 -0)
📝 docs/content/docs/plugins/index.mdx (+8 -0)
📝 docs/content/docs/plugins/oauth-provider.mdx (+19 -0)
📝 packages/better-auth/src/context/create-context.test.ts (+86 -0)
📝 packages/better-auth/src/context/create-context.ts (+8 -0)
📝 packages/better-auth/src/context/helpers.ts (+13 -0)
➕ packages/ciba/package.json (+82 -0)
➕ packages/ciba/src/approval.ts (+294 -0)
➕ packages/ciba/src/bc-authorize.ts (+379 -0)
➕ packages/ciba/src/ciba.test.ts (+809 -0)
➕ packages/ciba/src/client.ts (+14 -0)
➕ packages/ciba/src/error-codes.ts (+15 -0)
➕ packages/ciba/src/grant-handler.ts (+199 -0)
➕ packages/ciba/src/index.ts (+68 -0)
➕ packages/ciba/src/push.ts (+105 -0)
➕ packages/ciba/src/schema.ts (+82 -0)
➕ packages/ciba/src/types.ts (+99 -0)

...and 17 more files

📄 Description

Summary

Adds @better-auth/ciba, implementing OpenID Connect CIBA Core 1.0.

CIBA enables decoupled authentication: a client (AI agent, CLI tool, IoT device) initiates an auth request via a backchannel endpoint and the user approves on a separate device. No browser redirect needed.

CIBA importance

AI agents increasingly need to act on behalf of users but can't control a browser. CIBA solves this: the agent calls a backchannel endpoint, the user gets notified (email, push, SMS), approves or denies, and the agent receives tokens. This is the OpenID Foundation's answer to agentic authorization.

New package: @better-auth/ciba

Endpoints:

  • POST /oauth2/bc-authorize - Agent initiates a backchannel auth request with client credentials + user hint
  • GET /ciba/verify - Approval UI fetches request details (client name, scopes, binding message)
  • POST /ciba/authorize / POST /ciba/reject - Authenticated user approves or denies

Delivery modes: poll (agent polls token endpoint), ping (server notifies agent, then agent polls), push (server delivers tokens directly)

Features:

  • authorization_details (RFC 9396 RAR) for structured consent (e.g. purchase details, transfer amounts)
  • id_token_hint validation with JWKS asymmetric verification + HS256 fallback, audience binding
  • binding_message for human-readable context in the approval UI
  • requested_expiry for client-controlled request lifetimes
  • Resource indicators forwarded through the token pipeline
  • HTTPS enforcement on push/ping notification endpoints per CIBA §10.3 (loopback exempt for dev)
  • Rate limiting with slow_down and progressive interval increase
  • Replay protection: requests consumed on token issuance
  • Client re-verification before push token delivery
  • Client plugin with typed methods (ciba.verify(), ciba.authorize(), ciba.reject())

Changes to @better-auth/oauth-provider

  • Export createUserTokens, validateClientCredentials, getClient, basicToClientCredentials, checkResource for plugin reuse
  • Add customGrantTypeHandlers dispatch in the token endpoint's switch-default, so plugins can register custom grant types via init() without modifying core code
  • Widen grant_type Zod validation from enum to string for extensibility (dispatch still validates known types)
  • Add CIBA URN to grant_types arrays and backchannel metadata types to AuthorizationServerMetadata

Documentation

Full plugin docs at docs/content/docs/plugins/ciba.mdx with installation, usage examples, delivery modes, authorization details, endpoint reference, options, schema, error handling, and security considerations.

Usage

// Server
import { ciba } from "@better-auth/ciba";

export const auth = betterAuth({
  plugins: [
    jwt(),
    oauthProvider({ loginPage: "/sign-in", consentPage: "/consent" }),
    ciba({
      sendNotification: async (data) => {
        await sendEmail({
          to: lookupEmail(data.userId),
          subject: `Approve: ${data.bindingMessage}`,
          body: `Click to approve: ${data.approvalUrl}`,
        });
      },
    }),
  ],
});
// Agent
const { auth_req_id, interval } = await fetch("/api/auth/oauth2/bc-authorize", {
  method: "POST",
  headers: { Authorization: `Basic ${btoa(`${id}:${secret}`)}` },
  body: JSON.stringify({ scope: "openid", login_hint: "user@example.com" }),
}).then(r => r.json());

// Poll until approved

Summary by cubic

Adds @better-auth/ciba for OpenID Connect CIBA backchannel authentication and ships a typed Plugin Extension Protocol so plugins can extend @better-auth/oauth-provider with custom grants, discovery metadata, token claims, and auth methods. Also forwards OAuth Client Attestation headers to notifications, adds a hook for custom CIBA access token claims, and updates token handling and discovery.

  • New Features

    • CIBA endpoints: POST /oauth2/bc-authorize, GET /ciba/verify, POST /ciba/authorize, POST /ciba/reject
    • Delivery modes: poll, ping, push (with retries); HTTPS required for push/ping (localhost exempt); replay protection; slow_down backoff
    • Inputs: authorization_details (RAR), id_token_hint (JWKS/HS256) with audience check, binding_message, requested_expiry, optional agent_claims; forwards resource indicators and OAuth Client Attestation headers to sendNotification
    • Tokens: adds act: { sub: client_id }; echoes authorization_details; uses auth_req_id as referenceId; new buildAccessTokenClaims hook to inject custom claims using the full CIBA request
    • Verify and client: /ciba/verify returns client/scopes/message plus authorization_details and parsed agent_claims; client methods ciba.verify(), ciba.authorize(), ciba.reject(); export deliverPing; persistent cibaRequest model
  • Refactors

    • Core: Plugin Extension Protocol with extensions, dependencies (validated at startup), getExtensions(hostId); BetterAuthExtensionRegistry for extension contracts
    • @better-auth/oauth-provider: extension points for grant handlers, grant type URIs, discovery metadata, token claims, and token endpoint auth methods; token endpoint dispatches to extension handlers; grant_type and client grant_types now strings; discovery adds CIBA backchannel_* fields; redirect_uri no longer required in token exchange; exports helpers (createUserTokens, validateClientCredentials, getClient, basicToClientCredentials, checkResource, storeToken, getStoredToken) and types (GrantTypeHandler, OAuthProviderExtension, TokenClaimInfo)

Written for commit 5bf68bf861. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/8485 **Author:** [@gustavovalverde](https://github.com/gustavovalverde) **Created:** 3/7/2026 **Status:** 🔄 Open **Base:** `next` ← **Head:** `feat/ciba-plugin` --- ### 📝 Commits (4) - [`78bb334`](https://github.com/better-auth/better-auth/commit/78bb334f0a2ba80654c8f3d5cad5a3b7ffdf1a10) feat(core): add plugin extension registry and dependency validation - [`9ceb4c0`](https://github.com/better-auth/better-auth/commit/9ceb4c0246080f9262c7e45bdad5cd5e54f6195a) feat(oauth-provider): extensible grant types, metadata, and token claims - [`c0d5aaf`](https://github.com/better-auth/better-auth/commit/c0d5aaf1076adca34e289d63113dfe57bf3a87d5) feat(ciba): add backchannel authentication plugin - [`5bf68bf`](https://github.com/better-auth/better-auth/commit/5bf68bf8615b0fea0c5e6009fdf6cf5ef9f433db) feat(ciba): add buildAccessTokenClaims hook for custom token claims ### 📊 Changes **37 files changed** (+4288 additions, -93 deletions) <details> <summary>View changed files</summary> 📝 `.cspell/auth-terms.txt` (+2 -0) 📝 `docs/content/docs/concepts/plugins.mdx` (+47 -0) ➕ `docs/content/docs/guides/extending-plugins.mdx` (+260 -0) ➕ `docs/content/docs/plugins/ciba.mdx` (+667 -0) 📝 `docs/content/docs/plugins/index.mdx` (+8 -0) 📝 `docs/content/docs/plugins/oauth-provider.mdx` (+19 -0) 📝 `packages/better-auth/src/context/create-context.test.ts` (+86 -0) 📝 `packages/better-auth/src/context/create-context.ts` (+8 -0) 📝 `packages/better-auth/src/context/helpers.ts` (+13 -0) ➕ `packages/ciba/package.json` (+82 -0) ➕ `packages/ciba/src/approval.ts` (+294 -0) ➕ `packages/ciba/src/bc-authorize.ts` (+379 -0) ➕ `packages/ciba/src/ciba.test.ts` (+809 -0) ➕ `packages/ciba/src/client.ts` (+14 -0) ➕ `packages/ciba/src/error-codes.ts` (+15 -0) ➕ `packages/ciba/src/grant-handler.ts` (+199 -0) ➕ `packages/ciba/src/index.ts` (+68 -0) ➕ `packages/ciba/src/push.ts` (+105 -0) ➕ `packages/ciba/src/schema.ts` (+82 -0) ➕ `packages/ciba/src/types.ts` (+99 -0) _...and 17 more files_ </details> ### 📄 Description ## Summary Adds `@better-auth/ciba`, implementing [OpenID Connect CIBA Core 1.0](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html). CIBA enables decoupled authentication: a client (AI agent, CLI tool, IoT device) initiates an auth request via a backchannel endpoint and the user approves on a separate device. No browser redirect needed. ### CIBA importance AI agents increasingly need to act on behalf of users but can't control a browser. CIBA solves this: the agent calls a backchannel endpoint, the user gets notified (email, push, SMS), approves or denies, and the agent receives tokens. This is the OpenID Foundation's answer to agentic authorization. ### New package: `@better-auth/ciba` **Endpoints:** - `POST /oauth2/bc-authorize` - Agent initiates a backchannel auth request with client credentials + user hint - `GET /ciba/verify` - Approval UI fetches request details (client name, scopes, binding message) - `POST /ciba/authorize` / `POST /ciba/reject` - Authenticated user approves or denies **Delivery modes:** poll (agent polls token endpoint), ping (server notifies agent, then agent polls), push (server delivers tokens directly) **Features:** - `authorization_details` (RFC 9396 RAR) for structured consent (e.g. purchase details, transfer amounts) - `id_token_hint` validation with JWKS asymmetric verification + HS256 fallback, audience binding - `binding_message` for human-readable context in the approval UI - `requested_expiry` for client-controlled request lifetimes - Resource indicators forwarded through the token pipeline - HTTPS enforcement on push/ping notification endpoints per CIBA §10.3 (loopback exempt for dev) - Rate limiting with `slow_down` and progressive interval increase - Replay protection: requests consumed on token issuance - Client re-verification before push token delivery - Client plugin with typed methods (`ciba.verify()`, `ciba.authorize()`, `ciba.reject()`) ### Changes to `@better-auth/oauth-provider` - Export `createUserTokens`, `validateClientCredentials`, `getClient`, `basicToClientCredentials`, `checkResource` for plugin reuse - Add `customGrantTypeHandlers` dispatch in the token endpoint's switch-default, so plugins can register custom grant types via `init()` without modifying core code - Widen `grant_type` Zod validation from enum to string for extensibility (dispatch still validates known types) - Add CIBA URN to `grant_types` arrays and backchannel metadata types to `AuthorizationServerMetadata` ### Documentation Full plugin docs at `docs/content/docs/plugins/ciba.mdx` with installation, usage examples, delivery modes, authorization details, endpoint reference, options, schema, error handling, and security considerations. ### Usage ```ts // Server import { ciba } from "@better-auth/ciba"; export const auth = betterAuth({ plugins: [ jwt(), oauthProvider({ loginPage: "/sign-in", consentPage: "/consent" }), ciba({ sendNotification: async (data) => { await sendEmail({ to: lookupEmail(data.userId), subject: `Approve: ${data.bindingMessage}`, body: `Click to approve: ${data.approvalUrl}`, }); }, }), ], }); ``` ```ts // Agent const { auth_req_id, interval } = await fetch("/api/auth/oauth2/bc-authorize", { method: "POST", headers: { Authorization: `Basic ${btoa(`${id}:${secret}`)}` }, body: JSON.stringify({ scope: "openid", login_hint: "user@example.com" }), }).then(r => r.json()); // Poll until approved ``` <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds `@better-auth/ciba` for OpenID Connect CIBA backchannel authentication and ships a typed Plugin Extension Protocol so plugins can extend `@better-auth/oauth-provider` with custom grants, discovery metadata, token claims, and auth methods. Also forwards OAuth Client Attestation headers to notifications, adds a hook for custom CIBA access token claims, and updates token handling and discovery. - **New Features** - CIBA endpoints: POST `/oauth2/bc-authorize`, GET `/ciba/verify`, POST `/ciba/authorize`, POST `/ciba/reject` - Delivery modes: poll, ping, push (with retries); HTTPS required for push/ping (localhost exempt); replay protection; `slow_down` backoff - Inputs: `authorization_details` (RAR), `id_token_hint` (JWKS/HS256) with audience check, `binding_message`, `requested_expiry`, optional `agent_claims`; forwards resource indicators and OAuth Client Attestation headers to `sendNotification` - Tokens: adds `act: { sub: client_id }`; echoes `authorization_details`; uses `auth_req_id` as `referenceId`; new `buildAccessTokenClaims` hook to inject custom claims using the full CIBA request - Verify and client: `/ciba/verify` returns client/scopes/message plus `authorization_details` and parsed `agent_claims`; client methods `ciba.verify()`, `ciba.authorize()`, `ciba.reject()`; export `deliverPing`; persistent `cibaRequest` model - **Refactors** - Core: Plugin Extension Protocol with `extensions`, `dependencies` (validated at startup), `getExtensions(hostId)`; `BetterAuthExtensionRegistry` for extension contracts - `@better-auth/oauth-provider`: extension points for grant handlers, grant type URIs, discovery metadata, token claims, and token endpoint auth methods; token endpoint dispatches to extension handlers; `grant_type` and client `grant_types` now strings; discovery adds CIBA `backchannel_*` fields; `redirect_uri` no longer required in token exchange; exports helpers (`createUserTokens`, `validateClientCredentials`, `getClient`, `basicToClientCredentials`, `checkResource`, `storeToken`, `getStoredToken`) and types (`GrantTypeHandler`, `OAuthProviderExtension`, `TokenClaimInfo`) <sup>Written for commit 5bf68bf8615b0fea0c5e6009fdf6cf5ef9f433db. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 22:37:32 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#24907