added configurable support for clients to send extra parameters like display and prompt, addresses #426

pull/650/head
Justin Richer 2013-08-22 13:52:07 -04:00
parent d1881d1d7e
commit 56fe28bfd2
8 changed files with 142 additions and 18 deletions

View File

@ -22,6 +22,7 @@ import java.net.URI;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.Map;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -33,10 +34,12 @@ import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.JWKSetSigningAndValidationServiceCacheService; import org.mitre.jwt.signer.service.impl.JWKSetSigningAndValidationServiceCacheService;
import org.mitre.oauth2.model.RegisteredClient; import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.model.IssuerServiceResponse; import org.mitre.openid.connect.client.model.IssuerServiceResponse;
import org.mitre.openid.connect.client.service.AuthRequestOptionsService;
import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder; import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder;
import org.mitre.openid.connect.client.service.ClientConfigurationService; import org.mitre.openid.connect.client.service.ClientConfigurationService;
import org.mitre.openid.connect.client.service.IssuerService; import org.mitre.openid.connect.client.service.IssuerService;
import org.mitre.openid.connect.client.service.ServerConfigurationService; import org.mitre.openid.connect.client.service.ServerConfigurationService;
import org.mitre.openid.connect.client.service.impl.StaticAuthRequestOptionsService;
import org.mitre.openid.connect.config.ServerConfiguration; import org.mitre.openid.connect.config.ServerConfiguration;
import org.mitre.openid.connect.model.OIDCAuthenticationToken; import org.mitre.openid.connect.model.OIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -88,6 +91,7 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
private ServerConfigurationService servers; private ServerConfigurationService servers;
private ClientConfigurationService clients; private ClientConfigurationService clients;
private IssuerService issuerService; private IssuerService issuerService;
private AuthRequestOptionsService authOptions = new StaticAuthRequestOptionsService(); // initialize with an empty set of options
private AuthRequestUrlBuilder authRequestBuilder; private AuthRequestUrlBuilder authRequestBuilder;
protected int httpSocketTimeout = HTTP_SOCKET_TIMEOUT; protected int httpSocketTimeout = HTTP_SOCKET_TIMEOUT;
@ -201,7 +205,9 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
// this value comes back in the auth code response // this value comes back in the auth code response
String state = createState(session); String state = createState(session);
String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state); Map<String, String> options = authOptions.getOptions(serverConfig, clientConfig, request);
String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options);
logger.debug("Auth Request: " + authRequest); logger.debug("Auth Request: " + authRequest);
@ -604,4 +610,18 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
this.authRequestBuilder = authRequestBuilder; this.authRequestBuilder = authRequestBuilder;
} }
/**
* @return the authOptions
*/
public AuthRequestOptionsService getAuthRequestOptionsService() {
return authOptions;
}
/**
* @param authOptions the authOptions to set
*/
public void setAuthRequestOptionsService(AuthRequestOptionsService authOptions) {
this.authOptions = authOptions;
}
} }

View File

@ -0,0 +1,25 @@
/**
*
*/
package org.mitre.openid.connect.client.service;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.config.ServerConfiguration;
/**
*
* This service provides any extra options that need to be passed to the authentication request.
* These options may depend on the server configuration, client configuration, or HTTP request.
*
* @author jricher
*
*/
public interface AuthRequestOptionsService {
public Map<String, String> getOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request);
}

View File

@ -19,6 +19,8 @@
*/ */
package org.mitre.openid.connect.client.service; package org.mitre.openid.connect.client.service;
import java.util.Map;
import org.mitre.oauth2.model.RegisteredClient; import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.config.ServerConfiguration; import org.mitre.openid.connect.config.ServerConfiguration;
@ -36,6 +38,6 @@ public interface AuthRequestUrlBuilder {
* @param state * @param state
* @return * @return
*/ */
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state); public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options);
} }

View File

