Merge remote branch 'origin/master'
commit
abf3f0ec33
|
@ -2,33 +2,24 @@
|
|||
|
||||
## Overview
|
||||
|
||||
This is Web application created in response to [Issue #39] to permit the Client AuthenticationFilter to speak to multiple OpenID Connect servers.
|
||||
This is Web application created in response to [Issue #39] to permit the Client AuthenticationFilter to speak to multiple OpenID Connect Servers. The protocol between the Clinent and the Account Chooser UI application is documented the README.md of the openid-connect-client submodule.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Configure a bean configuration to the spring-servlet.xml like so:
|
||||
Configure AccountChooserController via configuring a AccountChooserConfig bean in the spring-servlet.xml like so:
|
||||
|
||||
<bean class="org.mitre.account_chooser.OIDCServers">
|
||||
<property name="servers">
|
||||
<bean name="AccountChooserConfig" class="org.mitre.account_chooser.AccountChooserConfig">
|
||||
<property name="issuers">
|
||||
<map>
|
||||
<entry key="1">
|
||||
<entry key="http://sever.example.com:8080/openid-connect-server">
|
||||
<bean class="org.mitre.account_chooser.OIDCServer">
|
||||
<property name="name" value="OIDC Server 1" />
|
||||
</bean>
|
||||
</entry>
|
||||
<entry key="2">
|
||||
<bean class="org.mitre.account_chooser.OIDCServer">
|
||||
<property name="name" value="OIDC Server 2" />
|
||||
</bean>
|
||||
</entry>
|
||||
<entry key="3">
|
||||
<bean class="org.mitre.account_chooser.OIDCServer">
|
||||
<property name="name" value="OIDC Server 3" />
|
||||
<property name="name" value="Example Server" />
|
||||
</bean>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
<property name="validClientIds" value="FGWEUIASJK, IUYTTYEV, GFHDSFYD" />
|
||||
</bean>
|
||||
|
||||
|
||||
|
@ -36,9 +27,15 @@ The keys must match those found in the OpenIdConnectAuthenticationFilter's confi
|
|||
|
||||
<bean id="openIdConnectAuthenticationFilter"
|
||||
class="org.mitre.openid.connect.client.OpenIdConnectAuthenticationFilter">
|
||||
<property name="OIDCServers">
|
||||
<property name="errorRedirectURI" value="/login.jsp?authfail=openid" />
|
||||
<property name="authenticationManager" ref="authenticationManager" />
|
||||
<property name="accountChooserURI"
|
||||
value="http://sever.example.com:8080/account-chooser" />
|
||||
<property name="accountChooserClientID" value="FGWEUIASJK" />
|
||||
<property name="oidcServerConfigs">
|
||||
<map>
|
||||
<entry key="1">
|
||||
<entry key="http://sever.example.com:8080/Fopenid-connect-server">
|
||||
<bean class="org.mitre.openid.connect.client.OIDCServerConfiguration">
|
||||
<property name="authorizationEndpointURI"
|
||||
value="http://sever.example.com:8080/openid-connect-server/oauth/authorize" />
|
||||
<property name="tokenEndpointURI"
|
||||
|
@ -48,7 +45,17 @@ The keys must match those found in the OpenIdConnectAuthenticationFilter's confi
|
|||
<property name="clientId"
|
||||
value="someClientId" />
|
||||
<property name="clientSecret" value="someClientSecret" />
|
||||
</bean>
|
||||
</entry>
|
||||
…
|
||||
. . .
|
||||
|
||||
|
||||
## Test the Default Configuration
|
||||
|
||||
To test the default config, deploy to a servlet container, and request:
|
||||
|
||||
http://localhost:8080/account-chooser/?redirect_uri=http://www.google.com&client_id=FGWEUIASJK
|
||||
|
||||
Click **Submit** or **Cancel**, and you will be Google will open. Study the URL parameters of each.
|
||||
|
||||
[Issue #39]: http://github.com/jricher/OpenID-Connect-Java-Spring-Server/issues/39 "Issue #39 -- Multiple Point Client"
|
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
|
@ -0,0 +1,127 @@
|
|||
# OpenID Connect Client - Account Chooser UI Application Protocol
|
||||
|
||||
## Overview
|
||||
|
||||
This Client - Account Chooser Protocol proposed in response to [Issue #39].
|
||||
|
||||
|
||||
#### Authorization when using Account Chooser Code Flow
|
||||
|
||||
The Authorization when using Account Chooser Code Flow goes through the following steps:
|
||||
|
||||
1. Client prepares an Account Chooser Request containing the desired request parameters.
|
||||
2. Client sends a request to the Account Chooser.
|
||||
3. Account Chooser presents a selection of OpenID Connect (OIDC) Servers from which the End-User must select from.
|
||||
4. End-User selects an OIDC.
|
||||
5. Account Chooser Sends the End-User back to the Client with key value of the OIDC End-User selected.
|
||||
6. The Client begins the Authorization flow described in [Authorization Code Flow][OpenID Connect Standard] of the [OpenID Connect Standard].
|
||||
|
||||
#### Account Chooser Request
|
||||
|
||||
When the End-User wishes to access a Protected Resource and the End-User Authorization has not yet been obtained, the Client will redirect the End-User to Account Chooser.
|
||||
|
||||
Account Chooser MUST support the use of the HTTP "GET" and "POST" methods defined in RFC 2616 [RFC2616].
|
||||
|
||||
Clients MAY use the HTTP "GET" or "POST" method to send the Account Chooser Request to the Account Chooser. If using the HTTP "GET" method, the request parameters are serialized using URI query string serialization. If using the HTTP "POST" method, the request parameters are serialized using form serialization.
|
||||
|
||||
#### Client Prepares an Account Chooser Request
|
||||
|
||||
The Client prepares an Account Chooser Request to the Account Chooser with the request parameters using the HTTP "GET" or "POST" method.
|
||||
|
||||
The required Account Chooser Request parameters are as follows:
|
||||
|
||||
* redirect_uri - REQUIRED. A redirection URI where the response will be sent.
|
||||
* client_id - REQUIRED. An identifier used to identify the Client to the Account Chooser UI application.
|
||||
|
||||
There is one method to construct and send the request to the Account Chooser:
|
||||
|
||||
* Simple Request Method
|
||||
|
||||
#### Simple Request Method
|
||||
|
||||
The Client prepares an Account Chooser Request to the Account Chooser using the appropriate parameters. If using the HTTP "GET" method, the request parameters are serialized using URI query string serialization. If using the HTTP "POST" method, the request parameters are serialized using form serialization.
|
||||
|
||||
The following is a non-normative example of an Account Chooser Request URL. Line wraps are for display purposes only.
|
||||
|
||||
http://server.example.com/chooser?
|
||||
redirect_uri=https%3A%2F%2Fclient.example.com%2Fopenid_connect_login
|
||||
&client_id=FGWEUIASJK
|
||||
|
||||
#### Client sends a request to the Account Chooser
|
||||
|
||||
Having constructed the Account Chooser Request, the Client sends it to the Account Chooser. This MAY happen via redirect, hyperlinking, or any other means of directing the User-Agent to the Account Chooser URL.
|
||||
|
||||
Following is a non-normative example using HTTP redirect. Line wraps are for display purposes only.
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://server.example.com/chooser?
|
||||
redirect_uri=https%3A%2F%2Fclient.example.com%2Fopenid_connect_login
|
||||
&client_id=FGWEUIASJK
|
||||
|
||||
#### Account Chooser Sends the End-User back to the Client
|
||||
|
||||
After the End-User has select an OpenID Connect Server, the Client issues an Account Chooser Response and delivers it to the Client by adding the response parameters to redirect_uri specified in the Account Choose Request using the "application/x-www-form-urlencoded" format.
|
||||
|
||||
The following response parameters are included:
|
||||
|
||||
* issuer - REQUIRED. The [Issuer Identifier] describing the selected account.
|
||||
|
||||
The following is non-normative example of a responses. Line wraps are for display purposes only.
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://client.example.com/openid_connect_login?
|
||||
issuer=http%3A%2F%2Fsever.example.com%3A8080%2Fopenid-connect-server
|
||||
|
||||
A sequence diagram of a successful interaction would be:
|
||||
|
||||

|
||||
|
||||
#### The Client ID is not Supported by the Account Chooser Application
|
||||
|
||||
If the Client sends a Client identifier that is not supported by the Account Chooser UI application, the Account Chooser MUST return an error response back to the End-User by issuing a HTTP Response with a message concerning the error. The Account Chooser MUST NOT directly return to the Client via the redirection URI specified in the Account Chooser Request automatically.
|
||||
|
||||
The error message the Account Chooser presents to the End-User MAY give the End-User the means to return the indication of an error to the Client, e.g., via the End-User clicking on a HTML link built from adding the error parameters to the query component of the redirection URI. No other parameters SHOULD be returned.
|
||||
|
||||
The error response parameters are the following:
|
||||
|
||||
* error - REQUIRED. The error code.
|
||||
* error_description - OPTIONAL. A human-readable UTF-8 encoded text description of the error.
|
||||
|
||||
The response parameters are added to the query component of the redirection URI specified in the Account Chooser Request.
|
||||
|
||||
The following is a non-normative example. Line wraps after the second line are for the display purposes only.
|
||||
|
||||
https://client.example.com/openid_connect_login?
|
||||
error=not_supported
|
||||
&error_description=The%20client_id%20is%20not%20supported%20by%20the%20AccountChooser%20UI%20application.
|
||||
|
||||
A sequence diagram of the interaction where the Account Chooser was not configured to support the Client would be:
|
||||
|
||||

|
||||
|
||||
#### End-User refuses to select an Account
|
||||
|
||||
If the End-User refuses to select an Account, the Account Chooser MUST return an error response. The Account Chooser returns the Client via the redirection URI specified in the Account Chooser Request with the appropriate error parameters. No other parameters SHOULD be returned.
|
||||
|
||||
The error response parameters are the following:
|
||||
|
||||
* error - REQUIRED. The error code.
|
||||
* error_description - OPTIONAL. A human-readable UTF-8 encoded text description of the error.
|
||||
|
||||
The response parameters are added to the query component of the redirection URI specified in the Account Chooser Request.
|
||||
|
||||
The following is a non-normative example. Line wraps after the second line are for the display purposes only.
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://client.example.com/openid_connect_login?
|
||||
error=end_user_cancelled
|
||||
&error_description=The%20end%2Duser%20refused%20to%20select%20an%20Account.
|
||||
|
||||
A sequence diagram of an interaction where the End-User refused to select an Account would be:
|
||||
|
||||

|
||||
|
||||
[OpenID Connect Standard]: http://openid.net/specs/openid-connect-standard-1_0.html "OpenID Connect Standard 1.0"
|
||||
[OpenID Connect Standard]: http://openid.net/specs/openid-connect-standard-1_0.html#code_flow "Authorization Code Flow, OpenID Connect Standard"
|
||||
[Issuer Identifier]: http://openid.net/specs/openid-connect-messages-1_0.html#issuer_identifier "Issuer Identifier"
|
||||
[Issue #39]: http://github.com/jricher/OpenID-Connect-Java-Spring-Server/issues/39 "Issue #39 -- Multiple Point Client"
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1,50 @@
|
|||
Text used to create sequence diagrams @ http://www.websequencediagrams.com/
|
||||
|
||||
|
||||
A Successful Client and Account Chooser Interaction
|
||||
|
||||
Client->End-User Browser: HTTP-Redirect\n to Account\n Chooser URL\nwith redirect_uri,\n and client_id\n parameters
|
||||
activate End-User Browser
|
||||
End-User Browser-->Account Chooser:HTTP Get/Put\n Request containing\n redirect_uri\n and client_id
|
||||
note right of "Account Chooser": Client Id\n is verified as\n supported, and the\n End-User selects\n an Account
|
||||
deactivate End-User Browser
|
||||
activate Account Chooser
|
||||
Account Chooser-->End-User Browser:HTTP-Redirect to \nAccount Chooser URL\n with issuer parameters
|
||||
activate End-User Browser
|
||||
deactivate Account Chooser
|
||||
End-User Browser->Client: HTTP Get\n Request containing the\n issuer parameter
|
||||
activate Client
|
||||
deactivate End-User Browser
|
||||
|
||||
|
||||
The Client ID is not Supported by the Account Chooser Application
|
||||
|
||||
Client->End-User Browser: HTTP-Redirect to\n Account Chooser URL\n w/ redirect_uri,\n and client_id parameters
|
||||
activate End-User Browser
|
||||
End-User Browser-->Account Chooser:HTTP Get/Put\n Request containing\n redirect_uri and client_id
|
||||
note right of "Account Chooser": Client Id is\nnot supported.\nSo, an error must\nbe returned.
|
||||
activate Account Chooser
|
||||
deactivate End-User Browser
|
||||
Account Chooser-->End-User Browser:Returns an\nHTTP Response\ncontaining an\nerror message.
|
||||
deactivate Account Chooser
|
||||
note right of "End-User Browser":The End-User is presented\nan error message\nthus ending the interaction.\n\nOptionally, the message\nMAY contain a HTML link\nto return the End-User\nback to the Client with\nan error and error description\nadded to the query component\nof the redirect_uri.
|
||||
activate End-User Browser
|
||||
End-User Browser->Client: If the End-User\nis presented and clicks\non a HTML link,\na HTTP "GET"\n Request containing the\nerror, and error_description\nparameter
|
||||
activate Client
|
||||
deactivate Account Chooser
|
||||
|
||||
|
||||
End-User Cancels Account Selection
|
||||
|
||||
Client->End-User Browser: HTTP-Redirect to\n Account Chooser URL\nwith redirect_uri,\nand client_id parameters
|
||||
activate End-User Browser
|
||||
End-User Browser-->Account Chooser:HTTP Get/Put\n Request containing\nredirect_uri and client_id
|
||||
note right of "Account Chooser": End-User refuses\nto select an\naccount via\ncancelling.
|
||||
deactivate End-User Browser
|
||||
activate Account Chooser
|
||||
Account Chooser-->End-User Browser:HTTP-Redirect\n to Account Chooser\n URL with error,\nand error_description\nparameters
|
||||
activate End-User Browser
|
||||
deactivate Account Chooser
|
||||
End-User Browser->Client: HTTP Get Request\ncontaining the error,\nand error_description\nparameter
|
||||
activate Client
|
||||
deactivate End-User Browser
|
|
@ -0,0 +1,49 @@
|
|||
/*******************************************************************************
|
||||
* 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.account_chooser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used to the configure AccountChooserController
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class AccountChooserConfig {
|
||||
|
||||
private String[] validClientIds;
|
||||
|
||||
private Map<String, ? extends OIDCServer> issuers = new HashMap<String, OIDCServer>();
|
||||
|
||||
public Map<String, ? extends OIDCServer> getIssuers() {
|
||||
return issuers;
|
||||
}
|
||||
|
||||
public String[] getValidClientIds() {
|
||||
return validClientIds;
|
||||
}
|
||||
|
||||
public void setIssuers(Map<String, ? extends OIDCServer> issuers) {
|
||||
this.issuers = issuers;
|
||||
}
|
||||
|
||||
public void setValidClientIds(String[] validClientIds) {
|
||||
|
||||
this.validClientIds = validClientIds;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,11 @@
|
|||
package org.mitre.account_chooser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -39,8 +44,43 @@ import org.springframework.web.servlet.ModelAndView;
|
|||
@Controller
|
||||
public class AccountChooserController {
|
||||
|
||||
/**
|
||||
* 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<String, String> queryStringFields) {
|
||||
|
||||
StringBuilder URLBuilder = new StringBuilder(baseURI);
|
||||
|
||||
char appendChar = '?';
|
||||
|
||||
for (Map.Entry<String, String> param : queryStringFields.entrySet()) {
|
||||
try {
|
||||
URLBuilder.append(appendChar).append(param.getKey())
|
||||
.append('=')
|
||||
.append(URLEncoder.encode(param.getValue(), "UTF-8"));
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
throw new IllegalStateException(uee);
|
||||
}
|
||||
appendChar = '&';
|
||||
}
|
||||
|
||||
return URLBuilder.toString();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
OIDCServers servers;
|
||||
AccountChooserConfig accountChooserConfig;
|
||||
|
||||
private static Log logger = LogFactory
|
||||
.getLog(AccountChooserController.class);
|
||||
|
@ -51,18 +91,51 @@ public class AccountChooserController {
|
|||
* @param redirectUri
|
||||
* A redirection URI where the response will be sent
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@RequestMapping(value = "/", method = { RequestMethod.GET,
|
||||
RequestMethod.POST })
|
||||
public ModelAndView handleChooserRequest(
|
||||
@RequestParam("redirect_uri") String redirectUri) {
|
||||
@RequestParam("redirect_uri") String redirectUri,
|
||||
@RequestParam("client_id") String clientId,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
ModelAndView modelAndView = new ModelAndView("form");
|
||||
modelAndView.addObject("servers", servers);
|
||||
ModelAndView modelAndView = null;
|
||||
|
||||
if (Arrays.asList(accountChooserConfig.getValidClientIds()).contains(
|
||||
clientId)) {
|
||||
|
||||
// client_id supported
|
||||
|
||||
modelAndView = new ModelAndView("chooser");
|
||||
modelAndView
|
||||
.addObject("issuers", accountChooserConfig.getIssuers());
|
||||
modelAndView.addObject("redirect_uri", redirectUri);
|
||||
modelAndView.setViewName("chooser");
|
||||
modelAndView.addObject("client_id", clientId);
|
||||
|
||||
} else {
|
||||
|
||||
// client_id not supported
|
||||
|
||||
Map<String, String> urlVariables = new HashMap<String, String>();
|
||||
|
||||
urlVariables.put("error", "not_supported");
|
||||
urlVariables
|
||||
.put("error_description",
|
||||
"The client_id is not supported by the Account Chooser UI application.");
|
||||
|
||||
modelAndView = new ModelAndView("error");
|
||||
|
||||
modelAndView.addObject("error", urlVariables.get("error"));
|
||||
modelAndView.addObject("error_description",
|
||||
urlVariables.get("error_description"));
|
||||
modelAndView.addObject("client_uri", AccountChooserController
|
||||
.buildURL(redirectUri, urlVariables));
|
||||
|
||||
}
|
||||
|
||||
return modelAndView;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,9 +154,50 @@ public class AccountChooserController {
|
|||
*/
|
||||
@RequestMapping(value = "/selected")
|
||||
public void processSubmit(@RequestParam("redirect_uri") String redirectUri,
|
||||
@RequestParam("alias") String alias, HttpServletResponse response)
|
||||
@RequestParam("issuer") String issuer,
|
||||
@RequestParam("client_id") String clientId,
|
||||
HttpServletResponse response)
|
||||
throws IOException {
|
||||
|
||||
response.sendRedirect(redirectUri + "?oidc_alias=" + alias);
|
||||
// Handle Submit
|
||||
|
||||
Map<String, String> urlVariables = new HashMap<String, String>();
|
||||
urlVariables.put("issuer", issuer);
|
||||
|
||||
response.sendRedirect(AccountChooserController.buildURL(redirectUri,
|
||||
urlVariables));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles form submits
|
||||
*
|
||||
* @param redirectUri
|
||||
* A redirection URI where the response will be sent.
|
||||
* @param alias
|
||||
* The OIDC alias selected.
|
||||
* @param response
|
||||
* Provide the HTTP-specific functionality for sending a
|
||||
* response. In this case a redirect to redirect the End-User
|
||||
* back to the OpenID Connect Client.
|
||||
* @throws IOException
|
||||
* If an output exception occurs in sending the redirect.
|
||||
*/
|
||||
@RequestMapping(value = "/cancel")
|
||||
public void processCancel(@RequestParam("redirect_uri") String redirectUri,
|
||||
@RequestParam("issuer") String issuer,
|
||||
@RequestParam("client_id") String clientId,
|
||||
HttpServletResponse response)
|
||||
throws IOException {
|
||||
|
||||
// Handle Cancel
|
||||
|
||||
Map<String, String> urlVariables = new HashMap<String, String>();
|
||||
urlVariables.put("error", "end_user_cancelled");
|
||||
urlVariables.put("error_description",
|
||||
"The end-user refused to select an Account.");
|
||||
|
||||
response.sendRedirect(AccountChooserController.buildURL(redirectUri,
|
||||
urlVariables));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.mitre.account_chooser;
|
|||
/**
|
||||
* @author nemonik
|
||||
*
|
||||
* Holdes properties of the Issuer, e.g. name, icon URL, description, et cetera
|
||||
*
|
||||
*/
|
||||
public class OIDCServer {
|
||||
|
||||
|
|
|
@ -1,77 +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.account_chooser;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class OIDCServers implements InitializingBean {
|
||||
|
||||
private Map<String, ? extends OIDCServer> servers = new HashMap<String, OIDCServer>();
|
||||
|
||||
private static Log logger = LogFactory.getLog(OIDCServers.class);
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
// used for debugging...
|
||||
if (!servers.isEmpty()) {
|
||||
logger.info(this.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the OIDCServers associated with this
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, ? extends OIDCServer> getServers() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OIDCServers associated with this
|
||||
*
|
||||
* @param signers
|
||||
* List of JwtSigners to associate with this service
|
||||
*/
|
||||
public void setServers(Map<String, ? extends OIDCServer> servers) {
|
||||
this.servers = servers;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OIDCServers [servers=" + servers
|
||||
+ "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -35,26 +35,17 @@
|
|||
|
||||
<!-- End view configuration -->
|
||||
|
||||
<bean class="org.mitre.account_chooser.OIDCServers">
|
||||
<property name="servers">
|
||||
<bean name="AccountChooserConfig" class="org.mitre.account_chooser.AccountChooserConfig">
|
||||
<property name="issuers">
|
||||
<map>
|
||||
<entry key="1">
|
||||
<entry key="http://sever.example.com:8080/openid-connect-server">
|
||||
<bean class="org.mitre.account_chooser.OIDCServer">
|
||||
<property name="name" value="OIDC Server 1" />
|
||||
</bean>
|
||||
</entry>
|
||||
<entry key="2">
|
||||
<bean class="org.mitre.account_chooser.OIDCServer">
|
||||
<property name="name" value="OIDC Server 2" />
|
||||
</bean>
|
||||
</entry>
|
||||
<entry key="3">
|
||||
<bean class="org.mitre.account_chooser.OIDCServer">
|
||||
<property name="name" value="OIDC Server 3" />
|
||||
<property name="name" value="Example Server" />
|
||||
</bean>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
<property name="validClientIds" value="FGWEUIASJK, IUYTTYEV, GFHDSFYD" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
|
@ -3,9 +3,8 @@
|
|||
<%@page import="org.mitre.account_chooser.OIDCServer"%>
|
||||
<%@page import="java.util.Map"%>
|
||||
<%@page import="java.util.Iterator"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
|
||||
<jsp:useBean id="servers" type="org.mitre.account_chooser.OIDCServers"
|
||||
<jsp:useBean id="issuers" type="java.util.Map"
|
||||
scope="request" />
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
@ -36,22 +35,21 @@ body {
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<form class="form-horizontal" action="selected" method="get">
|
||||
<form name="chooser" class="form-horizontal" action="selected" method="get">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="select01">Account:</label>
|
||||
<div class="controls">
|
||||
<select name="alias">
|
||||
<select name="issuer">
|
||||
<%
|
||||
Map map = servers.getServers();
|
||||
Iterator entries = map.entrySet().iterator();
|
||||
Iterator entries = issuers.entrySet().iterator();
|
||||
|
||||
while (entries.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) entries.next();
|
||||
|
||||
String alias = (String) entry.getKey();
|
||||
OIDCServer server = (OIDCServer) entry.getValue();
|
||||
String id = (String) entry.getKey();
|
||||
OIDCServer issuer = (OIDCServer) entry.getValue();
|
||||
%>
|
||||
<option value="<%= alias %>"><%= server.getName() %></option>
|
||||
<option value="<%= id %>"><%= issuer.getName() %></option>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
|
@ -61,12 +59,13 @@ body {
|
|||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<input name="redirect_uri" type="hidden" value="<c:out value="${redirect_uri}"/>">
|
||||
<input name="redirect_uri" type="hidden" value="${redirect_uri}">
|
||||
<input name="client_id" type="hidden" value="${client_id}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="btn btn-primary" type="submit">Submit</button>
|
||||
<button class="btn">Cancel</button>
|
||||
<a class="btn btn-primary" href="javascript: submitForm('selected')">Submit</a>
|
||||
<a class="btn" href="javascript: submitForm('cancel')">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -94,7 +93,10 @@ body {
|
|||
<script type="text/javascript" language="JavaScript">
|
||||
// <![CDATA [
|
||||
|
||||
// javascript secific to the page would go here, if I had any...
|
||||
function submitForm(action){
|
||||
$('.form-horizontal').attr( 'action' , action );
|
||||
$('.form-horizontal').submit();
|
||||
}
|
||||
|
||||
// ]]>
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
|
||||
pageEncoding="ISO-8859-1"%>
|
||||
<%@page import="org.mitre.account_chooser.OIDCServer"%>
|
||||
<%@page import="java.util.Map"%>
|
||||
<%@page import="java.util.Iterator"%>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Account Chooser</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Account Chooser GUI">
|
||||
<meta name="author" content="nemonik">
|
||||
<link href="resources/bootstrap/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="resources/bootstrap/css/bootstrap-responsive.css"
|
||||
rel="stylesheet">
|
||||
<link href="resources/bootstrap/css/docs.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
body {
|
||||
padding-top: 60px;
|
||||
/* 60px to make the container go all the way to the bottom of the topbar */
|
||||
}
|
||||
</style>
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<h1>
|
||||
${error}<br>
|
||||
<small>${error_description}</small>
|
||||
</h1>
|
||||
|
||||
<a href="${client_uri}">Return to client</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--/container-->
|
||||
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="resources/bootstrap/js/jquery.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-transition.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-alert.js"></script>
|
||||
|
||||
<script src="resources/bootstrap/js/bootstrap-modal.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-dropdown.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-scrollspy.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-tab.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-tooltip.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-popover.js"></script>
|
||||
|
||||
<script src="resources/bootstrap/js/bootstrap-button.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-collapse.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-carousel.js"></script>
|
||||
<script src="resources/bootstrap/js/bootstrap-typeahead.js"></script>
|
||||
|
||||
<script type="text/javascript" language="JavaScript">
|
||||
// <![CDATA [
|
||||
|
||||
// ]]>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
This is the Client, a Spring Security AuthenticationFilter, to OpenID Connect Java Spring Server described by [OpenID Connect Standard].
|
||||
|
||||
## Configure
|
||||
## Configuration of OIDCAuthenticationFilter
|
||||
|
||||
Configure the OpenIDConnectAuthenticationFilter by adding the XML to your application context security like so:
|
||||
Configure the OIDCAuthenticationFilter by adding the XML to your application context security like so:
|
||||
|
||||
<security:http auto-config="false"
|
||||
use-expressions="true"
|
||||
|
@ -40,7 +40,7 @@ Configure the OpenIDConnectAuthenticationFilter by adding the XML to your applic
|
|||
<security:authentication-manager alias="authenticationManager" />
|
||||
|
||||
<bean id="openIdConnectAuthenticationProvider"
|
||||
class='org.mitre.openid.connect.client.OpenIdConnectAuthenticationProvider">
|
||||
class='org.mitre.openid.connect.client.OIDCAuthenticationProvider">
|
||||
<property name="userDetaulsService" ref="myUserDetailsService"/>
|
||||
</bean>
|
||||
|
||||
|
@ -63,111 +63,22 @@ Configure the OpenIDConnectAuthenticationFilter by adding the XML to your applic
|
|||
|
||||
You will need to implement your own UserDetailsService and configure as the above does with the reference to *myUserDetailsService*.
|
||||
|
||||
## Proposed Account Chooser UI Application Extension
|
||||
## Configuration of OIDCAuthenticationUsingChooserFilter
|
||||
|
||||
The following proposed extension is in response to [Issue #39].
|
||||
The OIDCAuthenticationUsingChooserFilter was written in response to [Issue #39].
|
||||
|
||||
### Account Chooser Protocol
|
||||
|
||||
The following describes the protocol between the Client and Account Chooser UI application introduced in [Issue #39].
|
||||
|
||||
#### Authorization when using Account Chooser Code Flow
|
||||
|
||||
The Authorization when using Account Chooser Code Flow goes through the following steps.
|
||||
|
||||
1. Client prepares an Account Chooser Request containing the desired request parameters.
|
||||
2. Client sends a request to the Account Chooser.
|
||||
3. Account Chooser presents a selection of OpenID Connect (OIDC) Servers from which the End-User must select from.
|
||||
4. End-User selects an OIDC.
|
||||
5. Account Chooser Sends the End-User back to the Client with key value of the OIDC End-User selected.
|
||||
6. The Client begins the Authorization flow desrcribed in [Authorization Code Flow][OpenID Connect Standard] of the [OpenID Connect Standard].
|
||||
|
||||
#### Account Chooser Request
|
||||
|
||||
When the End-User wishes to access a Protected Resource, and the End-User Authorization has not yet been obtained, the Client will redirect the End-User to Account Chooser.
|
||||
|
||||
Account Chooser MUST support the use of the HTTP "GET" and "POST" methods defined in RFC 2616 [RFC2616].
|
||||
|
||||
Clients MAY use the HTTP "GET" or "POST" method to send the Account Chooser Request to the Account Chooser. If using the HTTP "GET" method, the request parameters are serialized using URI query string serialization. If using the HTTP "POST" method, the request parameters are serialized using form serialization.
|
||||
|
||||
#### Client Prepares an Account Chooser Request
|
||||
|
||||
The Client prepares an Account Chooser Request to the Account Chooser with the request parameters using the HTTP "GET" or "POST" method.
|
||||
|
||||
The required Account Chooser Request parameters are as follows:
|
||||
|
||||
* redirect_uri - A redirection URI where the response will be sent.
|
||||
|
||||
There is one method to construct and send the request to the Account Chooser:
|
||||
|
||||
a. Simple Request Method
|
||||
|
||||
#### Simple Request Method
|
||||
|
||||
The Client prepares an Account Chooser Request to the Account Chooser using the appropriate parameters. If using the HTTP "GET" method, the request parameters are serialized using URI query string serialization. If using the HTTP "POST" method, the request parameters are serialized using form serialization.
|
||||
|
||||
The following is a non-normative example of an Account Chooser Request URL. Line wraps are for display purposes only.
|
||||
|
||||
http://server.example.com/chooser?
|
||||
redirect_uri=https%3A%2F%2Fclient.example.com%2Fopenid_connect_login
|
||||
|
||||
#### Client sends a request to the Account Chooser
|
||||
|
||||
Having constructed the Account Chooser Request, the Client sends it to the Account Chooser. This MAY happen via redirect, hyperlinking, or any other means of directing the User-Agent to the Account Chooser URL.
|
||||
|
||||
Following is a non-normative example using HTTP redirect. Line wraps are for display purposes only.
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://server.example.com/chooser?
|
||||
redirect_uri=https%3A%2F%2Fclient.example.com%2Fopenid_connect_login
|
||||
|
||||
#### Account Chooser Sends the End-User back to the Client
|
||||
|
||||
After the End-User has select an OpenID Connect Server, it issues an Account Chooser Response and delivers it to the Client by adding the response parameters to redirect_uri specified in the Account Choose Request using the "application/x-www-form-urlencoded" format.
|
||||
|
||||
The following response parameters are included:
|
||||
|
||||
* oidc_alias - REQUIRED. The key used to configure the Client for its request of the selected OIDC server.
|
||||
|
||||
The following is non-normative example of a responses. Line wraps are for display purposes only.
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://client.example.com/openid_connect_login?
|
||||
oidc_alias=OIDC%20Server%201
|
||||
|
||||
#### End-User refuses to select an OIDC Server
|
||||
|
||||
If the End-User refuses to select an OIDC server, the Account Chooser MUST return an error response. The Account Chooser returns the Client via the redirection URI specified in the Account Chooser Request with the appropriate error parameters. No other parameters SHOULD be returned.
|
||||
|
||||
The error response parameters are the following:
|
||||
|
||||
* error - REQUIRED. The error code.
|
||||
* error_description - OPTIONAL. A human-readable UTF-8 encoded text description of the error.
|
||||
|
||||
The response parameters are added to the query component of the redirection URI.
|
||||
|
||||
The following is a non-normative example. Line wraps after the second line are for the display purposes only.
|
||||
|
||||
HTTP/1.1 302 Found
|
||||
Location: https://client.example.com/openid_connect_login?
|
||||
error=end_user_cancelled
|
||||
&error_description=The%20end%20user%20refused%20to%20select%20an%20OIDC%20server
|
||||
|
||||
### Modification to existing Client
|
||||
|
||||
#### Modifications to Client Configuration
|
||||
|
||||
The configuration of the filter would change by adding a OIDCServers property to the Client containing a map of OIDC servers, and a AccountChooserURI to denote the URI of the Account Chooser like so:
|
||||
Th Authentication Filter use the *oidcServerConfigs* property, a map of OIDC servers, an *accountChooserURI* property to denote the URI of the Account Chooser, and an *accountChooserClient* property to identify the Client to the Account Chooser UI application like so:
|
||||
|
||||
<bean id="openIdConnectAuthenticationFilter"
|
||||
class="org.mitre.openid.connect.client.OpenIdConnectAuthenticationFilter">
|
||||
class="org.mitre.openid.connect.client.OIDCAuthenticationUsingChooserFilter">
|
||||
<property name="errorRedirectURI" value="/login.jsp?authfail=openid" />
|
||||
<property name="authenticationManager" ref="authenticationManager" />
|
||||
<property name="AccountChooserURI"
|
||||
<property name="accountChooserURI"
|
||||
value="http://sever.example.com:8080/account-chooser" />
|
||||
<property name="accountChooserClientID" value="FGWEUIASJK" />
|
||||
<property name="oidcServerConfigs">
|
||||
<map>
|
||||
<entry key="OIDC Server 1">
|
||||
<entry key="http://sever.example.com:8080/Fopenid-connect-server">
|
||||
<bean class="org.mitre.openid.connect.client.OIDCServerConfiguration">
|
||||
<property name="authorizationEndpointURI"
|
||||
value="http://sever.example.com:8080/openid-connect-server/oauth/authorize" />
|
||||
|
@ -180,17 +91,14 @@ The configuration of the filter would change by adding a OIDCServers property to
|
|||
<property name="clientSecret" value="someClientSecret" />
|
||||
</bean>
|
||||
</entry>
|
||||
<entry key="OIDC Server 2">
|
||||
...
|
||||
<entry key=". . .
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
|
||||
|
||||
In cases where the Account Chooser will not be used, the Client will be configured with authorizationEndpointURI, tokenEndpointURI, checkIDEndpointURI, clientId, and clientSecret as the Client is presently.
|
||||
Again, you will need to implement your own UserDetailsService and configure as the above does with the reference to *myUserDetailsService*.
|
||||
|
||||
[OpenID Connect Standard]: http://openid.net/specs/openid-connect-standard-1_0.html "OpenID Connect Standard 1.0"
|
||||
[OpenID Connect Standard]: http://openid.net/specs/openid-connect-standard-1_0.html#code_flow "Authorization Code Flow, OpenID Connect Standard"
|
||||
[Issuer Identifier]: http://openid.net/specs/openid-connect-messages-1_0.html#issuer_identifier "Issuer Identifier"
|
||||
[Issue #39]: http://github.com/jricher/OpenID-Connect-Java-Spring-Server/issues/39 "Issue #39 -- Multiple Point Client"
|
|
@ -67,16 +67,16 @@ import com.google.gson.JsonParser;
|
|||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class OpenIdConnectAuthenticationFilter extends
|
||||
public class AbstractOIDCAuthenticationFilter extends
|
||||
AbstractAuthenticationProcessingFilter {
|
||||
|
||||
private final static int HTTP_SOCKET_TIMEOUT = 30000;
|
||||
private final static String SCOPE = "openid";
|
||||
private final static int KEY_SIZE = 1024;
|
||||
private final static String SIGNING_ALGORITHM = "SHA256withRSA";
|
||||
private final static String NONCE_SIGNATURE_COOKIE_NAME = "nonce";
|
||||
private final static String OIDC_ALIAS_COOKIE_NAME = "oidc_alias";
|
||||
private final static String FILTER_PROCESSES_URL = "/openid_connect_login";
|
||||
protected final static int HTTP_SOCKET_TIMEOUT = 30000;
|
||||
protected final static String SCOPE = "openid";
|
||||
protected final static int KEY_SIZE = 1024;
|
||||
protected final static String SIGNING_ALGORITHM = "SHA256withRSA";
|
||||
protected final static String NONCE_SIGNATURE_COOKIE_NAME = "nonce";
|
||||
|
||||
protected final static String FILTER_PROCESSES_URL = "/openid_connect_login";
|
||||
|
||||
/**
|
||||
* Builds the redirect_uri that will be sent to the Authorization Endpoint.
|
||||
|
@ -105,7 +105,9 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
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) {
|
||||
|
@ -150,13 +152,19 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
char appendChar = '?';
|
||||
|
||||
for (Map.Entry<String, String> param : queryStringFields.entrySet()) {
|
||||
|
||||
try {
|
||||
|
||||
URLBuilder.append(appendChar).append(param.getKey())
|
||||
.append('=')
|
||||
.append(URLEncoder.encode(param.getValue(), "UTF-8"));
|
||||
|
||||
} catch (UnsupportedEncodingException uee) {
|
||||
|
||||
throw new IllegalStateException(uee);
|
||||
|
||||
}
|
||||
|
||||
appendChar = '&';
|
||||
}
|
||||
|
||||
|
@ -188,8 +196,11 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
.replace("=", "");
|
||||
|
||||
} catch (GeneralSecurityException generalSecurityException) {
|
||||
|
||||
// generalSecurityException.printStackTrace();
|
||||
|
||||
throw new IllegalStateException(generalSecurityException);
|
||||
|
||||
}
|
||||
|
||||
return signature;
|
||||
|
@ -216,39 +227,37 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
return signer.verify(sigBytes);
|
||||
|
||||
} catch (GeneralSecurityException generalSecurityException) {
|
||||
|
||||
// generalSecurityException.printStackTrace();
|
||||
|
||||
throw new IllegalStateException(generalSecurityException);
|
||||
|
||||
} catch (UnsupportedEncodingException unsupportedEncodingException) {
|
||||
|
||||
// unsupportedEncodingException.printStackTrace();
|
||||
|
||||
throw new IllegalStateException(unsupportedEncodingException);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private String errorRedirectURI;
|
||||
protected String errorRedirectURI;
|
||||
|
||||
private OIDCServerConfiguration oidcServerConfig;
|
||||
protected String scope;
|
||||
|
||||
private String accountChooserURI;
|
||||
protected int httpSocketTimeout = HTTP_SOCKET_TIMEOUT;
|
||||
|
||||
private Map<String, ? extends OIDCServerConfiguration> oidcServerConfigs = new HashMap<String, OIDCServerConfiguration>();
|
||||
protected PublicKey publicKey;
|
||||
|
||||
private String scope;
|
||||
protected PrivateKey privateKey;
|
||||
|
||||
private int httpSocketTimeout = HTTP_SOCKET_TIMEOUT;
|
||||
|
||||
private PublicKey publicKey;
|
||||
|
||||
private PrivateKey privateKey;
|
||||
|
||||
private Signature signer;
|
||||
protected Signature signer;
|
||||
|
||||
/**
|
||||
* OpenIdConnectAuthenticationFilter constructor
|
||||
*/
|
||||
protected OpenIdConnectAuthenticationFilter() {
|
||||
protected AbstractOIDCAuthenticationFilter() {
|
||||
super(FILTER_PROCESSES_URL);
|
||||
|
||||
oidcServerConfig = new OIDCServerConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -258,40 +267,6 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
Assert.notNull(errorRedirectURI,
|
||||
"An Error Redirect URI must be supplied");
|
||||
|
||||
try {
|
||||
|
||||
// Validating configuration w/ Account Chooser UI Application
|
||||
// settings
|
||||
|
||||
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.");
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
// Failing over to validating configuration w/o Account Chooser UI
|
||||
// Application settings
|
||||
|
||||
Assert.notNull(oidcServerConfig.getAuthorizationEndpointURI(),
|
||||
"An Authorization Endpoint URI must be supplied");
|
||||
|
||||
Assert.notNull(oidcServerConfig.getTokenEndpointURI(),
|
||||
"A Token ID Endpoint URI must be supplied");
|
||||
|
||||
Assert.notNull(oidcServerConfig.getCheckIDEndpointURI(),
|
||||
"A Check 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");
|
||||
}
|
||||
|
||||
KeyPairGenerator keyPairGenerator;
|
||||
|
||||
try {
|
||||
|
@ -321,76 +296,13 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
* javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response) throws AuthenticationException,
|
||||
public Authentication attemptAuthentication(HttpServletRequest arg0,
|
||||
HttpServletResponse arg1) throws AuthenticationException,
|
||||
IOException, ServletException {
|
||||
|
||||
// Enter AuthenticationFilter here...
|
||||
|
||||
if (StringUtils.isNotBlank(request.getParameter("error"))) {
|
||||
|
||||
handleError(request, response);
|
||||
|
||||
} else if (request.getParameter("code") != null) {
|
||||
|
||||
return handleAuthorizationGrantResponse(request);
|
||||
|
||||
} else if (StringUtils.isNotBlank(accountChooserURI)) {
|
||||
|
||||
String oidcAlias = request.getParameter("oidc_alias");
|
||||
|
||||
if (StringUtils.isNotBlank(oidcAlias)) {
|
||||
|
||||
// Account Chooser UI selects the appropriate OIDC Server configuration
|
||||
|
||||
OIDCServerConfiguration oidcServerConfig = oidcServerConfigs
|
||||
.get(oidcAlias);
|
||||
|
||||
if (oidcServerConfig != null) {
|
||||
|
||||
Cookie oidcAliasCookie = new Cookie(OIDC_ALIAS_COOKIE_NAME, oidcAlias);
|
||||
response.addCookie(oidcAliasCookie);
|
||||
|
||||
handleAuthorizationRequest(request, response, oidcServerConfig);
|
||||
|
||||
} else {
|
||||
|
||||
throw new AuthenticationServiceException(
|
||||
"Security Filter not configured for OIDC Alias: "
|
||||
+ oidcAlias);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// redirect the End-User to the configured Account Chooser UI
|
||||
|
||||
Map<String, String> urlVariables = new HashMap<String, String>();
|
||||
|
||||
urlVariables.put("redirect_uri", OpenIdConnectAuthenticationFilter
|
||||
.buildRedirectURI(request, null));
|
||||
|
||||
response.sendRedirect(OpenIdConnectAuthenticationFilter.buildURL(
|
||||
accountChooserURI, urlVariables));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Use configuration that doesn't involve the Account Chooser UI.
|
||||
|
||||
handleAuthorizationRequest(request, response, this.oidcServerConfig);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAccountChooserURI() {
|
||||
return accountChooserURI;
|
||||
}
|
||||
|
||||
public Map<String, ? extends OIDCServerConfiguration> getOidcServerConfigs() {
|
||||
return oidcServerConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the authorization grant response
|
||||
*
|
||||
|
@ -400,20 +312,11 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
* @return The authenticated user token, or null if authentication is
|
||||
* incomplete.
|
||||
*/
|
||||
private Authentication handleAuthorizationGrantResponse(
|
||||
HttpServletRequest request) {
|
||||
protected Authentication handleAuthorizationGrantResponse(
|
||||
HttpServletRequest request, OIDCServerConfiguration serverConfig) {
|
||||
|
||||
final boolean debug = logger.isDebugEnabled();
|
||||
|
||||
OIDCServerConfiguration serverConfig = this.oidcServerConfig;
|
||||
|
||||
// Which OIDC configuration?
|
||||
Cookie oidcAliasCookie = WebUtils.getCookie(request, OIDC_ALIAS_COOKIE_NAME);
|
||||
|
||||
if (oidcAliasCookie != null) {
|
||||
serverConfig = oidcServerConfigs.get(oidcAliasCookie.getValue());
|
||||
}
|
||||
|
||||
String authorizationGrant = request.getParameter("code");
|
||||
|
||||
// Handle Token Endpoint interaction
|
||||
|
@ -440,7 +343,7 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
|
||||
form.add("grant_type", "authorization_code");
|
||||
form.add("code", authorizationGrant);
|
||||
form.add("redirect_uri", OpenIdConnectAuthenticationFilter
|
||||
form.add("redirect_uri", AbstractOIDCAuthenticationFilter
|
||||
.buildRedirectURI(request, new String[] { "code" }));
|
||||
|
||||
// pass clientId and clientSecret in post of request
|
||||
|
@ -448,15 +351,16 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
form.add("client_secret", serverConfig.getClientSecret());
|
||||
|
||||
if (debug) {
|
||||
logger.debug("tokenEndpointURI = " + serverConfig.getTokenEndpointURI());
|
||||
logger.debug("tokenEndpointURI = "
|
||||
+ serverConfig.getTokenEndpointURI());
|
||||
logger.debug("form = " + form);
|
||||
}
|
||||
|
||||
String jsonString = null;
|
||||
|
||||
try {
|
||||
jsonString = restTemplate.postForObject(serverConfig.getTokenEndpointURI(), form,
|
||||
String.class);
|
||||
jsonString = restTemplate.postForObject(
|
||||
serverConfig.getTokenEndpointURI(), form, String.class);
|
||||
} catch (HttpClientErrorException httpClientErrorException) {
|
||||
|
||||
// Handle error
|
||||
|
@ -554,8 +458,9 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
jsonString = null;
|
||||
|
||||
try {
|
||||
jsonString = restTemplate.postForObject(serverConfig.getCheckIDEndpointURI(),
|
||||
form, String.class);
|
||||
jsonString = restTemplate.postForObject(
|
||||
serverConfig.getCheckIDEndpointURI(), form,
|
||||
String.class);
|
||||
} catch (HttpClientErrorException httpClientErrorException) {
|
||||
|
||||
// Handle error
|
||||
|
@ -651,7 +556,7 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
* @throws IOException
|
||||
* If an input or output exception occurs
|
||||
*/
|
||||
private void handleAuthorizationRequest(HttpServletRequest request,
|
||||
protected void handleAuthorizationRequest(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
OIDCServerConfiguration serverConfiguration) throws IOException {
|
||||
|
||||
|
@ -662,7 +567,7 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
urlVariables.put("response_type", "code");
|
||||
urlVariables.put("client_id", serverConfiguration.getClientId());
|
||||
urlVariables.put("scope", scope);
|
||||
urlVariables.put("redirect_uri", OpenIdConnectAuthenticationFilter
|
||||
urlVariables.put("redirect_uri", AbstractOIDCAuthenticationFilter
|
||||
.buildRedirectURI(request, null));
|
||||
|
||||
// Create a string value used to associate a user agent session
|
||||
|
@ -684,7 +589,7 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
|
||||
// TODO: display, prompt, request, request_uri
|
||||
|
||||
response.sendRedirect(OpenIdConnectAuthenticationFilter.buildURL(
|
||||
response.sendRedirect(AbstractOIDCAuthenticationFilter.buildURL(
|
||||
serverConfiguration.getAuthorizationEndpointURI(), urlVariables));
|
||||
}
|
||||
|
||||
|
@ -699,7 +604,7 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
* @throws IOException
|
||||
* If an input or output exception occurs
|
||||
*/
|
||||
private void handleError(HttpServletRequest request,
|
||||
protected void handleError(HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException {
|
||||
|
||||
String error = request.getParameter("error");
|
||||
|
@ -718,44 +623,15 @@ public class OpenIdConnectAuthenticationFilter extends
|
|||
requestParams.put("error_uri", errorURI);
|
||||
}
|
||||
|
||||
response.sendRedirect(OpenIdConnectAuthenticationFilter.buildURL(
|
||||
response.sendRedirect(AbstractOIDCAuthenticationFilter.buildURL(
|
||||
errorRedirectURI, requestParams));
|
||||
}
|
||||
|
||||
public void setAccountChooserURI(String accountChooserURI) {
|
||||
this.accountChooserURI = accountChooserURI;
|
||||
}
|
||||
|
||||
public void setAuthorizationEndpointURI(String authorizationEndpointURI) {
|
||||
oidcServerConfig.setAuthorizationEndpointURI(authorizationEndpointURI);
|
||||
}
|
||||
|
||||
public void setCheckIDEndpointURI(String checkIDEndpointURI) {
|
||||
oidcServerConfig.setCheckIDEndpointURI(checkIDEndpointURI);
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
oidcServerConfig.setClientId(clientId);
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
oidcServerConfig.setClientSecret(clientSecret);
|
||||
}
|
||||
|
||||
public void setErrorRedirectURI(String errorRedirectURI) {
|
||||
this.errorRedirectURI = errorRedirectURI;
|
||||
}
|
||||
|
||||
public void setOidcServerConfigs(
|
||||
Map<String, ? extends OIDCServerConfiguration> oidcServerConfigs) {
|
||||
this.oidcServerConfigs = oidcServerConfigs;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public void setTokenEndpointURI(String tokenEndpointURI) {
|
||||
oidcServerConfig.setTokenEndpointURI(tokenEndpointURI);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/*******************************************************************************
|
||||
* 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 javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The OpenID Connect Authentication Filter
|
||||
*
|
||||
* See README.md to to configure
|
||||
*
|
||||
* @author nemonik
|
||||
*
|
||||
*/
|
||||
public class OIDCAuthenticationFilter extends AbstractOIDCAuthenticationFilter {
|
||||
|
||||
protected OIDCServerConfiguration oidcServerConfig;
|
||||
|
||||
/**
|
||||
* OpenIdConnectAuthenticationFilter constructor
|
||||
*/
|
||||
protected OIDCAuthenticationFilter() {
|
||||
super();
|
||||
|
||||
oidcServerConfig = new OIDCServerConfiguration();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.mitre.openid.connect.client.AbstractOIDCAuthenticationFilter#
|
||||
* afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
|
||||
// Validating configuration
|
||||
|
||||
Assert.notNull(oidcServerConfig.getAuthorizationEndpointURI(),
|
||||
"An Authorization Endpoint URI must be supplied");
|
||||
|
||||
Assert.notNull(oidcServerConfig.getTokenEndpointURI(),
|
||||
"A Token ID Endpoint URI must be supplied");
|
||||
|
||||
Assert.notNull(oidcServerConfig.getCheckIDEndpointURI(),
|
||||
"A Check 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");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.mitre.openid.connect.client.AbstractOIDCAuthenticationFilter#
|
||||
* attemptAuthentication(javax.servlet.http.HttpServletRequest,
|
||||
* javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
@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 (request.getParameter("code") != null) {
|
||||
|
||||
return handleAuthorizationGrantResponse(request, oidcServerConfig);
|
||||
|
||||
} else {
|
||||
|
||||
handleAuthorizationRequest(request, response, oidcServerConfig);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setAuthorizationEndpointURI(String authorizationEndpointURI) {
|
||||
oidcServerConfig.setAuthorizationEndpointURI(authorizationEndpointURI);
|
||||
}
|
||||
|
||||
public void setCheckIDEndpointURI(String checkIDEndpointURI) {
|
||||
oidcServerConfig.setCheckIDEndpointURI(checkIDEndpointURI);
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
oidcServerConfig.setClientId(clientId);
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
oidcServerConfig.setClientSecret(clientSecret);
|
||||
}
|
||||
|
||||
public void setErrorRedirectURI(String errorRedirectURI) {
|
||||
this.errorRedirectURI = errorRedirectURI;
|
||||
}
|
||||
|
||||
public void setTokenEndpointURI(String tokenEndpointURI) {
|
||||
oidcServerConfig.setTokenEndpointURI(tokenEndpointURI);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/*******************************************************************************
|
||||
* 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.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<String, ? extends OIDCServerConfiguration> oidcServerConfigs = new HashMap<String, OIDCServerConfiguration>();
|
||||
|
||||
/**
|
||||
* 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 oidcAliasCookie = WebUtils.getCookie(request,
|
||||
ISSUER_COOKIE_NAME);
|
||||
|
||||
return handleAuthorizationGrantResponse(request,
|
||||
oidcServerConfigs.get(oidcAliasCookie.getValue()));
|
||||
|
||||
} else {
|
||||
|
||||
String issuerId = request.getParameter("issuer");
|
||||
|
||||
if (StringUtils.isNotBlank(issuerId)) {
|
||||
|
||||
// Account Chooser UI provided and Issuer Identifier
|
||||
|
||||
OIDCServerConfiguration oidcServerConfig = oidcServerConfigs
|
||||
.get(issuerId);
|
||||
|
||||
if (oidcServerConfig != null) {
|
||||
|
||||
// The Client is configured to support this Issuer
|
||||
// Identifier
|
||||
|
||||
Cookie issuerCookie = new Cookie(ISSUER_COOKIE_NAME,
|
||||
issuerId);
|
||||
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 OIDC Alias: "
|
||||
+ issuerId);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Redirect the End-User to the configured Account Chooser UI
|
||||
// application
|
||||
|
||||
Map<String, String> urlVariables = new HashMap<String, String>();
|
||||
|
||||
urlVariables.put("redirect_uri",
|
||||
OIDCAuthenticationUsingChooserFilter.buildRedirectURI(
|
||||
request, null));
|
||||
|
||||
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<String, ? extends OIDCServerConfiguration> oidcServerConfigs) {
|
||||
this.oidcServerConfigs = oidcServerConfigs;
|
||||
}
|
||||
}
|
|
@ -5,10 +5,7 @@
|
|||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
||||
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||
<dependent-module archiveName="spring-security-oauth2-1.0.0.BUILD-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/spring-security-oauth2-MITRE/spring-security-oauth2-MITRE">
|
||||
<dependency-type>uses</dependency-type>
|
||||
</dependent-module>
|
||||
<dependent-module archiveName="openid-connect-common-0.1-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/openid-connect-common-MITRE/openid-connect-common-MITRE">
|
||||
<dependent-module archiveName="openid-connect-common-0.1-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/openid-connect-common/openid-connect-common">
|
||||
<dependency-type>uses</dependency-type>
|
||||
</dependent-module>
|
||||
<property name="java-output-path" value="/openid/target/classes"/>
|
||||
|
|
Loading…
Reference in New Issue