From c166cbe49c313b58cd674fe96e6fa4f56c9ffde0 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Tue, 23 Jun 2015 21:21:41 -0400 Subject: [PATCH] added login hint capability to client library --- .../client/OIDCAuthenticationFilter.java | 2 +- .../client/service/AuthRequestUrlBuilder.java | 5 ++- .../impl/EncryptedAuthRequestUrlBuilder.java | 8 +++- .../impl/PlainAuthRequestUrlBuilder.java | 8 +++- .../impl/SignedAuthRequestUrlBuilder.java | 8 +++- .../service/impl/WebfingerIssuerService.java | 2 +- .../impl/TestPlainAuthRequestUrlBuilder.java | 24 +++++++++- .../impl/TestSignedAuthRequestUrlBuilder.java | 45 ++++++++++++++++++- 8 files changed, 92 insertions(+), 10 deletions(-) diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java index ebf058cdd..2e6c091f3 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java @@ -262,7 +262,7 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi Map options = authOptions.getOptions(serverConfig, clientConfig, request); - String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options); + String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, issResp.getLoginHint()); logger.debug("Auth Request: " + authRequest); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java index 771714e3c..723ec1ee1 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java @@ -25,6 +25,8 @@ import org.mitre.oauth2.model.RegisteredClient; import org.mitre.openid.connect.config.ServerConfiguration; /** + * Builds a URL string to the IdP's authorization endpoint. + * * @author jricher * */ @@ -36,8 +38,9 @@ public interface AuthRequestUrlBuilder { * @param redirectUri * @param nonce * @param state + * @param loginHint * @return */ - public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options); + public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint); } diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java index 72438576f..1664cb35a 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java @@ -32,6 +32,7 @@ import org.mitre.openid.connect.config.ServerConfiguration; import org.springframework.security.authentication.AuthenticationServiceException; import com.google.common.base.Joiner; +import com.google.common.base.Strings; import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWEHeader; @@ -54,7 +55,7 @@ public class EncryptedAuthRequestUrlBuilder implements AuthRequestUrlBuilder { * @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequestUrl(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, java.lang.String, java.lang.String, java.lang.String, java.util.Map) */ @Override - public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options) { + public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint) { // create our signed JWT for the request object JWTClaimsSet claims = new JWTClaimsSet(); @@ -77,6 +78,11 @@ public class EncryptedAuthRequestUrlBuilder implements AuthRequestUrlBuilder { for (Entry option : options.entrySet()) { claims.setClaim(option.getKey(), option.getValue()); } + + // if there's a login hint, send it + if (!Strings.isNullOrEmpty(loginHint)) { + claims.setClaim("login_hint", loginHint); + } EncryptedJWT jwt = new EncryptedJWT(new JWEHeader(alg, enc), claims); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java index 1adfd87c6..d9a269b03 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java @@ -30,6 +30,7 @@ import org.mitre.openid.connect.config.ServerConfiguration; import org.springframework.security.authentication.AuthenticationServiceException; import com.google.common.base.Joiner; +import com.google.common.base.Strings; /** * @@ -44,7 +45,7 @@ public class PlainAuthRequestUrlBuilder implements AuthRequestUrlBuilder { * @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequest(javax.servlet.http.HttpServletRequest, org.mitre.openid.connect.config.ServerConfiguration, org.springframework.security.oauth2.provider.ClientDetails) */ @Override - public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options) { + public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint) { try { URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri()); @@ -62,6 +63,11 @@ public class PlainAuthRequestUrlBuilder implements AuthRequestUrlBuilder { for (Entry option : options.entrySet()) { uriBuilder.addParameter(option.getKey(), option.getValue()); } + + // if there's a login hint, send it + if (!Strings.isNullOrEmpty(loginHint)) { + uriBuilder.addParameter("login_hint", loginHint); + } return uriBuilder.build().toString(); diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java index 493e16e55..88bb1d234 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java @@ -31,6 +31,7 @@ import org.mitre.openid.connect.config.ServerConfiguration; import org.springframework.security.authentication.AuthenticationServiceException; import com.google.common.base.Joiner; +import com.google.common.base.Strings; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jwt.JWTClaimsSet; @@ -48,7 +49,7 @@ public class SignedAuthRequestUrlBuilder implements AuthRequestUrlBuilder { * @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequestUrl(org.mitre.openid.connect.config.ServerConfiguration, org.springframework.security.oauth2.provider.ClientDetails, java.lang.String, java.lang.String, java.lang.String) */ @Override - public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options) { + public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint) { // create our signed JWT for the request object JWTClaimsSet claims = new JWTClaimsSet(); @@ -71,6 +72,11 @@ public class SignedAuthRequestUrlBuilder implements AuthRequestUrlBuilder { for (Entry option : options.entrySet()) { claims.setClaim(option.getKey(), option.getValue()); } + + // if there's a login hint, send it + if (!Strings.isNullOrEmpty(loginHint)) { + claims.setClaim("login_hint", loginHint); + } JWSAlgorithm alg = clientConfig.getRequestObjectSigningAlg(); if (alg == null) { diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java index 8e1ef9135..aac435923 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java @@ -98,7 +98,7 @@ public class WebfingerIssuerService implements IssuerService { throw new AuthenticationServiceException("Issuer was in blacklist: " + issuer); } - return new IssuerServiceResponse(issuer, null, null); + return new IssuerServiceResponse(issuer, identifier, null); } catch (UncheckedExecutionException ue) { logger.warn("Issue fetching issuer for user input: " + identifier, ue); return null; diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java index 488a3afd6..af4ea9209 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java @@ -69,7 +69,27 @@ public class TestPlainAuthRequestUrlBuilder { Map options = ImmutableMap.of("foo", "bar"); - String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options); + String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options, null); + + assertThat(actualUrl, equalTo(expectedUrl)); + } + + @Test + public void buildAuthRequestUrl_withLoginHint() { + + String expectedUrl = "https://server.example.com/authorize?" + + "response_type=code" + + "&client_id=s6BhdRkqt3" + + "&scope=openid+profile" + // plus sign used for space per application/x-www-form-encoded standard + "&redirect_uri=https%3A%2F%2Fclient.example.org%2F" + + "&nonce=34fasf3ds" + + "&state=af0ifjsldkj" + + "&foo=bar" + + "&login_hint=bob"; + + Map options = ImmutableMap.of("foo", "bar"); + + String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options, "bob"); assertThat(actualUrl, equalTo(expectedUrl)); } @@ -81,7 +101,7 @@ public class TestPlainAuthRequestUrlBuilder { Map options = ImmutableMap.of("foo", "bar"); - urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options); + urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null); } } diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java index 0908c5454..b9b576782 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java @@ -82,6 +82,7 @@ public class TestSignedAuthRequestUrlBuilder { "9Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"; private String alg = "RS256"; private String kid = "2011-04-29"; + private String loginHint = "bob"; private DefaultJWTSigningAndValidationService signingAndValidationService; @@ -116,7 +117,7 @@ public class TestSignedAuthRequestUrlBuilder { @Test public void buildAuthRequestUrl() { - String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options); + String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, null); // parsing the result UriComponentsBuilder builder = null; @@ -152,11 +153,51 @@ public class TestSignedAuthRequestUrlBuilder { } } + @Test + public void buildAuthRequestUrl_withLoginHint() { + + String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, loginHint); + + // parsing the result + UriComponentsBuilder builder = null; + + try { + builder = UriComponentsBuilder.fromUri(new URI(requestUri)); + } catch (URISyntaxException e1) { + fail("URISyntaxException was thrown."); + } + + UriComponents components = builder.build(); + String jwtString = components.getQueryParams().get("request").get(0); + ReadOnlyJWTClaimsSet claims = null; + + try { + SignedJWT jwt = SignedJWT.parse(jwtString); + claims = jwt.getJWTClaimsSet(); + } catch (ParseException e) { + fail("ParseException was thrown."); + } + + assertEquals(responseType, claims.getClaim("response_type")); + assertEquals(clientConfig.getClientId(), claims.getClaim("client_id")); + + List scopeList = Arrays.asList(((String) claims.getClaim("scope")).split(" ")); + assertTrue(scopeList.containsAll(clientConfig.getScope())); + + assertEquals(redirectUri, claims.getClaim("redirect_uri")); + assertEquals(nonce, claims.getClaim("nonce")); + assertEquals(state, claims.getClaim("state")); + for (String claim : options.keySet()) { + assertEquals(options.get(claim), claims.getClaim(claim)); + } + assertEquals(loginHint, claims.getClaim("login_hint")); + } + @Test(expected = AuthenticationServiceException.class) public void buildAuthRequestUrl_badUri() { Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2"); - urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options); + urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null); } }