-
step1 : backend project setup
-
step2: frontend at different origin so CORS
-
adding Jwt filters
-
Oauth concept -pending
-
Security : access_token and refresh_token both only access by one user only / self only
http://localhost:{9081==PORT}/swagger-ui/index.html#/
https://crimson-comet-847628.postman.co/workspace/springBoot-fitness~3624bf6f-c3b5-4845-b04e-6d7f7fcd62fc/collection/25455646-cd70689a-dba5-41cc-8c5e-2caa1c08674c?action=share&creator=25455646
[TODO] Oauth with google/github server----------do it rightnow
This project implements OAuth2 authentication using Google and GitHub in a Spring Boot backend.
It follows a Backend-as-a-Service (BaaS) pattern:
- No frontend token handling logic
- Backend manages OAuth flow
- Backend generates JWT tokens
- Backend acts as:
- OAuth2 Client
- Internal JWT Authorization Server
- Resource Server (secured APIs)
User
β
Browser (React or any client)
β
Spring Boot Backend
β
Google / GitHub (OAuth Provider)
| Component | Role |
|---|---|
| Browser / React | Triggers login |
| Spring Boot | OAuth Client + JWT Issuer + Resource Server |
| Google / GitHub | External Authentication Provider |
| JWT | Secures APIs |
1. SecurityConfig β stateless, permit /auth/**, filter chain
2. JwtService β generate / validate / extract claims
3. JwtAuthFilter β OncePerRequestFilter, reads Bearer token
4. UserDetailsServiceImpl β loadUserByUsername via email
5. AuthService β authenticate, persist refresh, generate tokens
6. AuthController β POST /api/v1/auth/loginClient β POST /login (email, password)
β AuthController
β AuthService.login()
β AuthenticationManager.authenticate() [1. verify credentials]
β UserDetailsServiceImpl.loadUserByUsername() [2. load from DB]
β RefreshTokenRepository.save() [3. persist jti to DB]
β JwtService.generateAccessToken() [4. sign access JWT]
β JwtService.generateRefreshToken(jti) [5. sign refresh JWT]
β CookieService.attachRefreshTokenCookie() [6. HttpOnly cookie]
β LoginResponse(accessToken, expiresIn, "Bearer")
Every subsequent request:
Client β GET /any (Authorization: Bearer <token>)
β JwtAuthFilter
β JwtService.extractEmail()
β UserDetailsServiceImpl.loadUserByUsername()
β SecurityContextHolder.setAuthentication()
β Controller (authenticated)1. SecurityConfig β add oauth2Login, successHandler, failureHandler
2. OAuth2UserInfo β abstract, normalize Google/GitHub provider data
3. GoogleOAuth2UserInfo β extract fields from Google attributes
4. GithubOAuth2UserInfo β extract fields from GitHub attributes
5. CustomOAuth2UserService β loadUser, upsert User in DB
6. OAuth2SuccessHandler β generate JWT, attach cookie, redirect
7. OAuth2FailureHandler β redirect with error paramClient β GET /oauth2/authorize/google
β Spring redirects to Google consent screen
Google β GET /login/oauth2/code/google?code=xxx
β CustomOAuth2UserService.loadUser()
β fetch attributes from Google
β OAuth2UserInfoFactory.getOAuth2UserInfo() [normalize provider data]
β userRepository.findByEmail()
β EXISTS β update name/image [returning user]
β NOT EXISTS β create new User [first time OAuth]
β OAuth2SuccessHandler.onAuthenticationSuccess()
β RefreshTokenRepository.save(jti)
β JwtService.generateAccessToken()
β JwtService.generateRefreshToken(jti)
β CookieService.attachRefreshTokenCookie()
β redirect β frontend/dashboard?accessToken=xxx
Client (subsequent requests same as JWT flow)
β JwtAuthFilter β SecurityContext β ControllerOAuth2 β "Can I access your Google data?" β gives you ACCESS TOKEN
OIDC β "Who are you?" β gives you ID TOKEN (JWT with user claims)
OAuth2UserService β calls /userinfo endpoint separately to get user data
OidcUserService β ID token already HAS user claims (sub, email, name, picture)
no extra HTTP call needed β faster + standardCustomOAuth2UserService β CustomOidcUserService
extends DefaultOAuth2UserService β extends OidcUserService
OAuth2User return type β OidcUser return type
manual attribute extraction β standardClaims() built-in
SecurityConfig
.userService(customOAuth2UserService) β .oidcUserService(customOidcUserService)
application.yml
scope: email, profile β scope: openid, email, profile β openid triggers OIDCClient β GET /oauth2/authorize/google
β Spring appends scope=openid β triggers OIDC flow
Google β returns ID TOKEN (JWT) + optional access token
β Spring validates ID token signature (via Google JWKS endpoint)
β Spring calls CustomOidcUserService.loadUser()
β OidcUserInfo has standardClaims() β no extra /userinfo call
β upsert User in DB same as before
β OAuth2SuccessHandler (unchanged)
β generate JWT, attach cookie, redirectOAuth2 (GitHub) OIDC (Google)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
scope user:email, read:user openid, email, profile
token returned access token only ID token (JWT) + access token
user data extra /userinfo HTTP call already in ID token claims
signature verify no yes β Spring checks Google JWKS
your service CustomOAuth2UserService CustomOidcUserService
principal type OAuth2User OidcUser
email extracted attributes.get("email") oidcUser.getEmail()
sub claim attributes.get("id") oidcUser.getSubject()POST /api/v1/auth/register β email/password β JWT
POST /api/v1/auth/login β email/password β JWT
GET /oauth2/authorize/google β OIDC β ID token validated β JWT
GET /oauth2/authorize/github β OAuth2 β /userinfo call β JWT
All three end up at OAuth2SuccessHandler or AuthService
β same RefreshToken table
β same JwtService
β same HttpOnly cookie + accessToken response-
User clicks Login with Google/GitHub
-
Browser redirects to:
/oauth2/authorization/google -
Spring Boot redirects to Google/GitHub
-
User authenticates
-
Provider redirects to:
${api_domain:http://localhost}:${PORT}/login/oauth2/code/google -
Spring Boot:
- Receives authorization code
- Exchanges code for access token
- Fetches user info
- Creates or updates user in DB
- Generates:
- JWT Access Token
- JWT Refresh Token
- Stores refresh token in DB
- Sends:
- Access token in redirect URL
- Refresh token as HttpOnly cookie
Configure in Google/GitHub console:
http://localhost:9081/login/oauth2/code/google
http://localhost:9081/login/oauth2/code/github
Configured using:
spring:
security:
oauth2:
client:
registration:
google:
client-id: YOUR_GOOGLE_CLIENT_ID
client-secret: YOUR_GOOGLE_CLIENT_SECRETAfter successful OAuth login:
- Generates Access Token
- Generates Refresh Token
- Stores refresh token in database
All secured APIs validate:
- JWT Access Token
- Token expiry
- User roles
| Token | Storage | TTL |
|---|---|---|
| Access Token | Frontend (memory) | Short-lived |
| Refresh Token | HttpOnly Cookie + DB | Long-lived |
- id
- name
- provider
- providerId
- roles
- id
- jti
- user_id
- revoked
- expires_at
| Provider | Endpoint |
|---|---|
/oauth2/authorization/google |
|
| GitHub | /oauth2/authorization/github |
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>- Set
cookie-secure: true - Use HTTPS
- Rotate JWT secret
- Implement refresh token revocation
- Add role-based authorization
- Enable correlation ID logging
β Uses Google & GitHub as authentication providers
β Issues its own JWT tokens
β Stores refresh tokens securely
β Acts as OAuth client and resource server
β No frontend token logic required