Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Spring Security & JWT Auth

Spring Security Basics

  • Spring Security Overview
  • JWT Authentication
  • Authentication Mechanics

Authorization

  • Role-Based Access Control
  • Password Encoding
  • OAuth2 & Social Login Basics
Chaturmind
← Spring Security & JWT Auth

Spring Security Basics

  • Spring Security Overview
  • JWT Authentication
  • Authentication Mechanics

Authorization

  • Role-Based Access Control
  • Password Encoding
  • OAuth2 & Social Login Basics
HomeLearnSpring BootSpring Security & JWT AuthAuthorization
✓ FreeAdvanced· 7 min read

OAuth2 & Social Login Basics

OAuth2 flows, Spring Security OAuth2 client, and adding Google login to your API.

Published September 21, 2026


OAuth2 & Social Login Basics

OAuth 2.0 is a protocol that lets one application get limited access to a user's data held by another service, without ever seeing the user's password. When a photo-printing site asks to "access your Google Photos", OAuth2 is how Google gives it a token for just your photos, for a limited time, which you can revoke.

OpenID Connect (OIDC) is a thin layer on top of OAuth2 that adds login: along with the access token, you get an ID token, a signed statement of who the user is. "Sign in with Google" is OIDC.

The one-line distinction interviewers look for: OAuth2 is about authorization (what an app may access); OIDC adds authentication (who the user is).

The four roles

RoleIn "Sign in with Google" for your app
Resource ownerThe user
ClientYour application
Authorization serverGoogle's accounts service, which authenticates the user and issues tokens
Resource serverAn API that accepts tokens, e.g. Google's APIs, or your own API

The Authorization Code flow (with PKCE)

This is the flow for any app with a user in front of it:

 1. User clicks "Sign in with Google" in your app
 2. Your app redirects the browser to Google:
      /authorize?client_id=…&redirect_uri=…&response_type=code
                &scope=openid email profile&state=<random>&code_challenge=<hash of secret>
 3. User logs in at Google (your app never sees the password) and approves the scopes
 4. Google redirects back:   https://yourapp.com/login/oauth2/code/google?code=<one-time code>&state=<same random>
 5. Your server exchanges the code (plus client secret and PKCE code_verifier) at Google's token endpoint
 6. Google returns:  access_token  (+ id_token for OIDC, + optional refresh_token)
 7. Your server validates the id_token, finds or creates the local user, and starts a session

Why the extra step with a code instead of returning the token directly? The code travels through the browser's address bar, which is exposed to history, logs and extensions. It's useless on its own. The actual tokens are fetched server-to-server in step 5, together with credentials an attacker doesn't have.

