Add Google authentication with OpenID Connect and PKCE#840
Merged
Conversation
…del.JsonWebTokens
Approve Database Migration
|
64c966d to
3a72e17
Compare
3a72e17 to
ac05df0
Compare
ac05df0 to
122f1b8
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 & Motivation
Add external authentication via Google OAuth using OpenID Connect (OIDC) with PKCE, enabling users to sign up and log in with their Google account. The implementation is designed as a provider-agnostic framework that can be extended with additional identity providers (Microsoft Entra ID, MitID/Criipto, OKTA, etc.) by implementing the
IOAuthProviderinterface.Significant effort has gone into security hardening, domain modeling, infrastructure automation, frontend UX, and comprehensive test coverage.
Domain model and authentication flow changes:
Loginaggregate toEmailLoginand consolidateEmailConfirmationinto it, creating a cleaner separation between email-based OTP authentication and external provider authenticationExternalLoginaggregate to track OIDC flows from authorization redirect through callback completion, storing PKCE code verifier, encrypted state token, browser fingerprint, and nonceExternalIdentitiesJSON column onUserentity to support linking multiple external providers per user (Google, Entra ID, etc.)LoginMethodtoSessionentity to track whether a session was created via email OTP or an external provider/authentication/email/and external authentication uses/authentication/{provider}/Provider abstraction (
IOAuthProvider):BuildAuthorizationUrl()-- construct the OIDC authorization URL with PKCE challenge and nonceExchangeCodeForTokensAsync()-- exchange authorization code for tokens using PKCE verifierValidateIdTokenAsync()-- validate JWT signature, audience, issuer, nonce, andat_hashMapClaims()-- extract user profile from claims with provider-specific mappingGoogleOAuthProviderimplements this interface; aMockOAuthProviderenables E2E testing without real Google credentialsSecurity measures implemented:
at_hashclaim validation for access token bindingazp(authorized party) claim validation for defense-in-depthReturnPathHelperand frontendisValidReturnPath)@mock.localhostemail domains as a production safeguardJwtSecurityTokenHandlertoJsonWebTokenHandler(Microsoft's recommended replacement)__Host-return-pathcookie with 5-minute MaxAge and strict validationidquery parameter to alphanumeric and hyphens onlyBreaking:
__Host-cookie prefix correction:All authentication cookies have been renamed from
__Host_(underscore) to__Host-(hyphen). The__Host_prefix was silently ignored by browsers, meaning the cookie security attributes (Secure, Path=/, no Domain) were not being enforced. With the corrected__Host-prefix, browsers now properly enforce these constraints. All existing sessions are invalidated by this change -- every user will need to reauthenticate.Frontend:
/error) with context-specific error codes:user_not_found,account_already_exists,identity_mismatch,authentication_failed,invalid_request,access_denied,session_expiredlocalStorageand passed during Google loginInfrastructure:
key-vault-secrets.bicepmodulepp set-github-configdeveloper CLI command for configuring GitHub repository variables and secretsTesting:
ExternalLoginaggregate lifecycleExternalAvatarClient,GoogleOAuthProviderat_hashvalidation,MockOAuthProvideremail enforcement,PkceUtilities, andReturnPathHelperMockOAuthProvidercovering the full Google OAuth signup/login cycle, preferred tenant selection, error paths (access denied, token exchange failure, email not verified, user not found), and direct error page rendering with reference IDsDownstream projects
Breaking: All users must reauthenticate. Authentication cookies have been renamed from
__Host_(underscore) to__Host-(hyphen), which invalidates all existing sessions. No action is needed -- users will simply be prompted to log in again.Google OAuth is enabled by default. If no Client ID and Client Secret are configured, the buttons will be visible but the OIDC flow cannot complete. To enable Google OAuth, follow the setup instructions in the README section "(Optional) Set up Google OAuth for Sign in with Google".
To remove the Google sign-in buttons (if not using Google OAuth), delete the Google button markup and related state from:
application/account-management/WebApp/routes/login/index.tsx-- removehandleGoogleLogin,isGoogleLoginPending, the "or" divider, and the "Continue with Google" buttonapplication/account-management/WebApp/routes/signup/index.tsx-- removehandleGoogleSignup,isGoogleSignupPending, the "or" divider, and the "Sign up with Google" buttonapplication/account-management/WebApp/shared/images/google-icon.svg-- delete the icon file and its importsChecklist