diff --git a/openid-connect-client/README.md b/openid-connect-client/README.md index e0c7e3554..13fbb7337 100644 --- a/openid-connect-client/README.md +++ b/openid-connect-client/README.md @@ -65,7 +65,7 @@ Additionally, it contains a set of convenience methods to pass through to parame Configure like so: + class="org.mitre.openid.connect.client.PlainOIDCAuthenticationFilter"> diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilter.java deleted file mode 100644 index 09ddc2e9f..000000000 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilter.java +++ /dev/null @@ -1,613 +0,0 @@ -/******************************************************************************* - * Copyright 2012 The MITRE Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package org.mitre.openid.connect.client; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.security.SecureRandom; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.apache.commons.lang.StringUtils; -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.message.BasicNameValuePair; -import org.mitre.jwt.signer.service.JwtSigningAndValidationService; -import org.mitre.jwt.signer.service.impl.JWKSetSigningAndValidationServiceCacheService; -import org.mitre.openid.connect.config.OIDCServerConfiguration; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; -import org.springframework.util.Assert; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestTemplate; - -import com.google.common.base.Strings; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.nimbusds.jwt.ReadOnlyJWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -/** - * Abstract OpenID Connect Authentication Filter class - * - * @author nemonik - * - */ -public class AbstractOIDCAuthenticationFilter extends - AbstractAuthenticationProcessingFilter { - - protected static final String REDIRECT_URI_SESION_VARIABLE = "redirect_uri"; - protected static final String STATE_SESSION_VARIABLE = "state"; - protected final static String NONCE_SESSION_VARIABLE = "nonce"; - protected final static int HTTP_SOCKET_TIMEOUT = 30000; - protected final static String DEFAULT_SCOPE = "openid"; - - protected final static String FILTER_PROCESSES_URL = "/openid_connect_login"; - - // Allow for time sync issues by having a window of X seconds. - private int timeSkewAllowance = 300; - - @Autowired - JWKSetSigningAndValidationServiceCacheService validationServices; - - /** - * Builds the redirect_uri that will be sent to the Authorization Endpoint. - * By default returns the URL of the current request minus zero or more - * fields of the URL's query string. - * - * @param request - * the current request which is being processed by this filter - * @param ignoreFields - * an array of field names to ignore. - * @return a URL built from the messaged parameters. - */ - public static String buildRedirectURI(HttpServletRequest request, String[] ignoreFields) { - - List ignore = (ignoreFields != null) ? Arrays.asList(ignoreFields) : null; - - //boolean isFirst = true; - - StringBuffer sb = request.getRequestURL(); - List queryparams = new ArrayList(); - - - for (Enumeration e = request.getParameterNames(); e.hasMoreElements();) { - - String name = (String) e.nextElement(); - - if ((ignore == null) || (!ignore.contains(name))) { - - // Assume for simplicity that there is only one value - - String value = request.getParameter(name); - - if (value == null) { - continue; - } - - queryparams.add(new BasicNameValuePair(name,value)); - - //if (isFirst) { - // sb.append("?"); - // isFirst = false; - //} - - //sb.append(name).append("=").append(value); - - //if (e.hasMoreElements()) { - // sb.append("&"); - //} - } - - } - return sb.append("?").append(URLEncodedUtils.format(queryparams, "UTF-8")).toString(); - } - - /** - * Return the URL w/ GET parameters - * - * @param baseURI - * A String containing the protocol, server address, path, and - * program as per "http://server/path/program" - * @param queryStringFields - * A map where each key is the field name and the associated - * key's value is the field value used to populate the URL's - * query string - * @return A String representing the URL in form of - * http://server/path/program?query_string from the messaged - * parameters. - */ - public static String buildURL(String baseURI, Map queryStringFields) { - StringBuilder URLBuilder = new StringBuilder(baseURI); - List queryparams = new ArrayList(); - char appendChar = '?'; - - // build a NameValuePair list for the query paramaters - for (Map.Entry param : queryStringFields.entrySet()){ - queryparams.add(new BasicNameValuePair(param.getKey(),param.getValue())); - } - URLBuilder.append(appendChar).append(URLEncodedUtils.format(queryparams, "UTF-8")); - - return URLBuilder.toString(); - } - - protected String errorRedirectURI; - - protected String scope; - - protected int httpSocketTimeout = HTTP_SOCKET_TIMEOUT; - - /** - * OpenIdConnectAuthenticationFilter constructor - */ - protected AbstractOIDCAuthenticationFilter() { - super(FILTER_PROCESSES_URL); - } - - @Override - public void afterPropertiesSet() { - super.afterPropertiesSet(); - - Assert.notNull(errorRedirectURI, "An Error Redirect URI must be supplied"); - - if (Strings.isNullOrEmpty(scope)) { - setScope(DEFAULT_SCOPE); - } else { - setScope(scope); - } - } - - /* - * (non-Javadoc) - * - * @see org.springframework.security.web.authentication. - * AbstractAuthenticationProcessingFilter - * #attemptAuthentication(javax.servlet.http.HttpServletRequest, - * javax.servlet.http.HttpServletResponse) - */ - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { - - logger.debug("Request: " - + request.getRequestURI() - + (StringUtils.isNotBlank(request.getQueryString()) ? "?" - + request.getQueryString() : "")); - - return null; - } - - /** - * Handles the authorization grant response - * - * @param authorizationCode - * The Authorization grant code - * @param request - * The request from which to extract parameters and perform the - * authentication - * @return The authenticated user token, or null if authentication is - * incomplete. - * @throws Exception - * @throws UnsupportedEncodingException - */ - protected Authentication handleAuthorizationGrantResponse(String authorizationCode, HttpServletRequest request, OIDCServerConfiguration serverConfig) { - - final boolean debug = logger.isDebugEnabled(); - - HttpSession session = request.getSession(); - - // check for state - String storedState = getStoredState(session); - if (!StringUtils.isBlank(storedState)) { - String state = request.getParameter("state"); - if (!storedState.equals(state)) { - throw new AuthenticationServiceException("State parameter mismatch on return. Expected " + storedState + " got " + state); - } - } - - // Handle Token Endpoint interaction - HttpClient httpClient = new DefaultHttpClient(); - - httpClient.getParams().setParameter("http.socket.timeout", new Integer(httpSocketTimeout)); - - // - // TODO: basic auth is untested (it wasn't working last I - // tested) - // UsernamePasswordCredentials credentials = new - // UsernamePasswordCredentials(serverConfig.getClientId(), - // serverConfig.getClientSecret()); - // ((DefaultHttpClient) - // httpClient).getCredentialsProvider().setCredentials(AuthScope.ANY, - // credentials); - // - - HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); - - RestTemplate restTemplate = new RestTemplate(factory); - - MultiValueMap form = new LinkedMultiValueMap(); - form.add("grant_type", "authorization_code"); - form.add("code", authorizationCode); - - String redirectUri = getStoredRedirectUri(session); - if (redirectUri != null) { - form.add("redirect_uri", redirectUri); - } - - // pass clientId and clientSecret in post of request - form.add("client_id", serverConfig.getClientId()); - form.add("client_secret", serverConfig.getClientSecret()); - - if (debug) { - logger.debug("tokenEndpointURI = " + serverConfig.getTokenEndpointUrl()); - logger.debug("form = " + form); - } - - String jsonString = null; - - try { - jsonString = restTemplate.postForObject( - serverConfig.getTokenEndpointUrl(), form, String.class); - } catch (HttpClientErrorException httpClientErrorException) { - - // Handle error - - logger.error("Token Endpoint error response: " - + httpClientErrorException.getStatusText() + " : " - + httpClientErrorException.getMessage()); - - throw new AuthenticationServiceException( - "Unable to obtain Access Token."); - } - - logger.debug("from TokenEndpoint jsonString = " + jsonString); - - JsonElement jsonRoot = new JsonParser().parse(jsonString); - if (!jsonRoot.isJsonObject()) { - throw new AuthenticationServiceException("Token Endpoint did not return a JSON object: " + jsonRoot); - } - - JsonObject tokenResponse = jsonRoot.getAsJsonObject(); - - if (tokenResponse.get("error") != null) { - - // Handle error - - String error = tokenResponse.get("error") - .getAsString(); - - logger.error("Token Endpoint returned: " + error); - - throw new AuthenticationServiceException("Unable to obtain Access Token. Token Endpoint returned: " + error); - - } else { - - // Extract the id_token to insert into the - // OIDCAuthenticationToken - - // get out all the token strings - String accessTokenValue = null; - String idTokenValue = null; - String refreshTokenValue = null; - - if (tokenResponse.has("access_token")) { - accessTokenValue = tokenResponse.get("access_token").getAsString(); - } else { - throw new AuthenticationServiceException("Token Endpoint did not return an access_token: " + jsonString); - } - - if (tokenResponse.has("id_token")) { - idTokenValue = tokenResponse.get("id_token").getAsString(); - } else { - logger.error("Token Endpoint did not return an id_token"); - throw new AuthenticationServiceException("Token Endpoint did not return an id_token"); - } - - if (tokenResponse.has("refresh_token")) { - refreshTokenValue = tokenResponse.get("refresh_token").getAsString(); - } - - try { - SignedJWT idToken = SignedJWT.parse(idTokenValue); - - // validate our ID Token over a number of tests - ReadOnlyJWTClaimsSet idClaims = idToken.getJWTClaimsSet(); - - // check the signature - JwtSigningAndValidationService jwtValidator = validationServices.get(serverConfig.getJwkSigningUrl()); - if (jwtValidator != null) { - if(!jwtValidator.validateSignature(idToken)) { - throw new AuthenticationServiceException("Signature validation failed"); - } - } else { - logger.info("No validation service found. Skipping signature validation"); - } - - // check the issuer - if (idClaims.getIssuer() == null) { - throw new AuthenticationServiceException("Id Token Issuer is null"); - } else if (!idClaims.getIssuer().equals(serverConfig.getIssuer())){ - throw new AuthenticationServiceException("Issuers do not match, expected " + serverConfig.getIssuer() + " got " + idClaims.getIssuer()); - } - - // check expiration - if (idClaims.getExpirationTime() == null) { - throw new AuthenticationServiceException("Id Token does not have required expiration claim"); - } else { - // it's not null, see if it's expired - Date now = new Date(System.currentTimeMillis() - (timeSkewAllowance * 1000)); - if (now.after(idClaims.getExpirationTime())) { - throw new AuthenticationServiceException("Id Token is expired: " + idClaims.getExpirationTime()); - } - } - - // check not before - if (idClaims.getNotBeforeTime() != null) { - Date now = new Date(System.currentTimeMillis() + (timeSkewAllowance * 1000)); - if (now.before(idClaims.getNotBeforeTime())){ - throw new AuthenticationServiceException("Id Token not valid untill: " + idClaims.getNotBeforeTime()); - } - } - - // check issued at - if (idClaims.getIssueTime() == null) { - throw new AuthenticationServiceException("Id Token does not have required issued-at claim"); - } else { - // since it's not null, see if it was issued in the future - Date now = new Date(System.currentTimeMillis() + (timeSkewAllowance * 1000)); - if (now.before(idClaims.getIssueTime())) { - throw new AuthenticationServiceException("Id Token was issued in the future: " + idClaims.getIssueTime()); - } - } - - // check audience - if (idClaims.getAudience() == null) { - throw new AuthenticationServiceException("Id token audience is null"); - } else if (!idClaims.getAudience().contains(serverConfig.getClientId())) { - throw new AuthenticationServiceException("Audience does not match, expected " + serverConfig.getClientId() + " got " + idClaims.getAudience()); - } - - // compare the nonce to our stored claim - // FIXME: Nimbus claims as strings? - String nonce = (String) idClaims.getCustomClaim("nonce"); - if (StringUtils.isBlank(nonce)) { - - logger.error("ID token did not contain a nonce claim."); - - throw new AuthenticationServiceException("ID token did not contain a nonce claim."); - } - - String storedNonce = getStoredNonce(session); - if (!nonce.equals(storedNonce)) { - logger.error("Possible replay attack detected! The comparison of the nonce in the returned " - + "ID Token to the session " + NONCE_SESSION_VARIABLE + " failed. Expected " + storedNonce + " got " + nonce + "."); - - throw new AuthenticationServiceException( - "Possible replay attack detected! The comparison of the nonce in the returned " - + "ID Token to the session " + NONCE_SESSION_VARIABLE + " failed. Expected " + storedNonce + " got " + nonce + "."); - } - - // pull the subject (user id) out as a claim on the id_token - - String userId = idClaims.getSubject(); - - // construct an OIDCAuthenticationToken and return a Authentication object w/the userId and the idToken - - OIDCAuthenticationToken token = new OIDCAuthenticationToken(userId, idClaims.getIssuer(), serverConfig, idTokenValue, accessTokenValue, refreshTokenValue); - - Authentication authentication = this.getAuthenticationManager().authenticate(token); - - return authentication; - } catch (ParseException e) { - throw new AuthenticationServiceException("Couldn't parse idToken: ", e); - } - - - - } - } - - /** - * Initiate an Authorization request - * - * @param request - * The request from which to extract parameters and perform the - * authentication - * @param response - * The response, needed to set a cookie and do a redirect as part - * of a multi-stage authentication process - * @param serverConfiguration - * @throws IOException - * If an input or output exception occurs - */ - protected void handleAuthorizationRequest(HttpServletRequest request, - HttpServletResponse response, OIDCServerConfiguration serverConfiguration) throws IOException { - - HttpSession session = request.getSession(); - - Map urlVariables = new HashMap(); - - // Required parameters: - - urlVariables.put("response_type", "code"); - urlVariables.put("client_id", serverConfiguration.getClientId()); - urlVariables.put("scope", scope); - - String redirectURI = buildRedirectURI(request, null); - urlVariables.put("redirect_uri", redirectURI); - session.setAttribute(REDIRECT_URI_SESION_VARIABLE, redirectURI); - - // Create a string value used to associate a user agent session - // with an ID Token to mitigate replay attacks. The value is - // passed through unmodified to the ID Token. One method is to - // store a random value as a signed session cookie, and pass the - // value in the nonce parameter. - - String nonce = createNonce(session); - urlVariables.put("nonce", nonce); - - String state = createState(session); - urlVariables.put("state", state); - - // Optional parameters: - - // TODO: display, prompt, request, request_uri - - String authRequest = AbstractOIDCAuthenticationFilter.buildURL(serverConfiguration.getAuthorizationEndpointUrl(), urlVariables); - - logger.debug("Auth Request: " + authRequest); - - response.sendRedirect(authRequest); - } - - /** - * Handle Authorization Endpoint error - * - * @param request - * The request from which to extract parameters and handle the - * error - * @param response - * The response, needed to do a redirect to display the error - * @throws IOException - * If an input or output exception occurs - */ - protected void handleError(HttpServletRequest request, - HttpServletResponse response) throws IOException { - - String error = request.getParameter("error"); - String errorDescription = request.getParameter("error_description"); - String errorURI = request.getParameter("error_uri"); - - Map requestParams = new HashMap(); - - requestParams.put("error", error); - - if (errorDescription != null) { - requestParams.put("error_description", errorDescription); - } - - if (errorURI != null) { - requestParams.put("error_uri", errorURI); - } - - response.sendRedirect(AbstractOIDCAuthenticationFilter.buildURL( - errorRedirectURI, requestParams)); - } - - public void setErrorRedirectURI(String errorRedirectURI) { - this.errorRedirectURI = errorRedirectURI; - } - - public void setScope(String scope) { - this.scope = scope; - } - - - /** - * Get the named stored session variable as a string. Return null if not found or not a string. - * @param session - * @param key - * @return - */ - private static String getStoredSessionString(HttpSession session, String key) { - Object o = session.getAttribute(key); - if (o != null && o instanceof String) { - return o.toString(); - } else { - return null; - } - } - - /** - * Create a cryptographically random nonce and store it in the session - * @param session - * @return - */ - protected static String createNonce(HttpSession session) { - String nonce = new BigInteger(50, new SecureRandom()).toString(16); - session.setAttribute(NONCE_SESSION_VARIABLE, nonce); - - return nonce; - } - - /** - * Get the nonce we stored in the session - * @param session - * @return - */ - protected static String getStoredNonce(HttpSession session) { - return getStoredSessionString(session, NONCE_SESSION_VARIABLE); - } - - /** - * Create a cryptographically random state and store it in the session - * @param session - * @return - */ - protected static String createState(HttpSession session) { - String state = new BigInteger(50, new SecureRandom()).toString(16); - session.setAttribute(STATE_SESSION_VARIABLE, state); - - return state; - } - - /** - * Get the state we stored in the session - * @param session - * @return - */ - protected static String getStoredState(HttpSession session) { - return getStoredSessionString(session, STATE_SESSION_VARIABLE); - } - - /** - * Get the stored redirect URI that we used on the way out - * @param serverConfig - * @return - */ - protected static String getStoredRedirectUri(HttpSession session) { - return getStoredSessionString(session, REDIRECT_URI_SESION_VARIABLE); - } - - public int getTimeSkewAllowance() { - return timeSkewAllowance; - } - - public void setTimeSkewAllowance(int timeSkewAllowance) { - this.timeSkewAllowance = timeSkewAllowance; - } - -} 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 c621618d8..cff2a3f2f 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 @@ -16,141 +16,465 @@ package org.mitre.openid.connect.client; import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.text.ParseException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import org.apache.commons.lang.StringUtils; -import org.mitre.openid.connect.config.OIDCServerConfiguration; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.impl.client.DefaultHttpClient; +import org.mitre.jwt.signer.service.JwtSigningAndValidationService; +import org.mitre.jwt.signer.service.impl.JWKSetSigningAndValidationServiceCacheService; +import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder; +import org.mitre.openid.connect.client.service.ClientConfigurationService; +import org.mitre.openid.connect.client.service.IssuerService; +import org.mitre.openid.connect.client.service.ServerConfigurationService; +import org.mitre.openid.connect.config.ServerConfiguration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.util.Assert; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +import com.google.common.base.Strings; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.nimbusds.jwt.ReadOnlyJWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; /** - * The OpenID Connect Authentication Filter + * OpenID Connect Authentication Filter class * - * See README.md to to configure - * - * @author nemonik + * @author nemonik, jricher * */ -public class OIDCAuthenticationFilter extends AbstractOIDCAuthenticationFilter { +public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFilter { - protected OIDCServerConfiguration oidcServerConfig; + protected final static String REDIRECT_URI_SESION_VARIABLE = "redirect_uri"; + protected final static String STATE_SESSION_VARIABLE = "state"; + protected final static String NONCE_SESSION_VARIABLE = "nonce"; + protected final static String ISSUER_SESSION_VARIABLE = "issuer"; + protected final static int HTTP_SOCKET_TIMEOUT = 30000; + + protected final static String FILTER_PROCESSES_URL = "/openid_connect_login"; + + // Allow for time sync issues by having a window of X seconds. + private int timeSkewAllowance = 300; + + @Autowired + private JWKSetSigningAndValidationServiceCacheService validationServices; + + // modular services to build out client filter + private ServerConfigurationService servers; + private ClientConfigurationService clients; + private IssuerService issuerService; + private AuthRequestUrlBuilder authRequestBuilder; + + protected int httpSocketTimeout = HTTP_SOCKET_TIMEOUT; /** * OpenIdConnectAuthenticationFilter constructor */ protected OIDCAuthenticationFilter() { - super(); - - oidcServerConfig = new OIDCServerConfiguration(); + super(FILTER_PROCESSES_URL); } - /* - * (non-Javadoc) - * - * @see org.mitre.openid.connect.client.AbstractOIDCAuthenticationFilter# - * afterPropertiesSet() - */ @Override public void afterPropertiesSet() { super.afterPropertiesSet(); - - // Validating configuration - - Assert.notNull(oidcServerConfig.getAuthorizationEndpointUrl(), "An Authorization Endpoint URI must be supplied"); - - Assert.notNull(oidcServerConfig.getTokenEndpointUrl(), "A Token ID Endpoint URI must be supplied"); - - Assert.notNull(oidcServerConfig.getClientId(), "A Client ID must be supplied"); - - Assert.notNull(oidcServerConfig.getClientSecret(), "A Client Secret must be supplied"); } /* + * This is the main entry point for the filter. + * * (non-Javadoc) * - * @see org.mitre.openid.connect.client.AbstractOIDCAuthenticationFilter# - * attemptAuthentication(javax.servlet.http.HttpServletRequest, + * @see org.springframework.security.web.authentication. + * AbstractAuthenticationProcessingFilter + * #attemptAuthentication(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse) */ @Override - public Authentication attemptAuthentication(HttpServletRequest request, - HttpServletResponse response) throws AuthenticationException, - IOException, ServletException { + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { - // Enter AuthenticationFilter here... - - super.attemptAuthentication(request, response); - - if (StringUtils.isNotBlank(request.getParameter("error"))) { + if (!Strings.isNullOrEmpty(request.getParameter("error"))) { + // there's an error coming back from the server, need to handle this handleError(request, response); + return null; // no auth, response is sent to display page or something + + } else if (!Strings.isNullOrEmpty(request.getParameter("code"))) { - } else if (StringUtils.isNotBlank(request.getParameter("code"))) { + // we got back the code, need to process this to get our tokens + Authentication auth = handleAuthorizationCodeResponse(request, response); + return auth; + + } else { + + // not an error, not a code, must be an initial login of some type + handleAuthorizationRequest(request, response); + + return null; // no auth, response redirected to the server's Auth Endpoint (or possibly to the account chooser) + } - try { - return handleAuthorizationGrantResponse(request.getParameter("code"), request, oidcServerConfig); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } + + /** + * Initiate an Authorization request + * + * @param request + * The request from which to extract parameters and perform the + * authentication + * @param response + * @throws IOException + * If an input or output exception occurs + */ + protected void handleAuthorizationRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { + + HttpSession session = request.getSession(); + + String issuer = issuerService.getIssuer(request); + ServerConfiguration serverConfig = servers.getServerConfiguration(issuer); + ClientDetails clientConfig = clients.getClientConfiguration(issuer); + + // our redirect URI is this current URL, with no query parameters + String redirectUri = request.getRequestURL().toString(); + session.setAttribute(REDIRECT_URI_SESION_VARIABLE, redirectUri); + + // this value comes back in the id token and is checked there + String nonce = createNonce(session); + + // this value comes back in the auth code response + String state = createState(session); + + String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state); + + logger.debug("Auth Request: " + authRequest); + + response.sendRedirect(authRequest); + } + + /** + * @param request + * The request from which to extract parameters and perform the + * authentication + * @return The authenticated user token, or null if authentication is + * incomplete. + */ + protected Authentication handleAuthorizationCodeResponse(HttpServletRequest request, HttpServletResponse response) { + + String authorizationCode = request.getParameter("code"); + + HttpSession session = request.getSession(); + + // check for state, if it doesn't match we bail early + String storedState = getStoredState(session); + if (!StringUtils.isBlank(storedState)) { + String state = request.getParameter("state"); + if (!storedState.equals(state)) { + throw new AuthenticationServiceException("State parameter mismatch on return. Expected " + storedState + " got " + state); } + } + + // look up the issuer that we set out to talk to + String issuer = getStoredSessionString(session, ISSUER_SESSION_VARIABLE); + + // pull the configurations based on that issuer + ServerConfiguration serverConfig = servers.getServerConfiguration(issuer); + ClientDetails clientConfig = clients.getClientConfiguration(issuer); + + + // Handle Token Endpoint interaction + DefaultHttpClient httpClient = new DefaultHttpClient(); + + httpClient.getParams().setParameter("http.socket.timeout", new Integer(httpSocketTimeout)); + + + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(clientConfig.getClientId(), clientConfig.getClientSecret()); + httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, credentials); + + /* Alternatively, use form-based auth: + * + form.add("client_id", serverConfig.getClientId()); + form.add("client_secret", serverConfig.getClientSecret()); + */ + + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); + + RestTemplate restTemplate = new RestTemplate(factory); + + MultiValueMap form = new LinkedMultiValueMap(); + form.add("grant_type", "authorization_code"); + form.add("code", authorizationCode); + + String redirectUri = getStoredSessionString(session, REDIRECT_URI_SESION_VARIABLE); + if (redirectUri != null) { + form.add("redirect_uri", redirectUri); + } + + logger.debug("tokenEndpointURI = " + serverConfig.getTokenEndpointUri()); + logger.debug("form = " + form); + + String jsonString = null; + + try { + jsonString = restTemplate.postForObject(serverConfig.getTokenEndpointUri(), form, String.class); + } catch (HttpClientErrorException httpClientErrorException) { + + // Handle error + + logger.error("Token Endpoint error response: " + + httpClientErrorException.getStatusText() + " : " + + httpClientErrorException.getMessage()); + + throw new AuthenticationServiceException("Unable to obtain Access Token: " + httpClientErrorException.getMessage()); + } + + logger.debug("from TokenEndpoint jsonString = " + jsonString); + + JsonElement jsonRoot = new JsonParser().parse(jsonString); + if (!jsonRoot.isJsonObject()) { + throw new AuthenticationServiceException("Token Endpoint did not return a JSON object: " + jsonRoot); + } + + JsonObject tokenResponse = jsonRoot.getAsJsonObject(); + + if (tokenResponse.get("error") != null) { + + // Handle error + + String error = tokenResponse.get("error").getAsString(); + + logger.error("Token Endpoint returned: " + error); + + throw new AuthenticationServiceException("Unable to obtain Access Token. Token Endpoint returned: " + error); } else { - handleAuthorizationRequest(request, response, oidcServerConfig); + // Extract the id_token to insert into the + // OIDCAuthenticationToken + + // get out all the token strings + String accessTokenValue = null; + String idTokenValue = null; + String refreshTokenValue = null; + + if (tokenResponse.has("access_token")) { + accessTokenValue = tokenResponse.get("access_token").getAsString(); + } else { + throw new AuthenticationServiceException("Token Endpoint did not return an access_token: " + jsonString); + } + + if (tokenResponse.has("id_token")) { + idTokenValue = tokenResponse.get("id_token").getAsString(); + } else { + logger.error("Token Endpoint did not return an id_token"); + throw new AuthenticationServiceException("Token Endpoint did not return an id_token"); + } + + if (tokenResponse.has("refresh_token")) { + refreshTokenValue = tokenResponse.get("refresh_token").getAsString(); + } + + try { + SignedJWT idToken = SignedJWT.parse(idTokenValue); + + // validate our ID Token over a number of tests + ReadOnlyJWTClaimsSet idClaims = idToken.getJWTClaimsSet(); + + // check the signature + JwtSigningAndValidationService jwtValidator = validationServices.get(serverConfig.getJwksUri()); + if (jwtValidator != null) { + if(!jwtValidator.validateSignature(idToken)) { + throw new AuthenticationServiceException("Signature validation failed"); + } + } else { + logger.info("No validation service found. Skipping signature validation"); + } + + // check the issuer + if (idClaims.getIssuer() == null) { + throw new AuthenticationServiceException("Id Token Issuer is null"); + } else if (!idClaims.getIssuer().equals(serverConfig.getIssuer())){ + throw new AuthenticationServiceException("Issuers do not match, expected " + serverConfig.getIssuer() + " got " + idClaims.getIssuer()); + } + + // check expiration + if (idClaims.getExpirationTime() == null) { + throw new AuthenticationServiceException("Id Token does not have required expiration claim"); + } else { + // it's not null, see if it's expired + Date now = new Date(System.currentTimeMillis() - (timeSkewAllowance * 1000)); + if (now.after(idClaims.getExpirationTime())) { + throw new AuthenticationServiceException("Id Token is expired: " + idClaims.getExpirationTime()); + } + } + + // check not before + if (idClaims.getNotBeforeTime() != null) { + Date now = new Date(System.currentTimeMillis() + (timeSkewAllowance * 1000)); + if (now.before(idClaims.getNotBeforeTime())){ + throw new AuthenticationServiceException("Id Token not valid untill: " + idClaims.getNotBeforeTime()); + } + } + + // check issued at + if (idClaims.getIssueTime() == null) { + throw new AuthenticationServiceException("Id Token does not have required issued-at claim"); + } else { + // since it's not null, see if it was issued in the future + Date now = new Date(System.currentTimeMillis() + (timeSkewAllowance * 1000)); + if (now.before(idClaims.getIssueTime())) { + throw new AuthenticationServiceException("Id Token was issued in the future: " + idClaims.getIssueTime()); + } + } + + // check audience + if (idClaims.getAudience() == null) { + throw new AuthenticationServiceException("Id token audience is null"); + } else if (!idClaims.getAudience().contains(clientConfig.getClientId())) { + throw new AuthenticationServiceException("Audience does not match, expected " + clientConfig.getClientId() + " got " + idClaims.getAudience()); + } + + // compare the nonce to our stored claim + // FIXME: Nimbus claims as strings? + String nonce = (String) idClaims.getCustomClaim("nonce"); + if (StringUtils.isBlank(nonce)) { + + logger.error("ID token did not contain a nonce claim."); + + throw new AuthenticationServiceException("ID token did not contain a nonce claim."); + } + + String storedNonce = getStoredNonce(session); + if (!nonce.equals(storedNonce)) { + logger.error("Possible replay attack detected! The comparison of the nonce in the returned " + + "ID Token to the session " + NONCE_SESSION_VARIABLE + " failed. Expected " + storedNonce + " got " + nonce + "."); + + throw new AuthenticationServiceException( + "Possible replay attack detected! The comparison of the nonce in the returned " + + "ID Token to the session " + NONCE_SESSION_VARIABLE + " failed. Expected " + storedNonce + " got " + nonce + "."); + } + + // pull the subject (user id) out as a claim on the id_token + + String userId = idClaims.getSubject(); + + // construct an OIDCAuthenticationToken and return a Authentication object w/the userId and the idToken + + OIDCAuthenticationToken token = new OIDCAuthenticationToken(userId, idClaims.getIssuer(), serverConfig, idTokenValue, accessTokenValue, refreshTokenValue); + + Authentication authentication = this.getAuthenticationManager().authenticate(token); + + return authentication; + } catch (ParseException e) { + throw new AuthenticationServiceException("Couldn't parse idToken: ", e); + } + + + } - - return null; - } - - public void setAuthorizationEndpointUrl(String authorizationEndpointUrl) { - oidcServerConfig.setAuthorizationEndpointUrl(authorizationEndpointUrl); - } - - public void setTokenEndpointUrl(String tokenEndpointUrl) { - oidcServerConfig.setTokenEndpointUrl(tokenEndpointUrl); - } - - public void setClientId(String clientId) { - oidcServerConfig.setClientId(clientId); - } - - public void setClientSecret(String clientSecret) { - oidcServerConfig.setClientSecret(clientSecret); - } - - public void setX509EncryptUrl(String x509EncryptUrl) { - oidcServerConfig.setX509EncryptUrl(x509EncryptUrl); - } - - public void setX509SigningUrl(String x509SigningUrl) { - oidcServerConfig.setX509SigningUrl(x509SigningUrl); - } - - public void setJwkEncryptUrl(String jwkEncryptUrl) { - oidcServerConfig.setJwkEncryptUrl(jwkEncryptUrl); - } - - public void setJwkSigningUrl(String jwkSigningUrl) { - oidcServerConfig.setJwkSigningUrl(jwkSigningUrl); } /** - * @param issuer - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setIssuer(java.lang.String) - */ - public void setIssuer(String issuer) { - oidcServerConfig.setIssuer(issuer); - } + * Handle Authorization Endpoint error + * + * @param request + * The request from which to extract parameters and handle the + * error + * @param response + * The response, needed to do a redirect to display the error + * @throws IOException + * If an input or output exception occurs + */ + protected void handleError(HttpServletRequest request, HttpServletResponse response) throws IOException { + + String error = request.getParameter("error"); + String errorDescription = request.getParameter("error_description"); + String errorURI = request.getParameter("error_uri"); + + throw new AuthenticationServiceException("Error from Authorization Endpoint: " + error + " " + errorDescription + " " + errorURI); + } /** - * @param userInfoUrl - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setUserInfoUrl(java.lang.String) - */ - public void setUserInfoUrl(String userInfoUrl) { - oidcServerConfig.setUserInfoUrl(userInfoUrl); - } + * Get the named stored session variable as a string. Return null if not found or not a string. + * @param session + * @param key + * @return + */ + private static String getStoredSessionString(HttpSession session, String key) { + Object o = session.getAttribute(key); + if (o != null && o instanceof String) { + return o.toString(); + } else { + return null; + } + } + + /** + * Create a cryptographically random nonce and store it in the session + * @param session + * @return + */ + protected static String createNonce(HttpSession session) { + String nonce = new BigInteger(50, new SecureRandom()).toString(16); + session.setAttribute(NONCE_SESSION_VARIABLE, nonce); + + return nonce; + } + + /** + * Get the nonce we stored in the session + * @param session + * @return + */ + protected static String getStoredNonce(HttpSession session) { + return getStoredSessionString(session, NONCE_SESSION_VARIABLE); + } + + /** + * Create a cryptographically random state and store it in the session + * @param session + * @return + */ + protected static String createState(HttpSession session) { + String state = new BigInteger(50, new SecureRandom()).toString(16); + session.setAttribute(STATE_SESSION_VARIABLE, state); + + return state; + } + + /** + * Get the state we stored in the session + * @param session + * @return + */ + protected static String getStoredState(HttpSession session) { + return getStoredSessionString(session, STATE_SESSION_VARIABLE); + } + + public int getTimeSkewAllowance() { + return timeSkewAllowance; + } + + public void setTimeSkewAllowance(int timeSkewAllowance) { + this.timeSkewAllowance = timeSkewAllowance; + } + } diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationToken.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationToken.java index 7ef755a70..8cf1611cc 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationToken.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationToken.java @@ -18,7 +18,7 @@ package org.mitre.openid.connect.client; import java.util.ArrayList; import java.util.Collection; -import org.mitre.openid.connect.config.OIDCServerConfiguration; +import org.mitre.openid.connect.config.ServerConfiguration; import org.mitre.openid.connect.model.UserInfo; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; @@ -41,7 +41,7 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { private final String issuer; // issuer URL (parsed from the id token) private final String userId; // user id (parsed from the id token) - private final transient OIDCServerConfiguration serverConfiguration; // server configuration used to fulfill this token, don't serialize it + private final transient ServerConfiguration serverConfiguration; // server configuration used to fulfill this token, don't serialize it private final transient UserInfo userInfo; // user info container, don't serialize it b/c it might be huge and can be re-fetched /** @@ -84,7 +84,7 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { * @param idToken */ public OIDCAuthenticationToken(String userId, String issuer, - OIDCServerConfiguration serverConfiguration, + ServerConfiguration serverConfiguration, String idTokenValue, String accessTokenValue, String refreshTokenValue) { super(new ArrayList(0)); @@ -153,7 +153,7 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { /** * @return the serverConfiguration */ - public OIDCServerConfiguration getServerConfiguration() { + public ServerConfiguration getServerConfiguration() { return serverConfiguration; } diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationUsingChooserFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationUsingChooserFilter.java deleted file mode 100644 index a218a73f0..000000000 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationUsingChooserFilter.java +++ /dev/null @@ -1,168 +0,0 @@ -/******************************************************************************* - * Copyright 2012 The MITRE Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package org.mitre.openid.connect.client; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang.StringUtils; -import org.mitre.openid.connect.config.OIDCServerConfiguration; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.util.Assert; -import org.springframework.web.util.WebUtils; - -/** - * The OpenID Connect Authentication Filter using Acount Chooser UI Application - * - * See README.md to to configure - * - * @author nemonik - * - */ -public class OIDCAuthenticationUsingChooserFilter extends AbstractOIDCAuthenticationFilter { - - protected final static String ISSUER_COOKIE_NAME = "issuer"; - - protected String accountChooserURI; - - protected String accountChooserClientID; - - protected Map oidcServerConfigs = new HashMap(); - - /** - * OpenIdConnectAuthenticationFilter constructor - */ - protected OIDCAuthenticationUsingChooserFilter() { - super(); - } - - /* - * (non-Javadoc) - * - * @see org.mitre.openid.connect.client.AbstractOIDCAuthenticationFilter# - * afterPropertiesSet() - */ - @Override - public void afterPropertiesSet() { - super.afterPropertiesSet(); - - // Validating configuration - - Assert.notNull(oidcServerConfigs, "Server Configurations must be supplied if the Account Chooser UI Application is to be used."); - - Assert.notNull(accountChooserURI, "Account Chooser URI must be supplied if the Account Chooser UI Application is to be used."); - - Assert.notNull(accountChooserClientID, "Account Chooser Client ID must be supplied if the Account Chooser UI Application is to be used."); - } - - /* - * (non-Javadoc) - * - * @see org.springframework.security.web.authentication. - * AbstractAuthenticationProcessingFilter - * #attemptAuthentication(javax.servlet.http.HttpServletRequest, - * javax.servlet.http.HttpServletResponse) - */ - @Override - public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException, AuthenticationException, ServletException { - - // Enter AuthenticationFilter here... - super.attemptAuthentication(request, response); - - if (StringUtils.isNotBlank(request.getParameter("error"))) { - - handleError(request, response); - - } else if (request.getParameter("code") != null) { - - // Which OIDC configuration? - Cookie issuerCookie = WebUtils.getCookie(request, ISSUER_COOKIE_NAME); - - try { - return handleAuthorizationGrantResponse(request.getParameter("code"), request, oidcServerConfigs.get(issuerCookie.getValue())); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } else { - - String issuer = request.getParameter("issuer"); - - if (StringUtils.isNotBlank(issuer)) { - - // Account Chooser UI provided and Issuer Identifier - - OIDCServerConfiguration oidcServerConfig = oidcServerConfigs.get(issuer); - - if (oidcServerConfig != null) { - - // The Client is configured to support this Issuer - // Identifier - - Cookie issuerCookie = new Cookie(ISSUER_COOKIE_NAME, issuer); - response.addCookie(issuerCookie); - - handleAuthorizationRequest(request, response, oidcServerConfig); - - } else { - - // The Client is NOT configured to support this Issuer - // Identifier - - throw new AuthenticationServiceException("Security Filter not configured for issuer: " + issuer); - } - - } else { - - // Redirect the End-User to the configured Account Chooser UI - // application - - Map urlVariables = new HashMap(); - - urlVariables.put("redirect_uri", buildRedirectURI(request, null)); - - urlVariables.put("client_id", accountChooserClientID); - - response.sendRedirect(OIDCAuthenticationUsingChooserFilter - .buildURL(accountChooserURI, urlVariables)); - } - } - - return null; - } - - public void setAccountChooserClientID(String accountChooserClientID) { - this.accountChooserClientID = accountChooserClientID; - } - - public void setAccountChooserURI(String accountChooserURI) { - this.accountChooserURI = accountChooserURI; - } - - public void setOidcServerConfigs( - Map oidcServerConfigs) { - this.oidcServerConfigs = oidcServerConfigs; - } -} diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCSignedRequestFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCSignedRequestFilter.java deleted file mode 100644 index 84398263c..000000000 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCSignedRequestFilter.java +++ /dev/null @@ -1,256 +0,0 @@ -package org.mitre.openid.connect.client; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.apache.commons.lang.StringUtils; -import org.mitre.jwt.signer.service.JwtSigningAndValidationService; -import org.mitre.openid.connect.config.OIDCServerConfiguration; -import org.mitre.openid.connect.view.JwkKeyListView; -import org.mitre.openid.connect.view.X509CertificateView; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.util.Assert; -import org.springframework.web.servlet.ModelAndView; - -import com.google.common.base.Strings; -import com.nimbusds.jose.JWSHeader; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; - -public class OIDCSignedRequestFilter extends AbstractOIDCAuthenticationFilter { - - private OIDCServerConfiguration oidcServerConfig; - - private JwtSigningAndValidationService signingAndValidationService; - - protected OIDCSignedRequestFilter() { - super(); - - oidcServerConfig = new OIDCServerConfiguration(); - } - - @Override - public void afterPropertiesSet() { - super.afterPropertiesSet(); - - // Validating configuration - - Assert.notNull(oidcServerConfig.getAuthorizationEndpointUrl(), - "An Authorization Endpoint URI must be supplied"); - - Assert.notNull(oidcServerConfig.getTokenEndpointUrl(), - "A Token Endpoint URI must be supplied"); - - Assert.notNull(oidcServerConfig.getClientId(), - "A Client ID must be supplied"); - - Assert.notNull(oidcServerConfig.getClientSecret(), - "A Client Secret must be supplied"); - - } - - @Override - public Authentication attemptAuthentication(HttpServletRequest request, - HttpServletResponse response) throws AuthenticationException, - IOException, ServletException { - - // Enter AuthenticationFilter here... - - super.attemptAuthentication(request, response); - - if (StringUtils.isNotBlank(request.getParameter("error"))) { - - handleError(request, response); - - } else if (StringUtils.isNotBlank(request.getParameter("code"))) { - - try { - return handleAuthorizationGrantResponse(request.getParameter("code"), request, oidcServerConfig); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } else { - - handleAuthorizationRequest(request, response, oidcServerConfig); - } - - return null; - } - - @Override - public void handleAuthorizationRequest(HttpServletRequest request, HttpServletResponse response, - OIDCServerConfiguration serverConfiguration) throws IOException { - - SignedJWT jwt = createAndSignRequestJwt(request, response, serverConfiguration); - - Map urlVariables = new HashMap(); - - urlVariables.put("request", jwt.serialize()); - - String authRequest = AbstractOIDCAuthenticationFilter.buildURL(serverConfiguration.getAuthorizationEndpointUrl(), urlVariables); - - logger.debug("Auth Request: " + authRequest); - - response.sendRedirect(authRequest); - } - - public SignedJWT createAndSignRequestJwt(HttpServletRequest request, HttpServletResponse response, OIDCServerConfiguration serverConfiguration) { - HttpSession session = request.getSession(); - - JWTClaimsSet claims = new JWTClaimsSet(); - - //set parameters to JwtHeader -// header.setAlgorithm(JwsAlgorithm.getByName(SIGNING_ALGORITHM).toString()); - - //set parameters to JwtClaims - claims.setCustomClaim("response_type", "code"); - claims.setCustomClaim("client_id", serverConfiguration.getClientId()); - claims.setCustomClaim("scope", scope); - - // build our redirect URI - String redirectUri = buildRedirectURI(request, null); - claims.setCustomClaim("redirect_uri", redirectUri); - session.setAttribute(REDIRECT_URI_SESION_VARIABLE, redirectUri); - - //create random nonce and state, save them to the session - - String nonce = createNonce(session); - claims.setCustomClaim("nonce", nonce); - - String state = createState(session); - claims.setCustomClaim("state", state); - - SignedJWT jwt = new SignedJWT(new JWSHeader(serverConfiguration.getSigningAlgorithm()), claims); - - try { - signingAndValidationService.signJwt(jwt); - } catch (NoSuchAlgorithmException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - return jwt; - } - - /** - * @return the signingAndValidationService - */ - public JwtSigningAndValidationService getSigningAndValidationService() { - return signingAndValidationService; - } - - /** - * @param signingAndValidationService the signingAndValidationService to set - */ - public void setSigningAndValidationService(JwtSigningAndValidationService signingAndValidationService) { - this.signingAndValidationService = signingAndValidationService; - } - - /** - * @param authorizationEndpointURI - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setAuthorizationEndpointUrl(java.lang.String) - */ - public void setAuthorizationEndpointUrl(String authorizationEndpointURI) { - oidcServerConfig.setAuthorizationEndpointUrl(authorizationEndpointURI); - } - - /** - * @param clientId - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setClientId(java.lang.String) - */ - public void setClientId(String clientId) { - oidcServerConfig.setClientId(clientId); - } - - /** - * @param issuer - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setIssuer(java.lang.String) - */ - public void setIssuer(String issuer) { - oidcServerConfig.setIssuer(issuer); - } - - /** - * @param clientSecret - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setClientSecret(java.lang.String) - */ - public void setClientSecret(String clientSecret) { - oidcServerConfig.setClientSecret(clientSecret); - } - - /** - * @param tokenEndpointURI - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setTokenEndpointUrl(java.lang.String) - */ - public void setTokenEndpointUrl(String tokenEndpointURI) { - oidcServerConfig.setTokenEndpointUrl(tokenEndpointURI); - } - - /** - * @param x509EncryptUrl - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setX509EncryptUrl(java.lang.String) - */ - public void setX509EncryptUrl(String x509EncryptUrl) { - oidcServerConfig.setX509EncryptUrl(x509EncryptUrl); - } - - /** - * @param x509SigningUrl - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setX509SigningUrl(java.lang.String) - */ - public void setX509SigningUrl(String x509SigningUrl) { - oidcServerConfig.setX509SigningUrl(x509SigningUrl); - } - - /** - * @param jwkEncryptUrl - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setJwkEncryptUrl(java.lang.String) - */ - public void setJwkEncryptUrl(String jwkEncryptUrl) { - oidcServerConfig.setJwkEncryptUrl(jwkEncryptUrl); - } - - /** - * @param jwkSigningUrl - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setJwkSigningUrl(java.lang.String) - */ - public void setJwkSigningUrl(String jwkSigningUrl) { - oidcServerConfig.setJwkSigningUrl(jwkSigningUrl); - } - - /** - * @param userInfoUrl - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setUserInfoUrl(java.lang.String) - */ - public void setUserInfoUrl(String userInfoUrl) { - oidcServerConfig.setUserInfoUrl(userInfoUrl); - } - - /** - * @param algorithmName - * @see org.mitre.openid.connect.config.OIDCServerConfiguration#setAlgorithmName(java.lang.String) - */ - public void setAlgorithmName(String algorithmName) { - oidcServerConfig.setAlgorithmName(algorithmName); - } - - - -} diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java index 1ead26241..c2a1c210a 100644 --- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java @@ -26,7 +26,7 @@ public class UserInfoFetcher { form.add("access_token", token.getAccessTokenValue()); form.add("schema", "openid"); - String userInfoString = restTemplate.postForObject(token.getServerConfiguration().getUserInfoUrl(), form, String.class); + String userInfoString = restTemplate.postForObject(token.getServerConfiguration().getUserInfoUri(), form, String.class); JsonObject userInfoJson = new JsonParser().parse(userInfoString).getAsJsonObject(); 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 new file mode 100644 index 000000000..b34b07192 --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java @@ -0,0 +1,25 @@ +/** + * + */ +package org.mitre.openid.connect.client.service; + +import org.mitre.openid.connect.config.ServerConfiguration; +import org.springframework.security.oauth2.provider.ClientDetails; + +/** + * @author jricher + * + */ +public interface AuthRequestUrlBuilder { + + /** + * @param serverConfig + * @param clientConfig + * @param redirectUri + * @param nonce + * @param state + * @return + */ + public String buildAuthRequestUrl(ServerConfiguration serverConfig, ClientDetails clientConfig, String redirectUri, String nonce, String state); + +} diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ClientConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ClientConfigurationService.java new file mode 100644 index 000000000..cd4404eac --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ClientConfigurationService.java @@ -0,0 +1,16 @@ +/** + * + */ +package org.mitre.openid.connect.client.service; + +import org.springframework.security.oauth2.provider.ClientDetails; + +/** + * @author jricher + * + */ +public interface ClientConfigurationService { + + public ClientDetails getClientConfiguration(String issuer); + +} diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/IssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/IssuerService.java new file mode 100644 index 000000000..4abd092ba --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/IssuerService.java @@ -0,0 +1,19 @@ +/** + * + */ +package org.mitre.openid.connect.client.service; + +import javax.servlet.http.HttpServletRequest; + +/** + * + * Gets an issuer for the given request. Might do dynamic discovery, or might be statically configured. + * + * @author jricher + * + */ +public interface IssuerService { + + public String getIssuer(HttpServletRequest request); + +} diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ServerConfigurationService.java new file mode 100644 index 000000000..a18fad2ae --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ServerConfigurationService.java @@ -0,0 +1,16 @@ +/** + * + */ +package org.mitre.openid.connect.client.service; + +import org.mitre.openid.connect.config.ServerConfiguration; + +/** + * @author jricher + * + */ +public interface ServerConfigurationService { + + public ServerConfiguration getServerConfiguration(String issuer); + +} 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 new file mode 100644 index 000000000..e63fcc5f8 --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java @@ -0,0 +1,58 @@ +/** + * + */ +package org.mitre.openid.connect.client.service.impl; + +import java.net.URISyntaxException; + +import org.apache.http.client.utils.URIBuilder; +import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder; +import org.mitre.openid.connect.config.ServerConfiguration; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.oauth2.provider.ClientDetails; + +import com.google.common.base.Joiner; + +/** + * + * Builds an auth request redirect URI with normal query parameters. + * + * @author jricher + * + */ +public class PlainAuthRequestUrlBuilder implements AuthRequestUrlBuilder { + + /* (non-Javadoc) + * @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, ClientDetails clientConfig, String redirectUri, String nonce, String state) { + try { + + URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri()); + uriBuilder.addParameter("response_type", "code"); + uriBuilder.addParameter("client_id", clientConfig.getClientId()); + uriBuilder.addParameter("scope", Joiner.on(" ").join(clientConfig.getScope())); + + uriBuilder.addParameter("redirect_uri", redirectUri); + + uriBuilder.addParameter("nonce", nonce); + + uriBuilder.addParameter("state", state); + + // Optional parameters: + + // TODO: display, prompt + + return uriBuilder.build().toString(); + + } catch (URISyntaxException e) { + throw new AuthenticationServiceException("Malformed Authorization Endpoint Uri", e); + + } + + + + } + +} 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 new file mode 100644 index 000000000..29168fae9 --- /dev/null +++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java @@ -0,0 +1,76 @@ +/** + * + */ +package org.mitre.openid.connect.client.service.impl; + +import java.net.URISyntaxException; +import java.security.NoSuchAlgorithmException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.http.client.utils.URIBuilder; +import org.mitre.jwt.signer.service.JwtSigningAndValidationService; +import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder; +import org.mitre.openid.connect.config.ServerConfiguration; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.oauth2.provider.ClientDetails; + +import com.google.common.base.Joiner; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +/** + * @author jricher + * + */ +public class SignedAuthRequestUrlBuilder implements AuthRequestUrlBuilder { + + private JwtSigningAndValidationService signingAndValidationService; + + /* (non-Javadoc) + * @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, ClientDetails clientConfig, String redirectUri, String nonce, String state) { + + // create our signed JWT for the request object + JWTClaimsSet claims = new JWTClaimsSet(); + + //set parameters to JwtClaims + claims.setCustomClaim("response_type", "code"); + claims.setCustomClaim("client_id", clientConfig.getClientId()); + claims.setCustomClaim("scope", Joiner.on(" ").join(clientConfig.getScope())); + + // build our redirect URI + claims.setCustomClaim("redirect_uri", redirectUri); + + // this comes back in the id token + claims.setCustomClaim("nonce", nonce); + + // this comes back in the auth request return + claims.setCustomClaim("state", state); + + SignedJWT jwt = new SignedJWT(new JWSHeader(signingAndValidationService.getDefaultSigningAlgorithm()), claims); + + try { + signingAndValidationService.signJwt(jwt); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri()); + uriBuilder.addParameter("request", jwt.serialize()); + + // build out the URI + return uriBuilder.build().toString(); + } catch (URISyntaxException e) { + throw new AuthenticationServiceException("Malformed Authorization Endpoint Uri", e); + } + } + +} diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilterTest.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilterTest.java index 07e295302..db9994c59 100644 --- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilterTest.java +++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/AbstractOIDCAuthenticationFilterTest.java @@ -7,7 +7,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** - * Unit test for AbstractOIDCAuthenticationFilter + * Unit test for OIDCAuthenticationFilter * * @author amanda * @@ -17,7 +17,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; public class AbstractOIDCAuthenticationFilterTest { //@Autowired - private AbstractOIDCAuthenticationFilter filter; + private OIDCAuthenticationFilter filter; //@Test public void testUrlConstruction() { @@ -27,14 +27,14 @@ public class AbstractOIDCAuthenticationFilterTest { /** * @return the filter */ - public AbstractOIDCAuthenticationFilter getFilter() { + public OIDCAuthenticationFilter getFilter() { return filter; } /** * @param filter the filter to set */ - public void setFilter(AbstractOIDCAuthenticationFilter filter) { + public void setFilter(OIDCAuthenticationFilter filter) { this.filter = filter; } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/OIDCServerConfiguration.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/OIDCServerConfiguration.java deleted file mode 100644 index c1199eced..000000000 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/OIDCServerConfiguration.java +++ /dev/null @@ -1,186 +0,0 @@ -/******************************************************************************* - * Copyright 2012 The MITRE Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package org.mitre.openid.connect.config; - -import javax.persistence.Basic; - -import com.nimbusds.jose.JWSAlgorithm; - - -/** - * @author nemonik - * - */ -public class OIDCServerConfiguration { - - private String authorizationEndpointUrl; - - private String tokenEndpointUrl; - - private String clientSecret; - - private String clientId; - - private String issuer; - - private String x509EncryptUrl; - - private String x509SigningUrl; - - private String jwkEncryptUrl; - - private String jwkSigningUrl; - - private String userInfoUrl; - - private JWSAlgorithm signingAlgorithm; - - public String getAuthorizationEndpointUrl() { - return authorizationEndpointUrl; - } - - public String getClientId() { - return clientId; - } - - public String getIssuer() { - return issuer; - } - - public String getClientSecret() { - return clientSecret; - } - - public String getTokenEndpointUrl() { - return tokenEndpointUrl; - } - - public void setAuthorizationEndpointUrl(String authorizationEndpointURI) { - this.authorizationEndpointUrl = authorizationEndpointURI; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public void setIssuer(String issuer) { - this.issuer = issuer; - } - - public void setClientSecret(String clientSecret) { - this.clientSecret = clientSecret; - } - - public void setTokenEndpointUrl(String tokenEndpointURI) { - this.tokenEndpointUrl = tokenEndpointURI; - } - - public String getX509EncryptUrl() { - return x509EncryptUrl; - } - - public String getX509SigningUrl() { - return x509SigningUrl; - } - - public String getJwkEncryptUrl() { - return jwkEncryptUrl; - } - - public String getJwkSigningUrl() { - return jwkSigningUrl; - } - - public void setX509EncryptUrl(String x509EncryptUrl) { - this.x509EncryptUrl = x509EncryptUrl; - } - - public void setX509SigningUrl(String x509SigningUrl) { - this.x509SigningUrl = x509SigningUrl; - } - - public void setJwkEncryptUrl(String jwkEncryptUrl) { - this.jwkEncryptUrl = jwkEncryptUrl; - } - - public void setJwkSigningUrl(String jwkSigningUrl) { - this.jwkSigningUrl = jwkSigningUrl; - } - - /** - * @return the userInfoUrl - */ - public String getUserInfoUrl() { - return userInfoUrl; - } - - /** - * @param userInfoUrl the userInfoUrl to set - */ - public void setUserInfoUrl(String userInfoUrl) { - this.userInfoUrl = userInfoUrl; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "OIDCServerConfiguration [authorizationEndpointUrl=" + authorizationEndpointUrl + ", tokenEndpointUrl=" + tokenEndpointUrl + ", clientSecret=" + clientSecret + ", clientId=" + clientId + ", issuer=" + issuer + ", x509EncryptUrl=" + x509EncryptUrl + ", x509SigningUrl=" - + x509SigningUrl + ", jwkEncryptUrl=" + jwkEncryptUrl + ", jwkSigningUrl=" + jwkSigningUrl + ", userInfoUrl=" + userInfoUrl + "]"; - } - - /** - * @return the signingAlgorithm - */ - public JWSAlgorithm getSigningAlgorithm() { - return signingAlgorithm; - } - - /** - * @param signingAlgorithm the signingAlgorithm to set - */ - public void setSigningAlgorithm(JWSAlgorithm signingAlgorithm) { - this.signingAlgorithm = signingAlgorithm; - } - - /** - * Get the name of this algorithm, return null if no algorithm set. - * @return - */ - @Basic - public String getAlgorithmName() { - if (signingAlgorithm != null) { - return signingAlgorithm.getName(); - } else { - return null; - } - } - - /** - * Set the name of this algorithm. - * Calls JWSAlgorithm.parse() - * @param algorithmName - */ - public void setAlgorithmName(String algorithmName) { - if (algorithmName != null) { - signingAlgorithm = JWSAlgorithm.parse(algorithmName); - } else { - signingAlgorithm = null; - } - } - -} \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ServerConfiguration.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ServerConfiguration.java new file mode 100644 index 000000000..0ad8f0e4b --- /dev/null +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/config/ServerConfiguration.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright 2012 The MITRE Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.mitre.openid.connect.config; + + + +/** + * + * Container class for a client's view of a server's configuration + * + * @author nemonik, jricher + * + */ +public class ServerConfiguration { + + private String authorizationEndpointUri; + + private String tokenEndpointUri; + + private String issuer; + + private String jwksUri; + + private String userInfoUri; + + /** + * @return the authorizationEndpointUri + */ + public String getAuthorizationEndpointUri() { + return authorizationEndpointUri; + } + + /** + * @param authorizationEndpointUri the authorizationEndpointUri to set + */ + public void setAuthorizationEndpointUri(String authorizationEndpointUri) { + this.authorizationEndpointUri = authorizationEndpointUri; + } + + /** + * @return the tokenEndpointUri + */ + public String getTokenEndpointUri() { + return tokenEndpointUri; + } + + /** + * @param tokenEndpointUri the tokenEndpointUri to set + */ + public void setTokenEndpointUri(String tokenEndpointUri) { + this.tokenEndpointUri = tokenEndpointUri; + } + + /** + * @return the issuer + */ + public String getIssuer() { + return issuer; + } + + /** + * @param issuer the issuer to set + */ + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + /** + * @return the jwksUri + */ + public String getJwksUri() { + return jwksUri; + } + + /** + * @param jwksUri the jwksUri to set + */ + public void setJwksUri(String jwksUri) { + this.jwksUri = jwksUri; + } + + /** + * @return the userInfoUri + */ + public String getUserInfoUri() { + return userInfoUri; + } + + /** + * @param userInfoUri the userInfoUri to set + */ + public void setUserInfoUri(String userInfoUri) { + this.userInfoUri = userInfoUri; + } + +} \ No newline at end of file diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JwkKeyListView.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JwkKeyListView.java index e9c73e348..5b22c8d4c 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JwkKeyListView.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/view/JwkKeyListView.java @@ -80,7 +80,7 @@ public class JwkKeyListView extends AbstractView { BigInteger mod = rsa.getModulus(); BigInteger exp = rsa.getPublicExponent(); - + RSAKey rsaKey = new RSAKey(Base64URL.encode(mod.toByteArray()), Base64URL.encode(exp.toByteArray()), Use.SIGNATURE, JWSAlgorithm.RS256, keyId); jwks.add(rsaKey); diff --git a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/JsonWebKeyEndpoint.java b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/JsonWebKeyEndpoint.java index 54293eedb..496f87dcf 100644 --- a/openid-connect-server/src/main/java/org/mitre/openid/connect/web/JsonWebKeyEndpoint.java +++ b/openid-connect-server/src/main/java/org/mitre/openid/connect/web/JsonWebKeyEndpoint.java @@ -21,6 +21,7 @@ import java.util.Map; import org.mitre.jwt.signer.service.JwtSigningAndValidationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; @@ -28,29 +29,21 @@ import org.springframework.web.servlet.ModelAndView; public class JsonWebKeyEndpoint { @Autowired - JwtSigningAndValidationService jwtService; + private JwtSigningAndValidationService jwtService; @RequestMapping(value = "/jwk", produces = "application/json") - public ModelAndView getJwk() { + public String getJwk(Model m) { // map from key id to signer Map keys = jwtService.getAllPublicKeys(); // TODO: check if keys are empty, return a 404 here or just an empty list? - return new ModelAndView("jwkKeyList", "keys", keys); + m.addAttribute("keys", keys); + + return "jwkKeyList"; } - @RequestMapping("/x509") - public ModelAndView getX509() { - // map from key id to signer - Map keys = jwtService.getAllPublicKeys(); - - // TODO: check if keys are empty, return a 404 here or just an empty list? - - return new ModelAndView("x509certs", "keys", keys); - } - /** * @return the jwtService */