BPlatform specification
Login
featureStandard

Design model

Auth hub issuer, OAuth flow, pairing lifecycle, handoff JWT claims, and hub admin bootstrap.

Document layout: Feature capability layoutconforms
  • ## Purpose
  • ## Requirements

Design model Specification

Purpose

Auth hub issuer, OAuth flow, pairing lifecycle, handoff JWT claims, and hub admin bootstrap.

Requirements

Requirement: Design model conformance status

This capability SHALL remain non-conformant and MUST NOT be cited as an implemented Beskid guarantee until a validated OpenSpec change adds explicit behavioral requirements.

Stable ID: BSP-REQ-500ABC752238

Scenario: Capability has descriptive material only

  • GIVEN the migrated sources contain no uppercase BCP-14 obligation or accepted ADR decision
  • WHEN an implementation reports Beskid conformance
  • THEN it MUST NOT claim conformance based on this capability

Informative Source Provenance

The records below preserve migration history and are not normative except where text was extracted into a requirement above.

Source Record: Design model

Authority: informative provenance
Legacy path: /platform-spec/tooling/auth-hub/design-model/
Source: site/spec-content/platform-spec/tooling/auth-hub/design-model/content.md
SHA-256: 27f732e97500a63a2c6bed33eb0e780aa759327a808bf6f185a47ac6347fda26

Migrated source text
## Issuer and packages

| Constant | Value | Package |
| --- | --- | --- |
| `AUTH_HUB_ISSUER` | `beskid-auth-hub` | `@beskid/auth-client` |
| `AUTH_API_VERSION` | `v1` | `@beskid/auth-client` |
| `AUTH_APP_IDS` | `tracker`, `nexus`, `pckg` | `@beskid/auth-client` |

Handoff JWTs **must** use `iss: beskid-auth-hub`, `alg: HS256`, and a signing key derived from the consumer's **service token** (32+ characters) issued at pairing time.

## Hub routes

| Path | Purpose |
| --- | --- |
| `/` | Service picker |
| `/login?app=<appId>` | Start GitHub OAuth for a consumer or hub session |
| `/callback` | GitHub OAuth callback → handoff redirect or hub session cookie |
| `/onboarding` | First-run GitHub OAuth app registration |
| `/admin`, `/admin/pairing` | Hub admins, service pairing codes |
| `/api/v1/github/*` | GitHub API proxy for paired apps (Bearer `hubUserToken`) |
| `/api/v1/pairing/*` | Pairing approve/status |
| `/api/v1/health` | Liveness for compose health checks |

## Sign-in flow

```mermaid
sequenceDiagram
  participant User
  participant Consumer as Consumer app
  participant Hub as Auth hub
  participant GitHub

  User->>Consumer: Protected route
  Consumer->>Hub: Redirect /login?app=tracker
  Hub->>GitHub: OAuth authorize
  GitHub->>Hub: /callback code
  Hub->>Hub: Issue handoff JWT (iss beskid-auth-hub)
  Hub->>Consumer: Redirect /api/auth/hub-finish?handoff=…
  Consumer->>Consumer: Verify JWT, create session
```

Consumers **must** preserve the post-login return path via a redirect cookie set before `/api/auth/github` and read after `/api/auth/hub-finish`.

## Pairing flow

1. Hub admin creates a pairing request (**Admin → Service pairing**) with `appId` and consumer `publicUrl`.
2. Hub returns a short-lived pairing code (24h TTL) and approve URL template: `{publicUrl}/settings/auth/pair?code=…`.
3. Consumer admin opens the approve URL (or calls the app-specific pair API). The consumer stores the returned **service token** locally (SQLite / config file).
4. Subsequent handoffs are verified with `verifyHandoffToken(serviceToken, handoff, expectedApp)`.

**Autopairing (Tracker):** when `TRACKER_PUBLIC_URL` matches the pairing request and `GITHUB_SYNC_TOKEN` or an admin session is available, `GET /settings/auth/pair?code=…` **may** complete approval server-side without manual code entry.

## Hub admin bootstrap

| Rule | Behavior |
| --- | --- |
| AH-01 | If no hub admins are configured, the **first successful GitHub OAuth sign-in** becomes hub admin |
| AH-02 | Admins manage additional logins at **Administration → Hub admins** or `GET/POST/DELETE /api/v1/admin/admins` |
| AH-03 | Re-onboarding when already configured **requires** `AUTH_SETUP_TOKEN` |

## GitHub OAuth app

Exactly **one** GitHub OAuth application is registered on the hub during `/onboarding`. Callback URL **must** be `{AUTH_HUB_PUBLIC_URL}/callback` with no trailing slash on the origin.

Consumers **must not** set `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, or `GITHUB_OAUTH_CALLBACK_URL`.

## Decisions
<!-- spec:generate:adr-index -->
No ADRs published under **`adr/`** yet.
<!-- /spec:generate:adr-index -->
## Articles
<!-- spec:generate:article-index -->
_No articles in this bundle yet._
<!-- /spec:generate:article-index -->