Commit Graph
47 Commits
Author SHA1 Message Date
Dan MilneandClaude Opus 4.8 7149b98b7b Add OAuth device flow, token introspection, and dynamic client registration
Adds three OAuth surfaces to the OIDC provider so CLIs, terminal agents, and
MCP connectors can authenticate as real users instead of using static API keys.

Device Authorization Grant (RFC 8628):
- OidcDeviceCode model (HMAC device_code, short plaintext user_code, nullable
  user until approval, slow_down polling state), mirroring OidcAuthorizationCode
- POST /oauth/device_authorization issues the code pair + verification URIs
- device_code grant on /oauth/token returns authorization_pending / slow_down /
  access_denied / expired_token, then the standard token triple; single-use
- Authenticated /device approval page, gated by Application#user_allowed?

Token Introspection (RFC 7662):
- POST /oauth/introspect: confidential-caller-authenticated; returns active,
  scope, and the user's groups so resource servers can authorize on membership

Dynamic Client Registration (RFC 7591):
- POST /oauth/register creates public/confidential clients (PKCE required)
- Runtime toggle via a new Setting store + admin switch on the Applications page;
  off by default, env var CLINCH_DCR_ENABLED as bootstrap fallback
- New clients are default-deny (no allowed_groups) until an admin grants access
- RFC 8414 metadata alias at /.well-known/oauth-authorization-server;
  registration_endpoint advertised only while the window is open

Discovery advertises all three grants/endpoints. Seeds add a clinch-cli public
client and a c2a2-introspection confidential client. ADRs in docs/decisions
record the opaque-vs-JWT, device-flow, and DCR-security decisions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F7cwhwDJp3MJJDoNPVE6zq
2026-07-19 12:30:02 +10:00
Dan MilneandClaude Opus 4.8 aa5736ddab Update gems and fix lint to clear CI failures
Bumps dependencies (jwt 3.2.0, puma 8.0.2, net-imap 0.6.4.1 and others
via bundle update) to resolve bundler-audit advisories, and applies
standardrb autofixes so the lint job passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:51:23 +10:00
Dan MilneandClaude Fable 5 f38ac2ecc8 Prevent TOTP code replay within the drift window
verify_totp called ROTP without `after:`, so a captured 6-digit code stayed
valid for the full ~90s drift window and could be replayed in a separate
sign-in. Add a last_otp_at column, pass it as ROTP's `after:`, and persist the
matched timestep on success so a code (or any earlier one) cannot be reused.

Also fixes a latent bug surfaced by the new replay path: enable_totp! did
`self.backup_codes = generate_backup_codes`, reassigning backup_codes to the
plaintext return value (generate_backup_codes already stores the BCrypt hashes
internally). That stored backup codes in plaintext and broke verification.
enable_totp! is test-only today, but it is public and backup_codes is not
encrypted, so this is a real footgun. Now it just calls generate_backup_codes.

Rewrites the mislabeled "TOTP code cannot be reused" test to actually assert
that replaying an accepted code is rejected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:10:34 +10:00
Dan MilneandClaude Opus 4.7 03dfdbd83a Default-deny access control with group flags and access enumeration
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Replaces the implicit "empty allowed_groups means public" rule with
explicit default-deny across both OIDC and ForwardAuth. Adds two boolean
flags on Group — auto_assign (Keycloak-style auto-join on user create)
and admin (members can reach the admin panel) — and drops the
users.admin column entirely. Adds "Users with access" and "Accessible
applications" panels with via-group badges on the application/user show
pages.

BEHAVIOR CHANGE: a ForwardAuth app with no allowed_groups previously
bypassed authentication entirely; it now returns 403 like any other
unauthorized request. The data migration seeds an "everyone" group and
attaches it to all previously group-less apps to preserve behavior on
existing installs. An "admins" group is seeded and backfilled from any
user with the old admin column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 15:53:27 +10:00
Dan MilneandClaude Opus 4 93d8381214 Nullify token to auth-code FK on delete so cleanup job can purge codes
The FK added in b7fa499 defaulted to ON DELETE RESTRICT, which means
OidcTokenCleanupJob#perform would fail when deleting auth codes older
than 7 days if any refresh token (whose expiry is days-to-weeks) still
referenced them. Switch both token FKs to ON DELETE SET NULL so token
rows survive the code deletion with a NULL FK, preserving the audit
trail the cleanup job deliberately keeps.

