-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
management-service: oauth redesigned to use id token instead of auth …
…token (#143) * management-service: oauth redesigned to use id token instead of auth token * management-service: final version of oauth :) * management-service: refactor * management-service: refactor * management-service: refactor * management-service: removed unused imports * management-service: refactor
- Loading branch information
Showing
16 changed files
with
271 additions
and
412 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...r/zpi/auth/CustomAccessDeniedHandler.java → ...uth/oauth2/CustomAccessDeniedHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 0 additions & 47 deletions
47
management-service/src/main/java/pl/pwr/zpi/auth/oauth2/CustomOAuth2User.java
This file was deleted.
Oops, something went wrong.
29 changes: 0 additions & 29 deletions
29
management-service/src/main/java/pl/pwr/zpi/auth/oauth2/CustomOAuth2UserService.java
This file was deleted.
Oops, something went wrong.
96 changes: 96 additions & 0 deletions
96
management-service/src/main/java/pl/pwr/zpi/auth/oauth2/GoogleOAuthLoginSuccessHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package pl.pwr.zpi.auth.oauth2; | ||
|
||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.ResponseCookie; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; | ||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; | ||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; | ||
import org.springframework.security.oauth2.core.OAuth2RefreshToken; | ||
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; | ||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; | ||
import org.springframework.stereotype.Component; | ||
import pl.pwr.zpi.security.cookie.CookieService; | ||
import pl.pwr.zpi.user.data.User; | ||
import pl.pwr.zpi.user.dto.Provider; | ||
import pl.pwr.zpi.user.service.UserService; | ||
|
||
import java.io.IOException; | ||
import java.time.Instant; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class GoogleOAuthLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { | ||
|
||
@Value("${oauth2.google.redirect-uri}") | ||
private String redirectUri; | ||
|
||
private final UserService userService; | ||
private final OAuth2AuthorizedClientService authorizedClientService; | ||
private final CookieService cookieService; | ||
private final GoogleOauthTokenService googleOauthTokenService; | ||
|
||
@Override | ||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { | ||
if (!(authentication instanceof OAuth2AuthenticationToken)) { | ||
throw new ServletException("Unsupported authentication type."); | ||
} | ||
|
||
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication; | ||
String registrationId = oauthToken.getAuthorizedClientRegistrationId(); | ||
if (registrationId == null) { | ||
throw new ServletException("Missing registration ID."); | ||
} | ||
|
||
OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient(registrationId, authentication.getName()); | ||
DefaultOidcUser oidcUser = (DefaultOidcUser) authentication.getPrincipal(); | ||
OAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken(); | ||
|
||
if (refreshToken == null) { | ||
throw new RuntimeException("Refresh token is null. Please contact the administrator."); | ||
} | ||
|
||
addCookiesToResponse(response, oidcUser.getIdToken().getTokenValue(), oidcUser.getIdToken().getExpiresAt(), refreshToken.getTokenValue()); | ||
|
||
createOrUpdateUser(oidcUser); | ||
|
||
getRedirectStrategy().sendRedirect(request, response, redirectUri); | ||
} | ||
|
||
private void addCookiesToResponse(HttpServletResponse response, String authToken, Instant expiresAt, String refreshToken) { | ||
ResponseCookie authCookie = cookieService.createAuthCookie(authToken, expiresAt); | ||
ResponseCookie refreshCookie = cookieService.createRefreshCookie(refreshToken); | ||
|
||
response.addHeader("Set-Cookie", authCookie.toString()); | ||
response.addHeader("Set-Cookie", refreshCookie.toString()); | ||
} | ||
|
||
private void createOrUpdateUser(DefaultOidcUser oidcUser) { | ||
Payload payload = googleOauthTokenService.decodeToken(oidcUser.getIdToken().getTokenValue()); | ||
|
||
String email = payload.getEmail(); | ||
String nickname = (String) payload.get("name"); | ||
String fallbackNickname = nickname != null ? nickname : email.split("@")[0]; | ||
|
||
if (userService.findByEmail(email).isEmpty()) { | ||
saveNewUser(email, fallbackNickname); | ||
} | ||
} | ||
|
||
private void saveNewUser(String email, String nickname) { | ||
userService.saveUser( | ||
User.builder() | ||
.email(email) | ||
.nickname(nickname) | ||
.provider(Provider.GOOGLE) | ||
.build() | ||
); | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
management-service/src/main/java/pl/pwr/zpi/auth/oauth2/GoogleOauthTokenService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package pl.pwr.zpi.auth.oauth2; | ||
|
||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken; | ||
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier; | ||
import com.google.api.client.googleapis.auth.oauth2.GoogleRefreshTokenRequest; | ||
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; | ||
import com.google.api.client.http.javanet.NetHttpTransport; | ||
import com.google.api.client.json.gson.GsonFactory; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.http.ResponseCookie; | ||
import org.springframework.stereotype.Service; | ||
import pl.pwr.zpi.security.cookie.CookieService; | ||
import pl.pwr.zpi.utils.exception.AuthenticationException; | ||
|
||
import java.io.IOException; | ||
import java.security.GeneralSecurityException; | ||
import java.time.Instant; | ||
import java.util.Collections; | ||
|
||
@Service | ||
@Slf4j | ||
public class GoogleOauthTokenService { | ||
|
||
private final String googleClientId; | ||
private final String googleClientSecret; | ||
private final CookieService cookieService; | ||
private final GsonFactory jsonFactory; | ||
private final NetHttpTransport transport; | ||
private final GoogleIdTokenVerifier verifier; | ||
|
||
public GoogleOauthTokenService(@Value("${spring.security.oauth2.client.registration.google.client-id}") String googleClientId, | ||
@Value("${spring.security.oauth2.client.registration.google.client-secret}") String googleClientSecret, | ||
CookieService cookieService) { | ||
this.cookieService = cookieService; | ||
this.googleClientId = googleClientId; | ||
this.googleClientSecret = googleClientSecret; | ||
this.jsonFactory = GsonFactory.getDefaultInstance(); | ||
this.transport = new NetHttpTransport(); | ||
this.verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory) | ||
.setAudience(Collections.singletonList(googleClientId)) | ||
.build(); | ||
} | ||
|
||
public ResponseCookie updateAuthToken(String refreshToken) { | ||
var refreshedIdToken = refreshIdToken(refreshToken); | ||
return cookieService.createAuthCookie(refreshedIdToken.getIdToken(), Instant.ofEpochSecond(refreshedIdToken.getExpiresInSeconds())); | ||
} | ||
|
||
public GoogleIdToken.Payload decodeToken(String token) { | ||
try { | ||
GoogleIdToken idToken = verifier.verify(token); | ||
if (idToken == null) { | ||
throw new AuthenticationException("Token validation failed"); | ||
} | ||
return idToken.getPayload(); | ||
} catch (GeneralSecurityException | IOException e) { | ||
throw new AuthenticationException("Token validation failed"); | ||
} | ||
} | ||
|
||
public void validateToken(String token) throws GeneralSecurityException, IOException { | ||
verifier.verify(token); | ||
} | ||
|
||
private GoogleTokenResponse refreshIdToken(String refreshToken) { | ||
try { | ||
return new GoogleRefreshTokenRequest( | ||
transport, | ||
jsonFactory, | ||
refreshToken, | ||
googleClientId, | ||
googleClientSecret) | ||
.execute(); | ||
|
||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
|
||
} | ||
} |
100 changes: 0 additions & 100 deletions
100
management-service/src/main/java/pl/pwr/zpi/auth/oauth2/OAuthLoginSuccessHandler.java
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.