feat: connect planner via OAuth 2.1 / OIDC#32
Open
mroderick wants to merge 4 commits into
Open
Conversation
2c4d394 to
9826035
Compare
till
reviewed
Jun 19, 2026
f302a42 to
5ff85f2
Compare
Comment on lines
+26
to
+39
| smoke: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | ||
| with: | ||
| persist-credentials: false | ||
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | ||
| with: | ||
| node-version-file: ".nvmrc" | ||
| - run: npm ci | ||
| - run: | | ||
| npm run prettier:check | ||
| npm run lint | ||
| npm run fallow |
Install @better-auth/oauth-provider and configure Better Auth with: - jwt plugin: issues id_tokens with email/name claims for the planner - oauthProvider plugin: OAuth 2.1 authorize/token endpoints for the planner Add seedPlannerClient helper that inserts the first-party planner client via raw SQL (Better Auth has no public API for this without admin auth). Add a migration script for Heroku release phase that runs migrations and seeds the planner client. Add OAuth provider feature tests covering: - planner client seeded correctly (public, PKCE-required) - authorize redirects unauthenticated users to login - authorize issues code when authenticated - token endpoint exchanges code for access token with PKCE - token endpoint rejects missing/invalid PKCE, invalid code, mismatched redirect_uri
Replace the old redirect_url-based login flow with callbackURL that points to the OAuth provider's authorize endpoint. - Extract named route handlers (showLogin, showMagicLinkForm, sendMagicLink, startGitHubOAuth) for clarity - getCallbackURL preserves OAuth query params from the authorize request and falls back to a default authorize URL for direct login - GitHubButton and MagicLinkButton now POST/GET with callbackURL instead of using client-side JS or redirect_url Add integration test that exercises the full flow: authenticate -> authorize -> exchange code -> verify JWT
5ff85f2 to
714f6dc
Compare
Add a headless browser test that exercises the full OAuth 2.1 flow: unauthenticated authorize -> login -> magic link -> verify -> authorize -> code - Capture magic links in dev via devMagicLinks store when SENDGRID_API_KEY is not set - Expose /api/test/magic-links GET/DELETE endpoints for tests to read/clear captured links (dev-only, not available in production) - Use APIRequestContext for magic link verification so cookies are handled automatically across requests - Add e2e job to CI that runs after unit tests, using chromium-headless-shell for a smaller Playwright download
714f6dc to
215a38d
Compare
1092615 to
82dbb02
Compare
The jwt ruby gem (3.2.0) does not support EdDSA signatures. Configure Better Auth to use RS256 instead so the planner can verify id_tokens without additional dependencies.
82dbb02 to
8e0c04b
Compare
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
Connect the auth service to the planner via OAuth 2.1 / OIDC using Better Auth's built-in
oauthProviderandjwtplugins. The planner acts as a first-party OAuth client.Changes
Auth configuration (
src/auth.js)jwt()plugin — issuesid_tokens withemailandnameclaims for the planneroauthProvider()plugin — exposes OAuth 2.1 authorize/token endpoints (/api/auth/oauth2/*)trustedOriginsforlocalhost/127.0.0.1in devConfig (
src/config.js)AUTH_DEFAULT_PORTandPLANNER_DEFAULT_PORTconstantsbase_url,planner_url, andallowed_redirectsto use the planner callbackLogin flow (
src/app/routes/auth.js,src/app/components/login.js)redirect_url-based flow withcallbackURLpointing to the OAuth authorize endpointshowLogin,showMagicLinkForm,sendMagicLink,startGitHubOAuth)getCallbackURLpreserves OAuth query params (PKCE, state, etc.) from the authorize requestcallbackURLDatabase seeding
src/app/db/seed-client.js— inserts the planner OAuth client via raw SQL (Better Auth has no public API for this without admin auth)scripts/migrate.js— runs migrations and seeds the planner client (for Heroku release phase)Dev test helpers
src/dev/magic-links.js— captures sent magic links whenSENDGRID_API_KEYis unsetsrc/app/app.js— exposes/api/test/magic-linksGET/DELETE in non-production environmentsCI
e2ejob running Playwright after unit testschromium-headless-shellfor smaller downloadpermissions: contents: readat workflow level for zizmor complianceTesting
158 unit tests, 0 failures. New tests cover:
redirect_uri