Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ID-3333: Håndtere ren OAuth2 i OIDC demoklient #242

Merged
merged 4 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,3 @@ updates:
schedule:
interval: "daily"
open-pull-requests-limit: 10
- package-ecosystem: "maven" # See documentation for possible values
directory: "/" # Location of package manifests
registries:
- maven-github
schedule:
interval: "daily"
1 change: 1 addition & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
CVE-2024-22262 # springframework: URL Parsing with Host Validation
CVE-2024-34750 # tomcat: Improper Handling of Exceptional Conditions
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod;
import com.nimbusds.oauth2.sdk.pkce.CodeVerifier;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.openid.connect.sdk.*;
import com.nimbusds.openid.connect.sdk.claims.ACR;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
Expand All @@ -36,6 +37,7 @@
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;
import java.security.cert.Certificate;
import java.time.Clock;
Expand All @@ -55,49 +57,87 @@ public class OIDCIntegrationService {
private final ProtocolTracerService oidcProtocolTracerService;
private final FeatureSwichProperties featureSwichProperties;

public AuthenticationRequest authorzationRequest(AuthorizationRequest authorizationRequest) {
public com.nimbusds.oauth2.sdk.AuthorizationRequest authorizationRequest(AuthorizationRequest authorizationRequest) {
try {
AuthenticationRequest.Builder requestBuilder = new AuthenticationRequest.Builder(
new ResponseType(ResponseType.Value.CODE),
new Scope(authorizationRequest.getScopes().toArray(String[]::new)),
new ClientID(oidcIntegrationProperties.getClientId()),
oidcIntegrationProperties.getRedirectUri());
requestBuilder
.endpointURI(oidcProviderMetadata.getAuthorizationEndpointURI());
if (featureSwichProperties.isAuthorizationDetailsEnabled() && StringUtils.hasText(authorizationRequest.getAuthorizationDetails())) {
requestBuilder.customParameter("authorization_details", authorizationRequest.getAuthorizationDetails());
}
if (!CollectionUtils.isEmpty(authorizationRequest.getPrompt())) {
requestBuilder.prompt(new Prompt(authorizationRequest.getPrompt().toArray(String[]::new)));
}
if (!CollectionUtils.isEmpty(authorizationRequest.getUiLocales())) {
requestBuilder.uiLocales(authorizationRequest.getUiLocales().stream()
.map(this::langTag)
.filter(Objects::nonNull)
.collect(Collectors.toList()));
}
if (!CollectionUtils.isEmpty(authorizationRequest.getAcrValues())) {
requestBuilder.acrValues(authorizationRequest.getAcrValues().stream()
.map(acr -> new ACR(acr))
.collect(Collectors.toList()));
}
if (StringUtils.hasText(authorizationRequest.getState())) {
requestBuilder.state(new State(authorizationRequest.getState()));
}
if (StringUtils.hasText(authorizationRequest.getNonce())) {
requestBuilder.nonce(new Nonce(authorizationRequest.getNonce()));
}
if (StringUtils.hasText(authorizationRequest.getCodeVerifier())) {
requestBuilder.codeChallenge(
new CodeVerifier(authorizationRequest.getCodeVerifier()),
new CodeChallengeMethod(authorizationRequest.getCodeChallengeMethod()));
if (authorizationRequest.getScopes().contains("openid")) {
return oidcAuthorizationRequest(authorizationRequest);
}
return requestBuilder.build();
return oauth2AuthorizationRequest(authorizationRequest);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

protected com.nimbusds.oauth2.sdk.AuthorizationRequest oauth2AuthorizationRequest(AuthorizationRequest authorizationRequest) {
com.nimbusds.oauth2.sdk.AuthorizationRequest.Builder requestBuilder = new com.nimbusds.oauth2.sdk.AuthorizationRequest.Builder(
new ResponseType(ResponseType.Value.CODE),
new ClientID(oidcIntegrationProperties.getClientId()));
requestBuilder.scope(new Scope(authorizationRequest.getScopes().toArray(String[]::new)))
.redirectionURI(oidcIntegrationProperties.getRedirectUri())
.endpointURI(oidcProviderMetadata.getAuthorizationEndpointURI());

if (featureSwichProperties.isAuthorizationDetailsEnabled() && StringUtils.hasText(authorizationRequest.getAuthorizationDetails())) {
requestBuilder.customParameter("authorization_details", authorizationRequest.getAuthorizationDetails());
}
if (!CollectionUtils.isEmpty(authorizationRequest.getPrompt())) {
requestBuilder.prompt(new Prompt(authorizationRequest.getPrompt().toArray(String[]::new)));
}
if (!CollectionUtils.isEmpty(authorizationRequest.getUiLocales())) {
requestBuilder.customParameter("ui_locales", authorizationRequest.getUiLocales().toArray(String[]::new));
}
if (!CollectionUtils.isEmpty(authorizationRequest.getAcrValues())) {
requestBuilder.customParameter("acr_values", authorizationRequest.getAcrValues().toArray(String[]::new));
}
if (StringUtils.hasText(authorizationRequest.getState())) {
requestBuilder.state(new State(authorizationRequest.getState()));
}
if (StringUtils.hasText(authorizationRequest.getCodeVerifier())) {
requestBuilder.codeChallenge(
new CodeVerifier(authorizationRequest.getCodeVerifier()),
new CodeChallengeMethod(authorizationRequest.getCodeChallengeMethod()));
}
return requestBuilder.build();
}

protected AuthenticationRequest oidcAuthorizationRequest(AuthorizationRequest authorizationRequest) {
AuthenticationRequest.Builder requestBuilder = new AuthenticationRequest.Builder(
new ResponseType(ResponseType.Value.CODE),
new Scope(authorizationRequest.getScopes().toArray(String[]::new)),
new ClientID(oidcIntegrationProperties.getClientId()),
oidcIntegrationProperties.getRedirectUri());
requestBuilder
.endpointURI(oidcProviderMetadata.getAuthorizationEndpointURI());
if (featureSwichProperties.isAuthorizationDetailsEnabled() && StringUtils.hasText(authorizationRequest.getAuthorizationDetails())) {
requestBuilder.customParameter("authorization_details", authorizationRequest.getAuthorizationDetails());
}
if (!CollectionUtils.isEmpty(authorizationRequest.getPrompt())) {
requestBuilder.prompt(new Prompt(authorizationRequest.getPrompt().toArray(String[]::new)));
}
if (!CollectionUtils.isEmpty(authorizationRequest.getUiLocales())) {
requestBuilder.uiLocales(authorizationRequest.getUiLocales().stream()
.map(this::langTag)
.filter(Objects::nonNull)
.collect(Collectors.toList()));
}
if (!CollectionUtils.isEmpty(authorizationRequest.getAcrValues())) {
requestBuilder.acrValues(authorizationRequest.getAcrValues().stream()
.map(ACR::new)
.collect(Collectors.toList()));
}
if (StringUtils.hasText(authorizationRequest.getState())) {
requestBuilder.state(new State(authorizationRequest.getState()));
}
if (StringUtils.hasText(authorizationRequest.getNonce())) {
requestBuilder.nonce(new Nonce(authorizationRequest.getNonce()));
}
if (StringUtils.hasText(authorizationRequest.getCodeVerifier())) {
requestBuilder.codeChallenge(
new CodeVerifier(authorizationRequest.getCodeVerifier()),
new CodeChallengeMethod(authorizationRequest.getCodeChallengeMethod()));
}
return requestBuilder.build();
}

protected LangTag langTag(String locale) {
try {
return new LangTag(locale);
Expand All @@ -106,21 +146,26 @@ protected LangTag langTag(String locale) {
}
}

public OIDCTokenResponse token(AuthorizationSuccessResponse authorizationResponse, State state, Nonce nonce, CodeVerifier codeVerifier) {
public AccessTokenResponse token(AuthorizationSuccessResponse authorizationResponse, State state, Nonce nonce, CodeVerifier codeVerifier) {
try {
AuthorizationGrant codeGrant = new AuthorizationCodeGrant(authorizationResponse.toSuccessResponse().getAuthorizationCode(), oidcIntegrationProperties.getRedirectUri(), codeVerifier);
final ClientAuthentication clientAuth = clientAuthentication(oidcIntegrationProperties);
com.nimbusds.oauth2.sdk.TokenRequest tokenRequest = new com.nimbusds.oauth2.sdk.TokenRequest(oidcProviderMetadata.getTokenEndpointURI(), clientAuth, codeGrant);
com.nimbusds.oauth2.sdk.TokenResponse tokenResponse = process(tokenRequest);
if (tokenResponse.indicatesSuccess()) {
OIDCTokenResponse successResponse = (OIDCTokenResponse) tokenResponse.toSuccessResponse();
idTokenValidator.validate(successResponse.getOIDCTokens().getIDToken(), nonce);
return successResponse;
} else {
TokenErrorResponse errorResponse = tokenResponse.toErrorResponse();
log.warn("Error response from {}: {}", oidcProviderMetadata.getTokenEndpointURI(), errorResponse.toJSONObject().toJSONString());
throw new OIDCIntegrationException(errorResponse.getErrorObject().getCode() + ":" + errorResponse.getErrorObject().getDescription());
AuthorizationGrant codeGrant = new AuthorizationCodeGrant(authorizationResponse.toSuccessResponse().getAuthorizationCode(), oidcIntegrationProperties.getRedirectUri(), codeVerifier);
final ClientAuthentication clientAuth = clientAuthentication(oidcIntegrationProperties);
com.nimbusds.oauth2.sdk.TokenRequest tokenRequest = new com.nimbusds.oauth2.sdk.TokenRequest(oidcProviderMetadata.getTokenEndpointURI(), clientAuth, codeGrant);
com.nimbusds.oauth2.sdk.TokenResponse tokenResponse = process(tokenRequest);
if (tokenResponse.indicatesSuccess()) {
AccessTokenResponse accessTokenResponse = tokenResponse.toSuccessResponse();
if (accessTokenResponse instanceof OIDCTokenResponse) {
OIDCTokenResponse oidcTokenResponse = (OIDCTokenResponse) tokenResponse.toSuccessResponse();
if (oidcTokenResponse.getOIDCTokens().getIDToken() != null) {
idTokenValidator.validate(oidcTokenResponse.getOIDCTokens().getIDToken(), nonce);
}
}
return accessTokenResponse;
} else {
TokenErrorResponse errorResponse = tokenResponse.toErrorResponse();
log.warn("Error response from {}: {}", oidcProviderMetadata.getTokenEndpointURI(), errorResponse.toJSONObject().toJSONString());
throw new OIDCIntegrationException(errorResponse.getErrorObject().getCode() + ":" + errorResponse.getErrorObject().getDescription());
}
} catch (OIDCIntegrationException e) {
throw e;
} catch (Exception e) {
Expand All @@ -129,9 +174,9 @@ public OIDCTokenResponse token(AuthorizationSuccessResponse authorizationRespons
}
}

public String userinfo(OIDCTokenResponse oidcTokenResponse) {
public String userinfo(AccessToken accessToken) {
try {
UserInfoRequest userInfoRequest = new UserInfoRequest(oidcProviderMetadata.getUserInfoEndpointURI(), oidcTokenResponse.getOIDCTokens().getAccessToken());
UserInfoRequest userInfoRequest = new UserInfoRequest(oidcProviderMetadata.getUserInfoEndpointURI(), accessToken);
UserInfoResponse userInfoResponse = process(userInfoRequest);
if (userInfoResponse.indicatesSuccess()) {
return userInfoResponse.toSuccessResponse().getUserInfo().toJSONObject().toJSONString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.nimbusds.jwt.JWT;
import com.nimbusds.oauth2.sdk.AccessTokenResponse;
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod;
import com.nimbusds.oauth2.sdk.pkce.CodeVerifier;
Expand Down Expand Up @@ -68,9 +69,11 @@ public String authorize(@ModelAttribute AuthorizationRequest authorizationReques
if (StringUtils.hasText(authorizationRequest.getCodeVerifier())) {
request.getSession().setAttribute("code_verifier", new CodeVerifier(authorizationRequest.getCodeVerifier()));
}
AuthenticationRequest authenticationRequest = oidcIntegrationService.authorzationRequest(authorizationRequest);
com.nimbusds.oauth2.sdk.AuthorizationRequest authenticationRequest = oidcIntegrationService.authorizationRequest(authorizationRequest);
request.getSession().setAttribute("state", authenticationRequest.getState());
request.getSession().setAttribute("nonce", authenticationRequest.getNonce());
if (authenticationRequest instanceof AuthenticationRequest) {
request.getSession().setAttribute("nonce", ((AuthenticationRequest) authenticationRequest).getNonce());
}
protocolTracerService.traceAuthorizationRequest(request.getSession(), authenticationRequest.toURI());
return "redirect:" + authenticationRequest.toURI().toString();
}
Expand All @@ -87,17 +90,20 @@ public String callback(HttpServletRequest request, HttpServletResponse response,
if (authorizationResponse.indicatesSuccess()) {
final Nonce nonce = (Nonce) request.getSession().getAttribute("nonce");
final CodeVerifier codeVerifier = (CodeVerifier) request.getSession().getAttribute("code_verifier");
OIDCTokenResponse oidcTokenResponse = oidcIntegrationService.token(authorizationResponse.toSuccessResponse(), state, nonce, codeVerifier);
protocolTracerService.traceValidatedIdToken(request.getSession(), oidcTokenResponse.getOIDCTokens().getIDToken().getJWTClaimsSet());
if (oidcTokenResponse.getOIDCTokens().getAccessToken() != null) {
AccessToken accessToken = oidcTokenResponse.getOIDCTokens().getAccessToken();
AccessTokenResponse tokenResponse = oidcIntegrationService.token(authorizationResponse.toSuccessResponse(), state, nonce, codeVerifier);
AccessToken accessToken = tokenResponse.getTokens().getAccessToken();
if (accessToken != null && accessToken.getScope() != null && accessToken.getScope().contains("openid")) {
OIDCTokenResponse oidcTokenResponse = (OIDCTokenResponse) tokenResponse;
protocolTracerService.traceValidatedIdToken(request.getSession(), oidcTokenResponse.getOIDCTokens().getIDToken().getJWTClaimsSet());
request.getSession().setAttribute("id_token", oidcTokenResponse.getOIDCTokens().getIDToken());
model.addAttribute("personIdentifier", oidcTokenResponse.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim(themeProperties.getUserIdClaim()));
}
if (tokenResponse.getTokens().getAccessToken() != null) {
protocolTracerService.traceBearerAccessToken(request.getSession(), accessToken.getValue());
if (accessToken.getScope() != null && accessToken.getScope().contains("profile")) {
oidcIntegrationService.userinfo(oidcTokenResponse);
oidcIntegrationService.userinfo(accessToken);
}
}
request.getSession().setAttribute("id_token", oidcTokenResponse.getOIDCTokens().getIDToken());
model.addAttribute("personIdentifier", oidcTokenResponse.getOIDCTokens().getIDToken().getJWTClaimsSet().getClaim(themeProperties.getUserIdClaim()));
return "idtoken";
} else {
log.warn("Error authorization response: {}", authorizationResponse.toErrorResponse().getErrorObject().toJSONObject().toJSONString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.PlainJWT;
import com.nimbusds.oauth2.sdk.AuthorizationSuccessResponse;
import com.nimbusds.oauth2.sdk.Scope;
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.oauth2.sdk.pkce.CodeVerifier;
import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
Expand Down Expand Up @@ -185,7 +186,7 @@ public void testRetrieveTokens() throws Exception {
Nonce nonce = new Nonce();
CodeVerifier codeVerifier = new CodeVerifier();
JWT idToken = new PlainJWT(TestDataUtils.idTokenClaimsSet(TestDataUtils.testUserPersonIdentifier()));
OIDCTokenResponse tokenResponse = new OIDCTokenResponse(new OIDCTokens(idToken, new BearerAccessToken("at"), null));
OIDCTokenResponse tokenResponse = new OIDCTokenResponse(new OIDCTokens(idToken, new BearerAccessToken("at", 120, new Scope("openid")), null));
doReturn(tokenResponse).when(oidcIntegrationService).token(any(AuthorizationSuccessResponse.class), eq(state), eq(nonce), eq(codeVerifier));
MockHttpSession mockSession = new MockHttpSession();
ProtocolTracerService.create(mockSession);
Expand Down