@ -20,6 +20,8 @@
package org.mitre.openid.connect.client.service.impl; package org.mitre.openid.connect.client.service.impl;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.mitre.oauth2.model.RegisteredClient; import org.mitre.oauth2.model.RegisteredClient;
@ -42,7 +44,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) { public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options) {
try { try {
URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri()); URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri());
@ -57,8 +59,9 @@ public class PlainAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
uriBuilder.addParameter("state", state); uriBuilder.addParameter("state", state);
// Optional parameters: // Optional parameters:
for (Entry<String, String> option : options.entrySet()) {
// TODO: display, prompt uriBuilder.addParameter(option.getKey(), option.getValue());
}
return uriBuilder.build().toString(); return uriBuilder.build().toString();

View File

@ -20,6 +20,8 @@
package org.mitre.openid.connect.client.service.impl; package org.mitre.openid.connect.client.service.impl;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.mitre.jwt.signer.service.JwtSigningAndValidationService; import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
@ -45,24 +47,31 @@ 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) { public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options) {
// create our signed JWT for the request object // create our signed JWT for the request object
JWTClaimsSet claims = new JWTClaimsSet(); JWTClaimsSet claims = new JWTClaimsSet();
//set parameters to JwtClaims //set parameters to JwtClaims
claims.setCustomClaim("response_type", "code"); claims.setClaim("response_type", "code");
claims.setCustomClaim("client_id", clientConfig.getClientId()); claims.setClaim("client_id", clientConfig.getClientId());
claims.setCustomClaim("scope", Joiner.on(" ").join(clientConfig.getScope())); claims.setClaim("scope", Joiner.on(" ").join(clientConfig.getScope()));
// build our redirect URI // build our redirect URI
claims.setCustomClaim("redirect_uri", redirectUri); claims.setClaim("redirect_uri", redirectUri);
// this comes back in the id token // this comes back in the id token
claims.setCustomClaim("nonce", nonce); claims.setClaim("nonce", nonce);
// this comes back in the auth request return // this comes back in the auth request return
claims.setCustomClaim("state", state); claims.setClaim("state", state);
// Optional parameters
for (Entry<String, String> option : options.entrySet()) {
claims.setClaim(option.getKey(), option.getValue());
}
SignedJWT jwt = new SignedJWT(new JWSHeader(signingAndValidationService.getDefaultSigningAlgorithm()), claims); SignedJWT jwt = new SignedJWT(new JWSHeader(signingAndValidationService.getDefaultSigningAlgorithm()), claims);

View File

@ -0,0 +1,50 @@
/**
*
*/
package org.mitre.openid.connect.client.service.impl;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.service.AuthRequestOptionsService;
import org.mitre.openid.connect.config.ServerConfiguration;
/**
*
* Always returns the same set of options.
*
* @author jricher
*
*/
public class StaticAuthRequestOptionsService implements AuthRequestOptionsService {
private Map<String, String> options = new HashMap<String, String>();
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.AuthRequestOptionsService#getOptions(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, javax.servlet.http.HttpServletRequest)
*/
@Override
public Map<String, String> getOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request) {
return options;
}
/**
* @return the options
*/
public Map<String, String> getOptions() {
return options;
}
/**
* @param options the options to set
*/
public void setOptions(Map<String, String> options) {
this.options = options;
}
}

View File

@ -16,6 +16,9 @@
******************************************************************************/ ******************************************************************************/
package org.mitre.openid.connect.client.service.impl; package org.mitre.openid.connect.client.service.impl;
import java.util.Collections;
import java.util.Map;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mitre.oauth2.model.RegisteredClient; import org.mitre.oauth2.model.RegisteredClient;
@ -23,6 +26,7 @@ import org.mitre.openid.connect.config.ServerConfiguration;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.AuthenticationServiceException;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
@ -60,9 +64,12 @@ public class TestPlainAuthRequestUrlBuilder {
"&scope=openid+profile" + // plus sign used for space per application/x-www-form-encoded standard "&scope=openid+profile" + // plus sign used for space per application/x-www-form-encoded standard
"&redirect_uri=https%3A%2F%2Fclient.example.org%2F" + "&redirect_uri=https%3A%2F%2Fclient.example.org%2F" +
"&nonce=34fasf3ds" + "&nonce=34fasf3ds" +
"&state=af0ifjsldkj"; "&state=af0ifjsldkj" +
"&foo=bar";
String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj"); Map<String, String> options = ImmutableMap.of("foo", "bar");
String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options);
assertThat(actualUrl, equalTo(expectedUrl)); assertThat(actualUrl, equalTo(expectedUrl));
} }
@ -72,7 +79,9 @@ public class TestPlainAuthRequestUrlBuilder {
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2"); Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2");
urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", ""); Map<String, String> options = ImmutableMap.of("foo", "bar");
urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options);
} }
} }

View File

@ -35,6 +35,7 @@ import org.springframework.security.authentication.AuthenticationServiceExceptio
import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.nimbusds.jose.Algorithm; import com.nimbusds.jose.Algorithm;
@ -63,6 +64,8 @@ public class TestSignedAuthRequestUrlBuilder {
private String nonce = "34fasf3ds"; private String nonce = "34fasf3ds";
private String state = "af0ifjsldkj"; private String state = "af0ifjsldkj";
private String responseType = "code"; private String responseType = "code";
private Map<String, String> options = ImmutableMap.of("foo", "bar");
// RSA key properties: // RSA key properties:
// {@link package com.nimbusds.jose.jwk#RSAKey} // {@link package com.nimbusds.jose.jwk#RSAKey}
@ -113,7 +116,7 @@ public class TestSignedAuthRequestUrlBuilder {
@Test @Test
public void buildAuthRequestUrl() { public void buildAuthRequestUrl() {
String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state); String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options);
// parsing the result // parsing the result
UriComponentsBuilder builder = null; UriComponentsBuilder builder = null;
@ -144,6 +147,9 @@ public class TestSignedAuthRequestUrlBuilder {
assertEquals(redirectUri, claims.getClaim("redirect_uri")); assertEquals(redirectUri, claims.getClaim("redirect_uri"));
assertEquals(nonce, claims.getClaim("nonce")); assertEquals(nonce, claims.getClaim("nonce"));
assertEquals(state, claims.getClaim("state")); assertEquals(state, claims.getClaim("state"));
for (String claim : options.keySet()) {
assertEquals(options.get(claim), claims.getClaim(claim));
}
} }
@Test(expected = AuthenticationServiceException.class) @Test(expected = AuthenticationServiceException.class)
@ -151,6 +157,6 @@ public class TestSignedAuthRequestUrlBuilder {
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2"); Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2");
urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", ""); urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options);
} }
} }