Skip to content

Commit 133eb0e

Browse files
committed
Require c_hash for the hybrid case
1 parent 3d6f726 commit 133eb0e

4 files changed

Lines changed: 88 additions & 1 deletion

File tree

rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/idp/IdTokenResponseFilter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public void process(ClientAccessToken ct, ServerAccessToken st) {
5656
if (ct.getApprovedScope() == null || !ct.getApprovedScope().contains(OidcUtils.OPENID_SCOPE)) {
5757
return;
5858
}
59+
if (st.getResponseType() != null) {
60+
ct.getParameters().put(OAuthConstants.RESPONSE_TYPE, st.getResponseType());
61+
}
5962
String idToken = getProcessedIdToken(st);
6063
if (idToken != null) {
6164
ct.getParameters().put(OidcUtils.ID_TOKEN, idToken);

rt/rs/security/sso/oidc/src/main/java/org/apache/cxf/rs/security/oidc/rp/IdTokenReader.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.cxf.rs.security.jose.jwt.JwtToken;
2222
import org.apache.cxf.rs.security.oauth2.client.Consumer;
2323
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
24+
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
2425
import org.apache.cxf.rs.security.oidc.common.IdToken;
2526
import org.apache.cxf.rs.security.oidc.utils.OidcUtils;
2627

@@ -44,7 +45,11 @@ public JwtToken getIdJwtToken(ClientAccessToken at, String code, Consumer client
4445
String idJwtToken = at.getParameters().get(OidcUtils.ID_TOKEN);
4546
JwtToken jwt = getIdJwtToken(idJwtToken, client);
4647
OidcUtils.validateAccessTokenHash(at, jwt, requireAtHash);
47-
OidcUtils.validateCodeHash(code, jwt, requireCodeHash);
48+
if (code != null) {
49+
// The spec requires c_hash to be present in the id_token for hybrid flows,
50+
// but we allow it to be optional for token endpoint id_tokens
51+
OidcUtils.validateCodeHash(code, jwt, requireCodeHash || isHybridFlow(at));
52+
}
4853
return jwt;
4954
}
5055
public JwtToken getIdJwtToken(ClientAccessToken at, Consumer client) {
@@ -55,6 +60,13 @@ public JwtToken getIdJwtToken(String idJwtToken, Consumer client) {
5560
validateJwtClaims(jwt.getClaims(), client.getClientId(), true);
5661
return jwt;
5762
}
63+
64+
private boolean isHybridFlow(ClientAccessToken at) {
65+
String responseType = at.getParameters().get(OAuthConstants.RESPONSE_TYPE);
66+
return OidcUtils.CODE_ID_TOKEN_RESPONSE_TYPE.equals(responseType)
67+
|| OidcUtils.CODE_ID_TOKEN_AT_RESPONSE_TYPE.equals(responseType);
68+
}
69+
5870
private IdToken getIdTokenFromJwt(JwtToken jwt) {
5971
return new IdToken(jwt.getClaims().asMap());
6072
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.cxf.rs.security.oidc.rp;
20+
21+
import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
22+
import org.apache.cxf.rs.security.jose.jwt.JwtToken;
23+
import org.apache.cxf.rs.security.oauth2.client.Consumer;
24+
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
25+
import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException;
26+
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
27+
import org.apache.cxf.rs.security.oidc.utils.OidcUtils;
28+
29+
import org.junit.Test;
30+
31+
import static org.junit.Assert.assertNotNull;
32+
33+
public class IdTokenReaderTest {
34+
35+
@Test
36+
public void testCodeHashIsOptionalByDefaultForTokenEndpointIdToken() {
37+
IdTokenReader idTokenReader = new StubIdTokenReader(new JwtToken(new JwtClaims()));
38+
idTokenReader.setRequireAccessTokenHash(false);
39+
40+
ClientAccessToken accessToken = new ClientAccessToken("Bearer", "access-token");
41+
accessToken.getParameters().put(OidcUtils.ID_TOKEN, "id-token");
42+
43+
assertNotNull(idTokenReader.getIdJwtToken(accessToken, "auth-code", new Consumer("client-id")));
44+
}
45+
46+
@Test(expected = OAuthServiceException.class)
47+
public void testCodeHashIsRequiredByDefaultForHybridTokenEndpointIdToken() {
48+
IdTokenReader idTokenReader = new StubIdTokenReader(new JwtToken(new JwtClaims()));
49+
idTokenReader.setRequireAccessTokenHash(false);
50+
51+
ClientAccessToken accessToken = new ClientAccessToken("Bearer", "access-token");
52+
accessToken.getParameters().put(OidcUtils.ID_TOKEN, "id-token");
53+
accessToken.getParameters().put(OAuthConstants.RESPONSE_TYPE, OidcUtils.CODE_ID_TOKEN_RESPONSE_TYPE);
54+
55+
idTokenReader.getIdJwtToken(accessToken, "auth-code", new Consumer("client-id"));
56+
}
57+
58+
private static final class StubIdTokenReader extends IdTokenReader {
59+
private final JwtToken jwt;
60+
61+
private StubIdTokenReader(JwtToken jwt) {
62+
this.jwt = jwt;
63+
}
64+
65+
@Override
66+
public JwtToken getIdJwtToken(String idJwtToken, Consumer client) {
67+
return jwt;
68+
}
69+
}
70+
}

systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCFlowTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,8 @@ public void testHybridCodeIdToken() throws Exception {
672672
OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code);
673673
assertNotNull(accessToken.getTokenKey());
674674
assertTrue(accessToken.getApprovedScope().contains("openid"));
675+
assertEquals(OidcUtils.CODE_ID_TOKEN_RESPONSE_TYPE,
676+
accessToken.getParameters().get(OAuthConstants.RESPONSE_TYPE));
675677

676678
// Check id_token from the token endpoint
677679
idToken = accessToken.getParameters().get("id_token");

0 commit comments

Comments
 (0)