Wiring Sign in with Google into Spring Boot

OAuth with Google · lesson 6 of 9 · 6 min read

From an empty project to a working login.

Open this lesson in the learning hub

Key points

  • Add spring-boot-starter-oauth2-client and a registration under spring.security.oauth2.client.registration.google. Spring already knows Google endpoints, so no URLs are needed.
  • Enable oauth2Login() on the filter chain. That alone gives you a login page, the redirect, the code exchange, state and nonce validation, and a populated principal.
  • The redirect URI Spring uses by default is /login/oauth2/code/google. It must be registered in the Google console exactly, including scheme and port.
  • The user arrives as an OidcUser. Read identity from getSubject() - the stable sub - and never from the email, which can change.
  • Use a OAuth2UserService to map the Google user onto your own account record on first login, so authorisation uses your roles rather than Google claims.
  • Behind a proxy, set server.forward-headers-strategy=framework or the redirect will be built with the internal host and fail the exact-match check.

Example

# build.gradle
#   implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: ${GOOGLE_CLIENT_ID}
            client-secret: ${GOOGLE_CLIENT_SECRET}
            scope: openid,profile,email
# Provider details are built in - no URLs needed for Google.

# Behind a reverse proxy, or the redirect_uri is built wrong:
server:
  forward-headers-strategy: framework

---
@Configuration
@EnableWebSecurity
class GoogleLoginConfig {

    @Bean
    SecurityFilterChain web(HttpSecurity http, OAuth2UserService<OidcUserRequest,
            OidcUser> users) throws Exception {
        return http
                .authorizeHttpRequests(a -> a
                        .requestMatchers("/", "/error", "/webjars/**").permitAll()
                        .anyRequest().authenticated())
                .oauth2Login(o -> o
                        .userInfoEndpoint(u -> u.oidcUserService(users))
                        .defaultSuccessUrl("/dashboard", true))
                .logout(l -> l.logoutSuccessUrl("/").invalidateHttpSession(true))
                .build();
    }
}

// Map the Google identity onto YOUR account on first login.
@Service
class GoogleUserService implements OAuth2UserService<OidcUserRequest, OidcUser> {

    private final OidcUserService delegate = new OidcUserService();
    private final AccountRepository accounts;

    GoogleUserService(AccountRepository accounts) { this.accounts = accounts; }

    @Override
    public OidcUser loadUser(OidcUserRequest request) {
        OidcUser google = delegate.loadUser(request);

        if (!Boolean.TRUE.equals(google.getEmailVerified())) {
            throw new OAuth2AuthenticationException("Email not verified");
        }

        // sub is STABLE. Email is not - people change it.
        Account account = accounts.findByGoogleSub(google.getSubject())
                .orElseGet(() -> accounts.save(Account.fromGoogle(google)));

        // Authorities come from YOUR roles, not from Google.
        return new DefaultOidcUser(account.authorities(),
                google.getIdToken(), google.getUserInfo(), "sub");
    }
}

oauth2Login gives you the whole flow - your job is mapping the stable sub onto your own account and using your own roles.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OAuth with Google course, and every lesson in it is listed on the OAuth with Google contents page.