Google + Microsoft SSO (OAuth v1)
| Field | Value |
|---|---|
| Status | Deferred. Not included in any current tier. |
| Owner | Unassigned |
| Target repos | attunelogic-api, attunelogic-service, attunelogic-mobile |
| Related | Monetization & Tier Packaging (v1), Feature Roadmap Index |
| Last updated | 2026-04-23 |
Status banner: Google + Microsoft OAuth SSO was removed from the public Pro roadmap in v1. Passport-local (email + password + optional MFA) is the only production login path today. SAML SSO stays Enterprise-only and is a separate proposal.
Why this was removed from v1
- Each provider is a ~1 to 1.5 week lift on its own: configure the OAuth app, add passport strategy, build "link to existing email" onboarding, add the login buttons to the service web sign-in page, then handle the redirect-URI story for mobile via
expo-auth-session. - Two providers × two surfaces (web + mobile) + the account-linking UI blows past the launch-week budget.
- Not a win for pricing differentiation at launch either — most small trucking ICPs don't have corporate Google/MS tenants. SSO becomes a legitimate Pro-tier hook once we move up-market.
What shipping this requires
Provider setup
- Google: Google Cloud project + OAuth consent screen verification for
openid email profile(the minimal non-sensitive scope set, which avoids Google's slow security review). - Microsoft: Azure App Registration. Multi-tenant app (
commonauthority) so any MS tenant can sign in. Scopes:openid profile email offline_access. - Per-environment redirect URIs:
service.attunelogic.com/auth/callback/googleservice.attunelogic.com/auth/callback/microsoftattunelogic://auth/callback/google(mobile deep link viaexpo-auth-session)attunelogic://auth/callback/microsoft
Data model
- Extend
User:authProviders: [{ provider: "local" | "google" | "microsoft", providerUserId: string, linkedAt: Date }]
- Existing
emailremains the human identity key; all providers link to the same user row by normalized email match on first login, subject to admin confirmation the first time.
API + passport strategies
passport-google-oauth20— existing passport setup inattunelogic-api/src/services/auth/*.passport-azure-ad(OIDC strategy againsthttps://login.microsoftonline.com/common/v2.0).- Middleware:
GET /api/v1/auth/oauth/:provider/start— issues state + redirect.GET /api/v1/auth/oauth/:provider/callback— exchanges code, links or creates user, issues session.
- Account linking: if the incoming provider email matches an existing local-auth user, require password confirmation OR email-link confirmation before attaching the new provider. Never silently merge accounts on email alone.
Service web UI
- Sign-in page: "Sign in with Google" + "Sign in with Microsoft" buttons under the existing email/password form.
- Account settings: "Linked accounts" row listing linked providers with an Unlink button (refuses to remove the last auth method).
Mobile UI
expo-auth-sessionflow on iOS + Android. Uses the universal-links / deep-link redirect URIs above.- Reuse the existing
useAuthhook — SSO completion calls the same session-bootstrap endpoint after the code exchange.
Feature gating
sso.enabledfeature flag — already reserved in the monetization proposal. Pro and Enterprise only.- On Starter and Growth, the SSO buttons on the sign-in page are hidden. Paid Enterprise SAML is a separate proposal.
Tests
- Unit: provider-to-user linking precedence (email match, provider id match, no match → new user with confirmation email).
- Integration (mocked OIDC): happy-path login, state mismatch rejection, email verification not-set rejection.
- Tenant isolation: a linked provider identity on tenant A cannot impersonate a user in tenant B, even if the same email exists on both (multi-tenant email re-use is allowed in our data model).
Docs
- Customer SSO setup guide.
- Admin runbook for unlinking a compromised provider account.
Dependencies on existing infra
- passport-local — already wired in
src/services/auth. New strategies drop in beside it. - Session issuance — the existing session cookie + refresh flow is reused unchanged.
- useAuth hook on mobile — already handles session bootstrap; SSO just feeds it.
- Feature flag
sso.enabled— already reserved.
Estimated timeline and risk
- Effort: 1–1.5 weeks per provider for API + service web + mobile wiring. 2.5–3 weeks total for both.
- Risk: Low-medium. The risk is almost entirely in the account-linking UX, not the OAuth itself. The first time a customer signs in with Google using an email that already has a local password, we MUST surface the linking confirmation correctly — getting that wrong is an account-takeover primitive.
Open questions
- Do we want "SSO-only" as a per-tenant policy (force all users to sign in via a specific provider)? That's a meaningful enterprise feature that bumps us toward SAML territory.
- Do we enforce email verification on first provider sign-in, or trust the provider's verification?
- Do we ship Apple Sign-In alongside Google/Microsoft on iOS? (Apple requires it on App Store submissions that ship any third-party SSO button.)
Cross-references
- Existing auth:
attunelogic-api/src/services/auth - Mobile auth hook:
attunelogic-mobile/hooks/useAuth.tsx - SAML Enterprise SSO (out of scope here) — tracked separately.
- Billing proposal: Monetization & Tier Packaging (v1)
- Roadmap index: Feature Roadmap Index