Skip to content

feat: add magic link auth provider with code exchange#2649

Open
mroderick wants to merge 3 commits into
masterfrom
feature/magic-link-auth
Open

feat: add magic link auth provider with code exchange#2649
mroderick wants to merge 3 commits into
masterfrom
feature/magic-link-auth

Conversation

@mroderick

@mroderick mroderick commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

This adds an integration with our new auth app (auth.codebar.io), which allows users to sign up with a magic link as well as GitHub.

Initially, this will not be available through the UI, but on a new url: https://codebar.io/auth/codebar instead of https://codebar.io/auth/github

This allows us to iron out any kinks there might be, before we start relying on it.

Summary

Adds a custom OmniAuth strategy (:codebar) that delegates authentication to a separate auth app. The auth app becomes an identity provider — users authenticate via magic link or GitHub OAuth on the auth app, which issues a one-time code exchanged server-to-server for a signed JWT.

Commits

Commit Description
chore: add jwt gem for OIDC token verification Add jwt gem dependency for JWKS verification
feat: add codebar OmniAuth strategy with PKCE code exchange Configure autoload paths, register the provider, and implement the custom strategy (request_phase, callback_phase with PKCE, JWT verification, custom User-Agent)
spec: cover codebar OmniAuth strategy callback phase paths 14 tests for CSRF, missing code, PKCE, exchange errors, JWT verification, success path, and User-Agent header

Key Design Decisions

  • One-time code exchange over JWT in URL: JWT never appears in browser URL/history/logs. Delivered via opaque single-use code with 5-minute TTL.
  • RS256 via JWKS: No shared secret — planner verifies JWT signatures using auth app's public JWKS endpoint, whitelisted algorithms only.
  • PKCE: All flows require PKCE (S256), enforced by the auth app's OAuth client config.
  • State/nonce CSRF: Random nonce stored in Rails session, passed through auth app, verified on callback.
  • Custom User-Agent: The auth app sits behind Cloudflare which rejects User-Agent: Ruby (Net::HTTP default). All outgoing HTTP requests use Codebar Planner/1.0.
  • Phased coexistence: Both :github and :codebar providers coexist — existing auth controller unchanged.
  • JWKS cache: 15-minute TTL with automatic bust on unknown kid.
  • return fail!: All error paths use return fail!(...) instead of fail!(...); return so the Rack response propagates correctly.

Files

  • Gemfile / Gemfile.lock — Added jwt gem
  • config/application.rb — Exclude omniauth from autoload paths
  • config/initializers/omniauth.rb — Register :codebar provider with auth_url and audience
  • lib/omniauth/strategies/codebar.rb — Custom OmniAuth strategy (request_phase, callback_phase, PKCE code exchange, JWT verification with JWKS, custom User-Agent)
  • spec/lib/omniauth/strategies/codebar_spec.rb — 14 tests covering all callback phase paths

Testing

14 strategy tests + full existing suite (1061 examples, 0 failures).

@mroderick mroderick force-pushed the feature/magic-link-auth branch 7 times, most recently from 213613f to 7aca666 Compare June 22, 2026 10:50
@mroderick mroderick marked this pull request as ready for review June 22, 2026 11:31
@mroderick mroderick requested a review from olleolleolle June 22, 2026 11:33
@mroderick

Copy link
Copy Markdown
Collaborator Author

This is the related PR in auth: codebar/auth#32

@mroderick mroderick force-pushed the feature/magic-link-auth branch from 01f959e to 3798613 Compare June 24, 2026 10:20
Comment thread lib/omniauth/strategies/codebar.rb Outdated
@mroderick mroderick requested a review from olleolleolle June 24, 2026 10:33
Custom OmniAuth strategy that delegates authentication to the auth app (auth.codebar.io) via OAuth 2.1 / OIDC with PKCE.
Tests for CSRF detection, missing code, PKCE, token exchange failures, JWT verification, success path, and User-Agent header.
@mroderick mroderick force-pushed the feature/magic-link-auth branch from 59892fc to b4caba6 Compare June 25, 2026 14:48
@mroderick

mroderick commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

I have deployed both PRs for planner and auth to Heroku, in order to verify things in staging before we merge the PRs.

I also reviewed the PRs against OWASP top 10 for 2025, and fixed the few minor things that surfaced.

This unfortunately meant that I had to make some changes since I originally marked this as ready for review. I chose to rewrite the git history, in order to make the PRs easier to follow ... i.e. without any side quests. They should read fairly linearly now.


Testing on Heroku

To verify the OAuth 2.1 flow end-to-end:

Flow A: Sign in with GitHub

  1. Open https://codebar-staging.herokuapp.com/ — confirm the app loads
  2. Navigate to https://codebar-staging.herokuapp.com/auth/codebar << this is not linked in the UI, we'll only do that, once it is proven to work in production
  3. Observe you're redirected to https://auth.codebar.io/login?... with OAuth params
    (client_id=planner, response_type=code, code_challenge=..., state=...) in the URL
  4. Click "Sign In with GitHub" — authenticate with your GitHub account
  5. Observe you're redirected back to the staging app, signed in

Flow B: Sign in with Magic Link

  1. Clear all cookies for *.codebar.io and *.herokuapp.com to start fresh
  2. Navigate to https://codebar-staging.herokuapp.com/auth/codebar again
  3. Observe you're redirected to https://auth.codebar.io/login?...
  4. Enter your email address and click "Send Magic Link"
  5. Check your inbox for the magic link email from auth-noreply@codebar.io
  6. Click the link in the email
  7. Observe you're redirected back to the staging app, signed in

If something goes wrong

Check the staging app logs:

  heroku logs --tail -a codebar-staging

OAuth errors from the planner side show as (codebar) Authentication failure! <error_type>. The auth
app is already in production — let us know if you need access to its logs too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants