Skip to content

Spring Boot Security (Part 8) | OAuth2 Social Login Implementation

The Universal Passport Control Booth Analogy

Imagine visiting an international convention center hosting a global tech summit. At the registration desk, attendees can register by filling out a ten page handwritten registration questionnaire with emergency contacts, dietary restrictions, and physical address proof. Or, the convention center provides an automated electronic kiosk: you tap your government electronic passport on the scanner. The kiosk contacts the national passport database, extracts your verified name and citizenship, prints your summit badge, and grants access in five seconds.

In modern web development, OAuth2 Social Login is that electronic passport scanner. Instead of forcing users to invent, remember, and verify a new password for every website they join, your application delegates identity verification to trusted global identity providers (Google, GitHub, Facebook). The user logs in via Google with a single click, and Spring Boot automatically provisions their user profile inside your application database.

This lecture covers setting up spring-boot-starter-oauth2-client, configuring OAuth2 credentials for GitHub and Google in application.properties, customizing the SecurityFilterChain, and extracting authenticated user profiles with @AuthenticationPrincipal.


Step 1: Add Dependencies in pom.xml

Spring Boot provides first class support for OAuth2 clients:

xml
<dependencies>
    <!-- Spring Boot Starter Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- Spring Boot Starter OAuth2 Client -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-client</artifactId>
    </dependency>

    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Step 2: Register Application with the Identity Provider

Before configuring Spring Boot, you must obtain client credentials from your chosen identity provider:

For GitHub:

  1. Navigate to GitHub Settings -> Developer Settings -> OAuth Apps -> New OAuth App.
  2. Set Application Name: Order Service.
  3. Set Homepage URL: http://localhost:8080.
  4. Set Authorization Callback URL: http://localhost:8080/login/oauth2/code/github(This exact URI pattern is expected by Spring Security by default).
  5. Generate a Client ID and Client Secret.

Step 3: Configure application.properties

Spring Boot includes built in provider definitions for Google, GitHub, Facebook, and Okta:

properties
server.port=8080
spring.application.name=oauth2-demo

# GitHub OAuth2 Client Registration
spring.security.oauth2.client.registration.github.client-id=YOUR_GITHUB_CLIENT_ID
spring.security.oauth2.client.registration.github.client-secret=YOUR_GITHUB_CLIENT_SECRET
spring.security.oauth2.client.registration.github.scope=read:user,user:email

# Google OAuth2 Client Registration
spring.security.oauth2.client.registration.google.client-id=YOUR_GOOGLE_CLIENT_ID
spring.security.oauth2.client.registration.google.client-secret=YOUR_GOOGLE_CLIENT_SECRET
spring.security.oauth2.client.registration.google.scope=openid,profile,email

Notice how clean this is: because github and google are well known providers, you do not need to configure authorization URIs, token URIs, or user info endpoints. Spring Boot knows the standard endpoints automatically.


Step 4: Configure SecurityFilterChain

Enable OAuth2 login in your Spring Security configuration:

java
package com.example.orderservice.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                // Public landing page
                .requestMatchers("/", "/login", "/error").permitAll()
                // All other endpoints require authentication
                .anyRequest().authenticated()
            )
            // Activates OAuth2 social login
            .oauth2Login(oauth2 -> oauth2
                .defaultSuccessUrl("/profile", true)
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/")
                .permitAll()
            );

        return http.build();
    }
}

When an unauthenticated user visits a protected URL, Spring Security automatically presents a styled login selection screen with "Login with GitHub" and "Login with Google" buttons!


Step 5: Extracting Authenticated User Data

Once authenticated via OAuth2, Spring Security populates an OAuth2User principal in the security context.

You can inject it directly into controller methods using @AuthenticationPrincipal:

java
package com.example.orderservice.controller;

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class UserProfileController {

    @GetMapping("/profile")
    public Map<String, Object> getUserProfile(@AuthenticationPrincipal OAuth2User principal) {
        Map<String, Object> details = new HashMap<>();

        // Common attributes returned by identity providers
        String name = principal.getAttribute("name");
        String email = principal.getAttribute("email");
        String login = principal.getAttribute("login"); // GitHub specific

        details.put("name", name);
        details.put("email", email);
        details.put("login", login);
        details.put("authorities", principal.getAuthorities());
        details.put("allAttributes", principal.getAttributes());

        return details;
    }
}

Customizing the OAuth2UserService (Saving Users to Local DB)

In enterprise applications, social login must synchronize with your internal user database (e.g. creating a new UserEntity record if this is the user's first visit, or updating their last login timestamp).

Implement DefaultOAuth2UserService:

java
package com.example.orderservice.security;

import com.example.orderservice.entity.UserEntity;
import com.example.orderservice.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        // 1. Delegate to parent to fetch user attributes from provider
        OAuth2User oAuth2User = super.loadUser(userRequest);

        // 2. Extract provider details (e.g. "github", "google")
        String clientRegistrationId = userRequest.getClientRegistration().getRegistrationId();
        String email = oAuth2User.getAttribute("email");
        String name = oAuth2User.getAttribute("name");

        System.out.println("Processing OAuth2 Login via: " + clientRegistrationId + " for: " + email);

        // 3. Provision or synchronize user in local database
        userRepository.findByEmail(email).ifPresentOrElse(
            existingUser -> {
                existingUser.setLastLoginDate(java.time.LocalDateTime.now());
                userRepository.save(existingUser);
            },
            () -> {
                UserEntity newUser = new UserEntity();
                newUser.setEmail(email);
                newUser.setUsername(name != null ? name : email);
                newUser.setProvider(clientRegistrationId);
                newUser.setRole("USER");
                userRepository.save(newUser);
            }
        );

        return oAuth2User;
    }
}

Register your custom service in SecurityConfig:

java
@Autowired
private CustomOAuth2UserService customOAuth2UserService;

// Inside securityFilterChain:
.oauth2Login(oauth2 -> oauth2
    .userInfoEndpoint(userInfo -> userInfo
        .userService(customOAuth2UserService)
    )
)

Interview Questions & Pitfalls

Q1: What is the default redirect URI format expected by Spring Security for OAuth2 providers?

The default callback pattern is: /login/oauth2/code/{registrationId} (e.g. http://localhost:8080/login/oauth2/code/github). If the callback URL registered in the provider console does not match this exact path, authentication will fail with a redirect URI mismatch error.

Q2: What is the principal object type injected by @AuthenticationPrincipal during an OAuth2 session?

During standard OAuth2 social login, the principal is an instance of OAuth2User (or OidcUser if using OpenID Connect with providers like Google). You can query attributes using principal.getAttribute("email").

Q3: How do you automatically create an internal database user when a customer logs in with Google for the first time?

Extend DefaultOAuth2UserService and override loadUser(OAuth2UserRequest). After invoking super.loadUser(userRequest) to fetch the user's profile from Google, check your local database by email: if the user does not exist, persist a new entity; if they exist, update their login timestamp.

Q4: What is the difference between an OAuth2 Client and an OAuth2 Resource Server in Spring Security?

An OAuth2 Client initiates login flows and exchanges authorization codes for access tokens (e.g. a web app with social login). An OAuth2 Resource Server receives incoming HTTP requests with Bearer <token> headers and validates tokens to protect API resources.

Q5: Why is spring-security-oauth2-client preferred over writing manual OAuth HTTP calls?

Spring Security's client automatically manages the entire authorization code exchange, state verification (preventing CSRF), token renewal via refresh tokens, user profile extraction, and integration into the SecurityContext with zero low level HTTP boilerplate.