Skip to content

Commit 9b59471

Browse files
committed
Fix broken OAUTH2.0 authorization_code flow.
When configuring an Oauth2.0 provider that returns opaque access tokens, the UAA throws an error trying to parse it as it assumes it is a JWT id_token Easy to reproduce by adding Github as an OAuth provider in uaa.yml login: oauth: providers: github: type: oauth2.0 authUrl: https://github.com/login/oauth/authorize tokenUrl: https://github.com/login/oauth/access_token userInfoUrl: https://api.github.com/user issuer: https://github.com relyingPartyId: <your-github-app-client-id> relyingPartySecret: <your-github-app-client-secret> performRpInitiatedLogout: false scopes: - openid linkText: Login with Github showLinkText: true attributeMappings: user_name: login clientAuthInBody: true externalGroupsWhitelist: - "*"
1 parent 7e671a0 commit 9b59471

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
import static java.util.Collections.singletonMap;
117117
import static java.util.Objects.isNull;
118118
import static java.util.stream.Collectors.toSet;
119+
import static org.cloudfoundry.identity.uaa.constants.OriginKeys.OAUTH20;
119120
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.SUB;
120121
import static org.cloudfoundry.identity.uaa.oauth.token.CompositeToken.ID_TOKEN;
121122
import static org.cloudfoundry.identity.uaa.oauth.token.TokenConstants.GRANT_TYPE_AUTHORIZATION_CODE;
@@ -609,9 +610,14 @@ protected <T extends AbstractExternalOAuthIdentityProviderDefinition<T>> Map<Str
609610
ExternalOAuthCodeToken codeToken,
610611
final IdentityProvider<T> identityProvider
611612
) {
612-
String idToken = getTokenFromCode(codeToken, identityProvider);
613-
codeToken.setIdToken(idToken);
614-
return getClaimsFromToken(idToken, identityProvider);
613+
String tokenFieldName = getTokenFieldName(identityProvider.getConfig());
614+
String token = getTokenFromCode(codeToken, identityProvider);
615+
if ("access_token".equals(tokenFieldName) && token != null && OAUTH20.equals(identityProvider.getType())) {
616+
codeToken.setAccessToken(token);
617+
} else {
618+
codeToken.setIdToken(token);
619+
}
620+
return getClaimsFromToken(token, identityProvider);
615621
}
616622

617623
protected <T extends AbstractExternalOAuthIdentityProviderDefinition<T>> Map<String, Object> getClaimsFromToken(

server/src/test/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManagerTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.cloudfoundry.identity.uaa.provider.IdentityProvider;
3131
import org.cloudfoundry.identity.uaa.provider.IdentityProviderProvisioning;
3232
import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition;
33+
import org.cloudfoundry.identity.uaa.provider.RawExternalOAuthIdentityProviderDefinition;
3334
import org.cloudfoundry.identity.uaa.scim.ScimGroupExternalMember;
3435
import org.cloudfoundry.identity.uaa.scim.jdbc.JdbcScimGroupExternalMembershipManager;
3536
import org.cloudfoundry.identity.uaa.user.UaaUser;
@@ -40,6 +41,7 @@
4041
import org.cloudfoundry.identity.uaa.util.UaaTokenUtils;
4142
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
4243
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
44+
import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager;
4345
import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManagerImpl;
4446
import org.junit.jupiter.api.AfterEach;
4547
import org.junit.jupiter.api.BeforeEach;
@@ -83,6 +85,7 @@
8385
import static org.assertj.core.api.Assertions.assertThatNoException;
8486
import static org.assertj.core.api.Assertions.assertThatThrownBy;
8587
import static org.assertj.core.api.Assertions.fail;
88+
import static org.cloudfoundry.identity.uaa.constants.OriginKeys.OAUTH20;
8689
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.AUD;
8790
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.EMAIL;
8891
import static org.cloudfoundry.identity.uaa.oauth.token.ClaimConstants.EXPIRY_IN_SECONDS;
@@ -1083,6 +1086,49 @@ void oidcPasswordGrantWithPrompts() throws MalformedURLException, JOSEException
10831086
assertThat(headers).doesNotContainKey("X-Forwarded-For");
10841087
}
10851088

1089+
@Test
1090+
void oauth20AuthorizationFlowWithUserInfo() throws Exception {
1091+
1092+
final ExternalOAuthCodeToken codeToken = new ExternalOAuthCodeToken("thecode", "some-origin", "http://google.com", null, null, "signedrequest");
1093+
final RestTemplate restTemplate = mock(RestTemplate.class);
1094+
final ResponseEntity<Map<String, Object>> responseEntity = mock(ResponseEntity.class);
1095+
when(responseEntity.getStatusCode()).thenReturn(HttpStatus.OK);
1096+
when(responseEntity.getBody()).thenReturn(Collections.emptyMap());
1097+
when(restTemplate.exchange(any(URI.class), eq(HttpMethod.GET), any(HttpEntity.class), any(ParameterizedTypeReference.class))).thenReturn(responseEntity);
1098+
authManager = new ExternalOAuthAuthenticationManager(
1099+
identityProviderProvisioning,
1100+
mock(IdentityZoneManager.class),
1101+
restTemplate,
1102+
restTemplate,
1103+
tokenEndpointBuilder,
1104+
new KeyInfoService("http://uaa.example.com"),
1105+
null
1106+
) {
1107+
@Override
1108+
protected <T extends AbstractExternalOAuthIdentityProviderDefinition<T>> String getTokenFromCode(
1109+
ExternalOAuthCodeToken codeToken,
1110+
IdentityProvider<T> config
1111+
) {
1112+
return "opaque-access-token";
1113+
}
1114+
1115+
@Override
1116+
public RestTemplate getRestTemplate(AbstractExternalOAuthIdentityProviderDefinition config) {
1117+
return restTemplate;
1118+
}
1119+
};
1120+
final RawExternalOAuthIdentityProviderDefinition config = new RawExternalOAuthIdentityProviderDefinition();
1121+
config.setResponseType("code");
1122+
config.setUserInfoUrl(URI.create("https://some.metadata.url/userinfo").toURL());
1123+
final IdentityProvider<AbstractExternalOAuthIdentityProviderDefinition> idp = new IdentityProvider<>();
1124+
idp.setType(OAUTH20);
1125+
idp.setConfig(config);
1126+
authManager.getClaimsFromToken(codeToken, idp);
1127+
assertThat(codeToken.getIdToken()).isNull();
1128+
assertThat(codeToken.getAccessToken()).isEqualTo("opaque-access-token");
1129+
1130+
}
1131+
10861132
private static void assertAuthorizationHeaderIsSetAndStartsWithBasic(final HttpHeaders headers) {
10871133
assertThat(headers).containsKey("Authorization");
10881134
final List<String> authorizationHeaders = headers.get("Authorization");

0 commit comments

Comments
 (0)