Add "Sign in with 3AG Accounts" - #6
Merged
Merged
Conversation
SalesReport becomes a relying party of the identity provider at accounts.3ag.app, so a client signs in once there instead of keeping a separate password here. A Socialite provider drives the authorization code flow with PKCE. Claims come from the provider's userinfo endpoint rather than the ID token, because that call is a direct back-channel request over TLS and needs no signature check of its own. The callback resolves the local account by oidc_sub first. Failing that it matches on email, but only when the provider reports the address verified: without that check anyone able to set an unverified email there could claim an existing account here. Otherwise it creates the user and carries on into the existing onboarding flow. This is additive. The local login, registration and password reset all still work, and the existing suite is untouched. THREE_AG_SSO_ONLY=true is the switch that retires them, redirecting the sign-in pages to the provider, removing the endpoints that would accept a local password, and ending the session at the provider on logout. It ships off; flipping it is a separate and deliberate change once the flow is proven in production. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`deploy.php` shares only `storage` and `.env`, so a SQLite file under `database/` is replaced on every release, taking the shops and orders with it. The example environment now describes MySQL, which is what production should have been using all along. CI gets a MySQL service to match, because SQLite waves through differences the real engine enforces. That change immediately caught one: a query-log assertion in the report tests matched SQLite's double-quoted identifiers, which MySQL writes as backticks. It now compares against an unquoted column list and passes on both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The override dropped Fortify's JSON branch and ignored the configured logout redirect, changing how every existing session logs out in order to serve the one case that needs diverting. Now only a session that came from the provider, with SSO-only on, is sent to end its session there. Everything else falls through to exactly what Fortify would have done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
SalesReport becomes a relying party of the identity provider at accounts.3ag.app, so a client signs in once there instead of keeping a separate password here.
Pairs with 3agApp/Accounts#2 — that has to be deployed and seeded first, since the client id and secret come from it.
How it works
A Socialite provider drives the authorization code flow with PKCE. Claims come from the provider's
/oauth/userinforather than the ID token, because that call is a direct back-channel request over TLS and needs no signature check of its own.The callback resolves the local account by
oidc_subfirst. Failing that it matches on email — but only when the provider reports the address verified. Without that check, anyone able to set an unverified email at the provider could claim an existing account here. Otherwise it creates the user and continues into the existing onboarding flow.This is additive
Local login, registration and password reset all still work, and the existing 303 tests are untouched.
THREE_AG_SSO_ONLY=trueis the switch that retires them: it redirects the sign-in pages to the provider, removes the endpoints that would accept a local password, and ends the session at the provider on logout. It ships off — flipping it is a separate, deliberate change once the flow is proven in production.Testing
12 new feature tests: linking by subject, linking by email exactly once, refusing an unverified email, rejecting a tampered callback, and both sides of the SSO-only switch. Verified in a browser against a real Accounts instance — an existing user was linked by verified email with no duplicate created.
Deploying this
.envneedsTHREE_AG_BASE_URL=https://accounts.3ag.appplus theTHREE_AG_CLIENT_IDandTHREE_AG_CLIENT_SECRETprinted by the client seeder over there. The user also needsphp artisan accounts:grant <email> SalesReporton the Accounts side, or they will be refused at the door.🤖 Generated with Claude Code