All articles
    Authentication

    OAuth2 and OpenID Connect: How to Implement Authentication the Right Way

    June 10, 2026 10 min read
    OAuth2OpenID ConnectAuthenticationSecurityC#.NET

    OAuth2 Is Not Authentication

    This is the most important thing to understand before implementing OAuth2. OAuth2 is an authorisation protocol: it allows a user to grant an application access to resources on their behalf. It does not define how to verify who the user is. That is what OpenID Connect (OIDC) adds on top of OAuth2.

    In practice, most applications need both: OAuth2 for the delegation flow and OIDC for the identity claim. Understanding this distinction prevents a category of security mistakes that come from using OAuth2 tokens as if they were identity assertions.

    The Four OAuth2 Grant Types

    OAuth2 defines several grant types for different scenarios. Choosing the wrong one for your situation creates either security problems or unnecessary complexity.

    Authorization code with PKCE

    The right choice for any application where a human user is logging in. The user is redirected to the authorisation server, authenticates there, and is redirected back with an authorization code. The code is exchanged for tokens. PKCE (Proof Key for Code Exchange) prevents interception attacks by binding the token exchange to the original request. Every new OAuth2 implementation for a public client should use this flow.

    Client credentials

    The right choice for server-to-server communication with no human user involved. Your service authenticates directly with the authorisation server using a client ID and secret, and receives an access token. No redirect, no user consent. Use this for background jobs, microservices calling internal APIs, and machine-to-machine integrations.

    Refresh token

    Not a standalone grant type but a mechanism used alongside the authorization code flow. When an access token expires, the client uses a refresh token to get a new access token without asking the user to log in again. Refresh token rotation (issuing a new refresh token with each use and invalidating the old one) is the current best practice for detecting token theft.

    Implicit flow

    Deprecated. Do not use it. It was designed for browser-based apps before PKCE existed and has security weaknesses that PKCE solves. If you see existing code using the implicit flow, it should be migrated to authorization code with PKCE.

    Token Handling: Where Most Implementations Go Wrong

    Storing tokens correctly

    Access tokens stored in localStorage are readable by any JavaScript on the page, including injected scripts from XSS attacks. The safer option for web apps is HttpOnly cookies, which are inaccessible to JavaScript. For mobile apps, use the platform's secure keychain (iOS Keychain, Android Keystore).

    Validating tokens on the server

    Never trust a token just because it is present. Always validate the signature using the authorisation server's public keys (available at the JWKS endpoint), check the expiry (exp claim), verify the audience (aud claim) matches your API, and check the issuer (iss claim) matches the expected authorisation server. Skipping any of these checks creates a security vulnerability.

    Token expiry and refresh

    Access tokens should be short-lived: 5 to 15 minutes is typical. The application should detect 401 responses, attempt to refresh the access token using the refresh token, retry the original request with the new token, and only redirect to login if the refresh fails. This must be implemented carefully in concurrent environments to avoid refresh token races, where multiple requests simultaneously detect an expired token and all try to refresh at once. Use a mutex or queue to serialize the refresh operation.

    Google OAuth2: The Redirect URI Trap

    Google's OAuth2 implementation enforces an exact match on redirect URIs, including the protocol. http://app.example.com/callback and https://app.example.com/callback are treated as different URIs. After any SSL migration or domain change, every registered redirect URI in Google Cloud Console must be updated to match what the application now sends.

    We resolved exactly this issue for Bonanno Mental Healthcare, restoring patient portal login access within hours after another developer spent two days unable to diagnose the mismatch. The error message (redirect_uri_mismatch) is correct but not specific enough to identify the protocol difference at a glance.

    OpenID Connect on Top of OAuth2

    OIDC adds an ID token (a JWT containing user identity claims) alongside the OAuth2 access token. Key claims in the ID token:

    • sub: The user's unique identifier at the authorisation server. Use this as your stable user identifier, not the email address (emails change).
    • email and email_verified: The email address and whether the provider has verified it.
    • nonce: A value you generate and include in the authorization request. Verify it matches in the ID token to prevent replay attacks.

    The OIDC UserInfo endpoint provides additional claims beyond what fits in the ID token. Call it when you need profile data that is not included in the token by default.

    Our OAuth2 Experience

    OAuth2 and OIDC integrations are among the most common engagements we take on. We have implemented OAuth2 for .NET backends, fixed broken Google OAuth2 integrations for healthcare platforms, built LTI 1.3 (which uses OAuth2 for tool authentication in e-learning systems), and delivered VB.NET OAuth2 integrations for clients in New York, completing one engagement five days ahead of schedule with fewer hours than agreed.

    If you need an OAuth2 or OIDC integration implemented correctly the first time, or you have an existing implementation that is not working, get in touch with the details of your setup.

    K

    Khalil

    Senior Software Engineer & Founder, FriendsBit

    8+ years building enterprise software, API integrations, and cloud systems across healthcare, government, and SaaS. React, Next.js, Go, .NET, React Native, and AWS.

    LinkedIn

    Have a similar challenge?

    We've solved problems like this before. Tell us about your project and we'll get back to you within 24 hours.

    Get in touch

    Related service

    API Integration Services

    View service