Two security parameters matter:

  • state: a random value your app generates and checks when the user comes back. It prevents CSRF, where an attacker tricks a user into completing a login the attacker started.
  • PKCE (Proof Key for Code Exchange): the app creates a random secret, sends its hash in step 2, and the secret itself in step 5. An intercepted code can't be redeemed without the secret. It's required for mobile and single-page apps (which can't keep a client secret), and recommended everywhere.

The old Implicit flow (token returned directly in the redirect) and the Password grant (the app collects the user's password) are deprecated. Don't use them.

Other flows you should recognise

  • Client Credentials: machine-to-machine, no user. Service A authenticates with its own ID and secret and gets a token to call service B. Common between microservices.
  • Refresh token: exchanges a long-lived refresh token for a new short-lived access token without bothering the user.
  • Device Authorization: for TVs and CLIs without a browser ("go to example.com/device and enter code ABCD-1234").

Tokens

TokenPurposeWho reads it
Access tokenProves the bearer may call an API with certain scopes. Short-lived (minutes to an hour)The resource server
ID token (OIDC)A signed JWT describing the user: sub (stable user ID), email, name, iss, aud, expYour app (the client), never sent to APIs as a credential
Refresh tokenGets new access tokens. Long-lived, so it must be stored securelyThe authorization server only

Scopes say what's being requested: openid email profile for login, or API-specific scopes such as photos.read. Ask for the minimum.

Social login in Spring Boot

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: ${GOOGLE_CLIENT_ID}
            client-secret: ${GOOGLE_CLIENT_SECRET}
            scope: openid, email, profile
          github:
            client-id: ${GITHUB_CLIENT_ID}
            client-secret: ${GITHUB_CLIENT_SECRET}
@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
    return http
            .authorizeHttpRequests(a -> a.requestMatchers("/", "/public/**").permitAll()
                                         .anyRequest().authenticated())
            .oauth2Login(o -> o.userInfoEndpoint(u -> u.oidcUserService(appUserService())))
            .build();
}

Spring Boot knows Google's and GitHub's endpoints, so the configuration is short. It handles the redirect, state, the code exchange and ID token validation for you. Your job is mapping the external identity to your own user:

@Service
@RequiredArgsConstructor
public class AppUserService extends OidcUserService {
    private final UserRepository users;

    @Override
    public OidcUser loadUser(OidcUserRequest request) {
        OidcUser oidcUser = super.loadUser(request);
        String provider = request.getClientRegistration().getRegistrationId();   // "google"
        String subject  = oidcUser.getSubject();                                  // stable per-provider user ID

        users.findByProviderAndSubject(provider, subject)
             .orElseGet(() -> users.save(User.fromOidc(provider, subject, oidcUser.getEmail(), oidcUser.getFullName())));
        return oidcUser;
    }
}

Key the user on provider + sub, not on email. Emails can change, and not every provider verifies them. Linking a new social login to an existing account by email alone is a known account-takeover risk unless the provider asserts email_verified.

Protecting your own API: resource server

If a separate authorization server (Keycloak, Auth0, Okta, Cognito) issues tokens for your API, the API only needs to validate them:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com/realms/shop
http.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()));

Spring downloads the issuer's public keys (JWKS), and on each request verifies the JWT's signature, expiry, issuer and, if configured, audience. Scopes become authorities (SCOPE_orders.read) usable in @PreAuthorize.

Where should a browser app keep tokens?

Tokens in localStorage can be read by any script on the page, so one XSS bug leaks them. The recommended pattern for browser apps is a Backend-for-Frontend (BFF): the server side of your app does the OAuth flow, keeps the tokens, and gives the browser only an HttpOnly, Secure, SameSite session cookie that JavaScript can't read.

Follow-up questions this topic invites — and their answers

Q: What's the difference between OAuth2 and OpenID Connect? A: OAuth2 issues access tokens that grant an app access to resources. It says nothing standard about who the user is. OIDC adds an ID token (a signed JWT with user identity claims), a userinfo endpoint and standard scopes (openid, email, profile). Use OIDC for "log in with X", and OAuth2 scopes for "access my data at X".

Q: Why is PKCE needed if the code is short-lived? A: A short lifetime doesn't stop an attacker who intercepts the redirect, for example a malicious app registered for the same custom URL scheme on mobile, from redeeming the code first. PKCE binds the code to a secret that only the app that started the flow knows, so a stolen code is useless.

Q: How do you log a user out? A: Ending your own session is the first part (invalidate the session cookie). Access tokens stay valid until they expire, which is why they're kept short. Revoke refresh tokens at the authorization server. If you want to log the user out of the identity provider too, redirect to its end-session endpoint (OIDC RP-initiated logout).

Q: Access token as a JWT or an opaque string? A: A JWT can be validated locally by any API using the issuer's public key: no network call, but it can't be revoked before it expires. An opaque token must be checked with the authorization server (introspection) on each use, which costs latency but allows instant revocation. Short-lived JWTs plus revocable refresh tokens is the common compromise.

Q: Which flow for one backend service calling another? A: Client Credentials. Each service has its own client ID and secret (or a certificate), gets a token scoped to what it's allowed to call, and caches the token until it nears expiry. There's no user involved, so no redirects.

Previous

Password Encoding

AI Tutor

Lesson: OAuth2 & Social Login Basics

Quick actions

AI responses can be inaccurate. Verify critical information.