Add a regression test covering the exact scenario: a 10-day-old auth
code with a token still pointing at it -> cleanup deletes the code,
token survives, token FK is nulled.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-04-20 18:12:03 +10:00
Dan MilneandClaude Opus 4 b7fa49953c Revoke full token chain on OIDC authorization-code replay
The replay handler previously used a created_at time-range filter to
target access tokens and called update_all(expires_at:), which left
revoked_at nil, skipped refresh tokens entirely, and could miss or
falsely catch tokens from concurrent flows. Add an oidc_authorization_code
FK on both token tables, carry it through refresh-token rotation, and
use the association to revoke every descendant via revoke! (which sets
revoked_at and cascades access -> refresh).

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-04-20 17:39:08 +10:00
Dan MilneandClaude Opus 4.6 65c19fa732 Upgrade to Ruby 4.0.1, bump version to 0.9.0
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Replace CGI.parse (removed in Ruby 4.0) with Rack::Utils.parse_query
in application controller, sessions controller, and OIDC tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:52:11 +11:00
Dan MilneandClaude Opus 4.6 fd8785a43d Add API keys / bearer tokens for forward auth
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / scan_container (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Enables server-to-server authentication for forward auth applications
(e.g., video players accessing WebDAV) where browser cookies aren't
available. API keys use clk_ prefixed tokens stored as HMAC hashes.

Bearer token auth is checked before cookie auth in /api/verify.
Invalid tokens return 401 JSON (no redirect). Requests without
bearer tokens fall through to existing cookie flow unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:45:40 +11:00
Dan Milne 95d0d844e9 Add a method to remove parameters from urls, so we can redirect without risk of infinite redirect. Fix a bunch of redirects to login afer being foced to log out. Add missing migrations 2026-01-05 13:01:32 +11:00
Dan Milne 074a734c0c Accidentally added skip-consent to this branch 2026-01-05 12:01:04 +11:00
Dan Milne 4a48012a82 Add claims support 2026-01-05 12:00:29 +11:00
Dan Milne 93a0edb0a2 StandardRB fixes
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2026-01-01 13:29:44 +11:00
Dan Milne d036e25fef Add auth_time, acr and azp support for OIDC claims 2025-12-31 17:07:54 +11:00
Dan Milne fcdd2b6de7 Continue adding auth_time - need it in the refresh token too, so we can accurately create new access tokens. 2025-12-31 16:57:28 +11:00
Dan Milne 9d352ab8ec Fix tests - add missing files 2025-12-31 16:01:31 +11:00
Dan Milne 3db466f5a2 Switch Access / Refresh tokens / Auth Code from bcrypt ( and plain ) to hmac. BCrypt is for low entropy passwords and prevents dictionary attacks - HMAC is suitable for 256-bit random data.
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-12-31 15:48:32 +11:00
Dan Milne cc7beba9de PKCE is now default enabled. You can now create public / no-secret apps OIDC apps 2025-12-31 09:22:18 +11:00
Dan Milne 71d59e7367 Remove plain text token from everywhere
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-12-30 11:58:11 +11:00
Dan Milne 99c3ac905f Add a token prefix column, generate the token_prefix and the token_digest, removing the plaintext token from use. 2025-12-30 09:45:16 +11:00
Dan Milne 6be23c2c37 Add backchannel logout, per application logout.
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-27 16:38:27 +11:00
Dan Milne eb2d7379bf Backchannel complete - improve oidc credential display 2025-11-27 11:52:25 +11:00
Dan Milne 67d86e5835 Add Icons for apps 2025-11-25 19:11:22 +11:00
Dan Milne d6029556d3 Add OIDC fixes, add prefered_username, add application-user claims 2025-11-25 16:29:40 +11:00
Dan Milne 7796c38c08 Add pairwise SID with a UUIDv4, a significatant upgrade over User.id.to_s. Complete allowing admin to enforce TOTP per user
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-23 11:16:06 +11:00
Dan Milne e882a4d6d1 More complete oidc
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-18 20:03:03 +11:00
Dan Milne ab0085e9c9 More complete oidc 2025-11-18 20:02:45 +11:00
Dan Milne 4df2eee4d9 Bug fix for domain names with empty string instead of null. Form errors and some security fixes
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-09 12:22:41 +11:00
Dan Milne c92e69fa4a Add PCKE 2025-11-09 11:54:45 +11:00
Dan Milne 038801f34b Add pkce
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-09 10:21:29 +11:00
Dan Milne 044b9239d6 Ok - this time add the new controllers we stripped out of inline and add back the csp
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 18:55:20 +11:00
Dan Milne fb14ce032f Strip out more inline javascript code. Encrypt backup codes and treat the backup codes attribute as a json array 2025-11-04 18:46:11 +11:00
Dan Milne bf104a9983 Fix CSP errors - migrate inline JS to stimulus controllers. Add a URL for applications so users can discover them
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 17:06:53 +11:00
Dan Milne 57abc0b804 Add webauthn
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 16:20:11 +11:00
Dan Milne 19bfc21f11 Move sessions into their own view for easier management
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 15:19:39 +11:00
Dan Milne ef15db77f9 Massive refactor. Merge forward_auth into App, remove references to unimplemented OIDC federation and SAML features. Add group and user custom claims. Groups now allocate which apps a user can use
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-11-04 13:21:55 +11:00
Dan Milne cfab21b130 More tests 2025-10-28 08:27:19 +11:00
Dan Milne 8dd3e60071 Add a list_sign_in_at field for users so magick links work 2025-10-26 22:40:54 +11:00
Dan Milne 88428bfd97 Add configuration foward-auth headers 2025-10-26 14:41:20 +11:00
Dan Milne 0af3dbefed Remember that we concented.
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-24 17:01:03 +11:00
Dan Milne 8c80343b89 Add nonce to the auth codes
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-24 16:34:38 +11:00
Dan Milne 12e0ef66ed OIDC app creation with encrypted secrets and application roles
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-24 14:47:24 +11:00
Dan Milne fc9afcd1b7 Separate Forward auth into it's own models + controller
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-24 10:56:27 +11:00
Dan Milne ec2eb27da1 Add user admin 2025-10-23 21:13:50 +11:00
Dan Milne 7f075391c1 Switch user status to enum
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-23 20:24:19 +11:00
Dan Milne 256cbe3a48 User registation working. Sidebar built. Dashboard built. TOTP enable works - TOTP login works
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-23 18:07:27 +11:00
Dan Milne 56f7dd7b3c First crack
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-23 16:45:00 +11:00
Dan Milne 1ff0a95392 First commit
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2025-10-23 16:19:56 +11:00