Social auth
This pack adds social sign-in to your app. Provider buttons on your login and signup pages run an OAuth round-trip, and signed-in users get a connected-accounts area to link more providers or disconnect ones they no longer use.
- Provider emails are trusted only when the provider verifies them, and an unverified one is routed through your existing email-verification flow.
- An email that already belongs to a local account is refused rather than silently linked to the provider.
- A password-less account cannot disconnect its last provider, so it can never lock itself out.
- A "Last used" hint marks whichever method the visitor signed in with last.
This pack extends the auth pack's User model and reuses its email-verification flow. Apply the auth pack first.
Apply the social auth pack with Flow. Start a new session in your selected coding agent and execute the following slash command inside it.
After applying, Flow will make the following changes to your app.
-
Reuse your account settings area to host the connected accounts, or establish a minimal profile page when you have none.
-
Land the backend as shipped, from the
social_identitiesmigration and the nullable-password change through thewithSocialIdentity()model mixin, the OAuth redirect and callback controller, the disconnect controller, and the routes. -
Configure the providers you pick, Google and GitHub by default, wire the "Last used" hint into your login and signup, and add the two-factor branch when your app already has two-factor.
-
Restyle the provider buttons on your login and signup pages and the connected-accounts section to your design system.
-
Run the tests, then walk the sign-in, connect, and disconnect flow and show you the result.
Apply the social auth pack to your app by hand by working through the steps below in order. It builds on the auth pack, adding provider sign-in to your login and signup pages and a connected-accounts area to your account settings.
-
Confirm your auth foundation
This pack builds on the auth pack. Make sure your app already has it before continuing.
- Your User model composes
withAuthFinder(...)andwithManagedEmail(), and@adonisplus/personais installed. - The email-verification flow is wired: the
account_activation_provisionmailer, a resolvableemail_verifications.createroute, andunverifiedEmailandcreateEmailVerificationTokenon the User model. - A web session guard, the
guestandauthnamed middleware, and an authenticated landing route (dashboardin these steps) are registered.
- Your User model composes
Flow adapts the pack to your app, so the exact set of created and edited files depends on what you already have. This is the shape of an apply onto the hypermedia starter kit with the auth pack in place.
Configuration
Social identities are stored by the withSocialIdentity() mixin on the User model, which reads and writes one row per linked provider account. Pass an options object to the mixin to change where those rows live.
table
The table social identities are read from and written to. Defaults to social_identities.
export default class User extends compose(
UserSchema,
withAuthFinder(hash),
withManagedEmail(),
withSocialIdentity({ table: 'oauth_identities' })
) {}
One provider account links to one user
Every linked account is a row keyed by its provider name and the provider's own user id, with a unique constraint on that [provider, provider_user_id] pair behind it. Two behaviors follow:
- Linking a provider account the user already holds just refreshes it in place, so re-authenticating with the same provider is safe to repeat.
- Linking one that already belongs to a different user is refused with an error, so a single Google or GitHub account can never attach to two local accounts.
Provider data is a snapshot, not your source of truth
Each identity row keeps the provider's email, name, nickname, and avatarUrl, and every sign-in refreshes them from the provider. These fields stay on the identity row and never flow back to your users table.
So a user renaming themselves on GitHub does not rename their account with you. The connected-accounts view reads the fresh provider snapshot, while your account record stays under your app's control.
Provider emails are trusted only when verified
Not every provider confirms that the person owns the email it hands back. Ally reports this per provider as emailVerificationState, one of verified, unverified, or unsupported, and a new signup forks on it:
- A
verifiedemail is treated as active immediately. - Any other state routes the new account through the same email-verification flow the auth pack ships, so an unconfirmed address still has to prove itself before the account is active.
A colliding email is refused, not merged
When someone signs in with a provider whose email already belongs to a local account that has no link to that provider, resolveSocialIdentity returns a link_required outcome and the pack refuses the sign-in. It neither logs them in nor attaches the provider to the existing account.
Auto-linking on a matching email would let anyone who controls an address at a provider take over the local account that happens to share it. Instead the user is told to sign in with their original method and connect the provider from their account settings, where the link is made against a session that is already theirs.
1.0.0
Initial release.