added login hint capability to client library

pull/820/merge
Justin Richer 2015-06-23 21:21:41 -04:00
parent f7a082d4b8
commit c166cbe49c
8 changed files with 92 additions and 10 deletions

View File

@ -262,7 +262,7 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
Map<String, String> options = authOptions.getOptions(serverConfig, clientConfig, request); Map<String, String> 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); logger.debug("Auth Request: " + authRequest);

View File

@ -25,6 +25,8 @@ import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.config.ServerConfiguration; import org.mitre.openid.connect.config.ServerConfiguration;
/** /**
* Builds a URL string to the IdP's authorization endpoint.
*
* @author jricher * @author jricher
* *
*/ */
@ -36,8 +38,9 @@ public interface AuthRequestUrlBuilder {
* @param redirectUri * @param redirectUri
* @param nonce * @param nonce
* @param state * @param state
* @param loginHint
* @return * @return
*/ */
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options); public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint);
} }

View File

@ -32,6 +32,7 @@ import org.mitre.openid.connect.config.ServerConfiguration;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.nimbusds.jose.EncryptionMethod; import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JWEAlgorithm; import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWEHeader; 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) * @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 @Override
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options) { public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint) {
// create our signed JWT for the request object // create our signed JWT for the request object
JWTClaimsSet claims = new JWTClaimsSet(); JWTClaimsSet claims = new JWTClaimsSet();
@ -78,6 +79,11 @@ public class EncryptedAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
claims.setClaim(option.getKey(), option.getValue()); 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); EncryptedJWT jwt = new EncryptedJWT(new JWEHeader(alg, enc), claims);
JWTEncryptionAndDecryptionService encryptor = encrypterService.getEncrypter(serverConfig.getJwksUri()); JWTEncryptionAndDecryptionService encryptor = encrypterService.getEncrypter(serverConfig.getJwksUri());

View File

@ -30,6 +30,7 @@ import org.mitre.openid.connect.config.ServerConfiguration;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import com.google.common.base.Joiner; 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) * @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 @Override
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options) { public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint) {
try { try {
URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri()); URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri());
@ -63,6 +64,11 @@ public class PlainAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
uriBuilder.addParameter(option.getKey(), option.getValue()); 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(); return uriBuilder.build().toString();
} catch (URISyntaxException e) { } catch (URISyntaxException e) {

View File

@ -31,6 +31,7 @@ import org.mitre.openid.connect.config.ServerConfiguration;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jwt.JWTClaimsSet; 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) * @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 @Override
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options) { public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint) {
// create our signed JWT for the request object // create our signed JWT for the request object
JWTClaimsSet claims = new JWTClaimsSet(); JWTClaimsSet claims = new JWTClaimsSet();
@ -72,6 +73,11 @@ public class SignedAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
claims.setClaim(option.getKey(), option.getValue()); 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(); JWSAlgorithm alg = clientConfig.getRequestObjectSigningAlg();
if (alg == null) { if (alg == null) {
alg = signingAndValidationService.getDefaultSigningAlgorithm(); alg = signingAndValidationService.getDefaultSigningAlgorithm();

View File

@ -98,7 +98,7 @@ public class WebfingerIssuerService implements IssuerService {
throw new AuthenticationServiceException("Issuer was in blacklist: " + issuer); throw new AuthenticationServiceException("Issuer was in blacklist: " + issuer);
} }
return new IssuerServiceResponse(issuer, null, null); return new IssuerServiceResponse(issuer, identifier, null);
} catch (UncheckedExecutionException ue) { } catch (UncheckedExecutionException ue) {
logger.warn("Issue fetching issuer for user input: " + identifier, ue); logger.warn("Issue fetching issuer for user input: " + identifier, ue);
return null; return null;

View File

@ -69,7 +69,27 @@ public class TestPlainAuthRequestUrlBuilder {
Map<String, String> options = ImmutableMap.of("foo", "bar"); Map<String, String> 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<String, String> options = ImmutableMap.of("foo", "bar");
String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options, "bob");
assertThat(actualUrl, equalTo(expectedUrl)); assertThat(actualUrl, equalTo(expectedUrl));
} }
@ -81,7 +101,7 @@ public class TestPlainAuthRequestUrlBuilder {
Map<String, String> options = ImmutableMap.of("foo", "bar"); Map<String, String> options = ImmutableMap.of("foo", "bar");
urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options); urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null);
} }
} }

View File

@ -82,6 +82,7 @@ public class TestSignedAuthRequestUrlBuilder {
"9Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"; "9Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q";
private String alg = "RS256"; private String alg = "RS256";
private String kid = "2011-04-29"; private String kid = "2011-04-29";
private String loginHint = "bob";
private DefaultJWTSigningAndValidationService signingAndValidationService; private DefaultJWTSigningAndValidationService signingAndValidationService;
@ -116,7 +117,7 @@ public class TestSignedAuthRequestUrlBuilder {
@Test @Test
public void buildAuthRequestUrl() { 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 // parsing the result
UriComponentsBuilder builder = null; 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<String> 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) @Test(expected = AuthenticationServiceException.class)
public void buildAuthRequestUrl_badUri() { public void buildAuthRequestUrl_badUri() {
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2"); Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2");
urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options); urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null);
} }
} }