Social login registration & sign-in
A user authenticates and (when permitted) registers an account through an external identity provider — Google, Microsoft, or LinkedIn — using OAuth 2.0 Authorization Code with PKCE and OpenID Connect. The flow covers first-time registration via invitation or domain allowlist, sign-in for already-linked users, and linking a social provider to an existing password account.
When this happens: You click Continue with Google / Continue with Microsoft / Continue with LinkedIn on the Login page or on the Accept Invitation page.
Step by step
- 1
Click Continue with [Provider] on the Login page.
EasyCRM generates a cryptographically random
stateandnonceand a PKCEcode_verifier(storingcode_challenge = SHA256(verifier)server-side, bound to the browser session). EasyCRM takes you the browser to the provider's authorization endpoint withresponse_type=code.state,nonce,code_challenge,code_challenge_method=S256, the configuredclient_id, and the registered redirect URI. - 2
Authenticates with the provider and consents to share email and profile claims.
- 3
Redirects the browser back to
/api/auth/external/{provider}/callback?code=…&state=….EasyCRM checks that
statematches the value persisted in step 2. (Mismatch — .) Exchanges the authorization code for tokens by calling the provider's token endpoint over TLS, presenting theclient_secretand thecode_verifier. EasyCRM checks the returnedid_token: signature against the provider's JWKS,iss,aud,exp, andnonce. (Failure — .) Extracts claims:sub,email,email_verified,given_name,family_name,name,picture,locale,zoneinfo. EasyCRM verifiesemail_verified = true. (False or absent — .) Looks up anExternalLoginrow for (provider,sub). Not found — continues to step 11. (Found — sign-in path.) Looks up an activeUserwhoseEmailmatches the asserted email. Not found — continues to step 12. (Found — linking prompt.) Resolves the registration path: (a) an active, non-expiredInvitationexists for the email — use the invitation's role and Reports-To; (b) otherwise, the email's domain matches an activeAutoProvisionDomainrule — use that rule's default role and Reports-To. (Neither matches — .) EasyCRM creates the user record with:Email,FirstName/LastNamefromgiven_name/family_name(or splitname),Usernamegenerated from the email local part with collision suffixing (jdoe,jdoe2, …),Rolefrom step 12.Timezonefromzoneinfo—locale— tenant default,ProfilePhotoUrlfrompicture. EasyCRM creates anExternalLoginrow bindingUser,Provider,ProviderSubject,LinkedAt, and the email asserted at link time. If the registration path used an invitation, marks itAcceptedwith the resolvedUser. If the resolved role enforces MFA — routes the user to MFA Enrollment (UC-1.2, ). Otherwise continues to step 17. Issues a JWT access token and refresh token; sets HTTP-only Secure cookies. EasyCRM creates audit entries:UserRegisteredViaSocial(with provider and registration path) andUserLogin. If required profile fields cannot be inferred (e.g. Timezone), routes to the Profile Completion screen (). Otherwise redirects to the role-based default dashboard.
Other paths
Existing account — link external login
If a user with the asserted email already exists. EasyCRM shows the Link Account screen: An account for [email] already exists. Sign in once with your password to connect [Provider] Enter their existing password (and an MFA code if MFA is enabled on the account). EasyCRM verifies the password and any MFA code using the standard checks from UC-1.1 (lockout, deactivation, and MFA rules apply identically). On success, creates an ExternalLogin row linking the existing User to (provider, sub). EasyCRM sends an email to the account owner: [Provider] was connected to your account on [date/time]. If this wasn't you, contact your administrator Continues from main-flow step 17 issuing tokens for the existing user. Audit entry: ExternalLoginLinked (actor: self). If you click Cancel instead — see .
Returning user — direct sign-in
If an ExternalLogin row matches (provider, sub). System loads the linked User. EasyCRM runs the standard account-state checks from UC-1.1: locked account — (mirrors UC-1.1 ); deactivated account — (mirrors UC-1.1 ); tenant suspended — . If the linked your email differs from the email returned by the provider — see . If the role enforces MFA but you has not enrolled — routes to MFA Enrollment (UC-1.2, ). If MFA is enabled — presents MFA verification (UC-1.1, ) before proceeding. Issues JWT access and refresh tokens. Audit entry: UserLoggedInViaSocial with provider name. Continues from main-flow step 19.
Domain-whitelisted auto-provisioning
If no invitation exists, but the email's domain matches an active AutoProvisionDomain rule. System resolves Role and ReportsToUserId from the rule. Continues from step 13. Audit entry includes and the matched domain. The Admin role is never auto-assigned via this path even if a misconfigured rule names it; system substitutes the tenant default role and emits a SecurityWarning audit entry for Admin review.
Profile completion required after registration
If the new you are missing required fields (e.g. The provider returned no usable name; timezone could not be inferred). EasyCRM takes you to the Profile Completion screen with only the missing fields shown. Fill in the required fields and clicks Continue EasyCRM saves the profile updates and redirects to the role-based default dashboard.
Redemption from accept-invitation page
Click the redemption link in the invitation email and lands on the Accept Invitation page. Select Continue with [Provider] on that page (instead of the password path). Carries the invitation token through the OIDC state so step 12 can resolve the invitation deterministically even if the provider returns a different (e.g. Relay) email. Continues from step 13. The invitation supersedes the asserted email when it differs (with audit annotation EmailOverriddenByInvitation).
If something goes wrong
State mismatch (csrf protection)
If the returned state does not match the value persisted at step 2 (or has already been consumed). System aborts the flow and clears the pending state. EasyCRM shows a generic error: We could not securely complete sign-in. Please start again Audit entry: SocialLoginRejected with , source IP, and user-agent.
Provider authorization denied or cancelled
Returns (or you closed the consent window). EasyCRM returns you to the Login page with: Sign-in with [Provider] was cancelled Audit entry: SocialLoginRejected with .
Email not verified by provider
If email_verified is false or the claim is absent. EasyCRM shows: Your [Provider] account email is not verified. Please verify it with [Provider] and try again Audit entry: SocialLoginRejected with .
Registration not permitted
If no invitation matches the email and the email's domain is not on the tenant's allowlist. EasyCRM shows: We don't have an invitation for [email]. Please contact your administrator Offers a Request access link that opens a form which emails tenant Admins. No user record is created. Audit entry: SocialLoginRejected with , captured email, and provider.
User cancels account linking
At , clicks Cancel Aborts; no ExternalLogin row is created and the existing account remains untouched. EasyCRM returns you to the Login page with: [Provider] sign-in was cancelled. You can still sign in with your password Audit entry: SocialLoginRejected with .
Provider did not return an email
If the email claim is empty or absent. EasyCRM shows: We could not retrieve your email from [Provider]. Please grant email access or use another sign-in method Audit entry: SocialLoginRejected with .
Invalid `id_token`
If signature, issuer, audience, expiry, or nonce validation fails. Aborts and displays a generic error: Sign-in failed. Please try again Audit entry: SocialLoginRejected with and the specific failure type (e.g. SignatureMismatch, ExpiredToken, NonceMismatch) for forensic review — never shown to the end user.
Provider disabled by admin
If the requested provider is no longer enabled. Hides the button on the Login page (provider list is cached for ≤60 seconds) and returns HTTP 404 if the start route is hit directly. Audit entry: SocialLoginRejected with .
Invitation expired
If an invitation matches the email but has expired. EasyCRM shows: Your invitation has expired. Please ask your administrator to send a new one Offers a Request a new invitation link that emails the inviting Admin. Audit entries: InvitationExpired and SocialLoginRejected with .
Tenant inactive or suspended
At step 12 or 13, the resolved tenant is inactive. EasyCRM shows: This workspace is currently unavailable. Please contact your administrator Audit entry: SocialLoginRejected with .
Account locked or deactivated (returning user)
At , the matched you are locked (failed-attempt threshold) or deactivated. Mirrors UC-1.1 (locked) or UC-1.1 (deactivated) — same user-facing messages, same response codes. Audit entry includes the social-login provider context.
Provider email drift (possible account hijack)
At , the (provider, sub) is unchanged but the asserted email differs from the email cached on the ExternalLogin row. If (default): updates the cached email on the ExternalLogin row, emits ExternalLoginEmailChanged audit entry, and continues sign-in. If : blocks sign-in, sends a confirmation link to the previously-cached email, and displays Please confirm this change from your previous email address before continuing Audit entry: SocialLoginRejected with .
(Provider, subject) already linked to another user
During explicit linking, the (provider, sub) being added is already attached to a different user. EasyCRM shows: This [Provider] account is already connected to another user Linking is rejected with no changes. Audit entry: ExternalLoginLinkRejected with .
Good to know
- Social login registration is not unrestricted self-registration. It requires either an Admin-issued invitation or a domain on the tenant's auto-provision allowlist.
- All OAuth flows MUST use Authorization Code with PKCE;
stateandnonceare required and validated. - The
(provider, subject)pair is the canonical, immutable external identifier;emailis treated as a mutable attribute and never used alone for identity matching after the first link. - A user account MUST have at least one usable sign-in method at all times. Unlinking the last login method without setting a password is blocked.
- Provider must assert
email_verified = truefor any registration. Linking to an existing account additionally requires successful local re-authentication. - Role-based MFA enforcement applies after social authentication identically to password authentication.
- Brute-force counters and account lockout apply to the password-verification step of (linking) but NOT to the OIDC handshake itself; per-IP rate limiting protects the OIDC start endpoint.
- Auto-provisioned accounts MUST NOT be assigned the Admin role. Admin elevation is always explicit and post-registration.
- Client secrets and TOTP keys MUST be encrypted at rest using the platform key vault and MUST NOT be returned to the UI after save.
- Disabling a provider takes effect within 60 seconds; in-flight callbacks complete normally to avoid orphaning users mid-handshake.