11 KiB
OpenID Connect Client
Overview
You are reading the documentation for the OIDC Client implemented as a Spring Security AuthenticationFilter. The client facilitates a user's authentication into the secured application to an OpenID Connect Java Spring Server following the OpenID Connect Standard described protocol.
Configuring
Configure the client by adding the following XML to your application context security making changes where necessary for your specific deployment.
Open and define an HTTP security configuration with a reference to a bean defined custom AuthenticationEntryPoint:
<security:http auto-config="false"
use-expressions="true"
disable-url-rewriting="true"
entry-point-ref="authenticationEntryPoint"
pattern="/**">
Specify the access attributes and/or filter list for a particular set of URLs needing protection:
<security:intercept-url
pattern="/**"
access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
Indicate that OpenIdConnectAuthenticationFilter authentication filter should be incorporated into the security filter chain:
<security:custom-filter
before="PRE_AUTH_FILTER
ref="openIdConnectAuthenticationFilter" />
Set up remember-me authentication referencing the yet to be defined UserDetailsService:
<security:remember-me user-service-ref="myUserDetailsService"
NOTE: See the last section as how to implement your own UserDetailsService necessary to complete authentication.
Then close the HTTP security configuration:
</security:http>
Define a custom AuthenticationEntryPoint via a bean declaration:
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl"
value="/openid_connect_login"/>
</bean>
NOTE: The loginFormUrl value is post-pended to the URI of the application being secured to define the redirect_uri, the value passed to the OIDC Server and, if the OIDCAuthenticationUsingChooserFilter is configured, also the Account Chooser Application.
Define an AuthenticationManager with a reference to a custom authentication provider, OpenIDConnectAuthenticationProvider:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="openIDConnectAuthenticationProvider" />
</security:authentication-manager>
Define the custom authentication provider referencing the your yet to be defined implementation of a UserDetailsService:
<bean id="openIdConnectAuthenticationProvider"
class='org.mitre.openid.connect.client.OIDCAuthenticationProvider">
<property name="userDetailsService" ref="myUserDetailsService"/>
</bean>
Configuring the OIDCAuthenticationFilter
The OpenIdConnectAuthenticationFilter filter is defined with the following properties:
- authenticationManager -- a reference to the AuthenticationManager,
- errorRedirectURI -- the URI of the Error redirect,
- authorizationEndpointURI -- the URI of the Authorization Endpoint,
- tokenEndpointURI -- the URI of the Token Endpoint,
- clientId -- the registered client identifier, and
- clientSecret -- the registered client secret.
Configure like so:
<bean id="openIdConnectAuthenticationFilter"
class="org.mitre.openid.connect.client.OpenIdConnectAuthenticationFilter">
<property name="authenticationManager"
ref="authenticationManager" />
<property name="errorRedirectURI"
value="/login.jsp?authfail=openid" />
<property name="authorizationEndpointURI"
value="http://sever.example.com:8080/openid-connect-server/openidconnect/auth" />
<property name="tokenEndpointURI"
value="http://sever.example.com:8080/openid-connect-server/openidconnect/token" />
<property name="clientId"
value="someClientId" />
<property name="clientSecret" value="someClientSecret" />
<property name="issuer" value="http://server.example.com:8080/openid-connect-server/" />
<property name="jwkSigningUrl" value="http://server.example.com:8080/openid-connect-server/jwk" />
</bean>
NOTE: Again, you will need your own implementation of a UserDetailsService specific to your deployment. See the last section of this document.
Or Alternatively, Configuring the OIDCAuthenticationUsingChooserFilter
Alternatively, the OIDCAuthenticationUsingChooserFilter can be configured and used. It was written in response to Issue #39. The Client -- Account Chooser protocol documentation details the protocol used between the Client and an Account Chooser application.
The OIDCAuthenticationUsingChooserFilter Authentication Filter has the following properties:
- oidcServerConfigs -- a map of OIDCserverConfigurations to encapsulate the settings necesary for the client to communicate with each respective OIDC server,
- accountChooserURI -- to denote the URI of the Account Chooser, and
- accountChooserClient -- to identify the Client to the Account Chooser UI application.
Each OIDCServerConfiguration entry in OIDCserverConfiguration map is keyed to the issuer returned from the Account Chooser Application and enumerates the following properties:
- authorizationEndpointURI -- the URI of the Authorization Endpoint,
- tokenEndpointURI -- the URI of the Token Endpoint,
- clientId -- the registered client identifier, and
- clientSecret -- the registered client secret.
Configure like so:
<bean id="openIdConnectAuthenticationFilter"
class="org.mitre.openid.connect.client.OIDCAuthenticationUsingChooserFilter">
<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="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"
value="http://sever.example.com:8080/openid-connect-server/oauth/token" />
<property name="clientId"
value="someClientId" />
<property name="clientSecret" value="someClientSecret" />
</bean>
</entry>
<entry key=". . .
</map>
</property>
</bean>
Again, you will need your own implementation of a UserDetailsService. See the next section.
Implementing your own UserDetailsService
You need to implement your own UserDetailsService to complete the authentication.
An example UserDetailsService for the Rave Portal follows:
package org.mitre.mpn.service.impl;
import org.apache.rave.portal.model.NewUser;
import org.apache.rave.portal.model.User;
import org.apache.rave.portal.service.NewAccountService;
import org.apache.rave.portal.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.mitre.openid.connect.client.OpenIdConnectAuthenticationToken;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Service(value = "myUserDetailsService")
public class MyUserDetailsService implements UserDetailsService,
AuthenticationUserDetailsService<OpenIdConnectAuthenticationToken> {
private static final Logger log = LoggerFactory.getLogger(MpnUserDetailsService.class);
private final UserService userService;
private final NewAccountService newAccountService;
//TODO: This is temporarily hard-coded while we wait for the concept of Page Templates to be implemented in Rave
private static final String DEFAULT_LAYOUT_CODE = "columns_3";
@Autowired
public MyUserDetailsService(UserService userService, NewAccountService newAccountService) {
this.userService = userService;
this.newAccountService = newAccountService;
}
/* (non-Javadoc)
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.debug("loadUserByUsername called with: {}", username);
User user = userService.getUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User with username '" + username + "' was not found!");
}
return user;
}
/* (non-Javadoc)
* @see org.springframework.security.core.userdetails.AuthenticationUserDetailsService#loadUserDetails(org.springframework.security.core.Authentication)
*/
public UserDetails loadUserDetails(OpenIdConnectAuthenticationToken token) throws UsernameNotFoundException {
log.debug("loadUserDetails called with: {}", token);
User user = userService.getUserByUsername(token.getUserId());
if (user == null) {
NewUser newUser = new NewUser();
newUser.setUsername(token.getUserId());
newUser.setEmail(token.getUserId() + "@example.com");
newUser.setPageLayout(DEFAULT_LAYOUT_CODE);
newUser.setPassword(UUID.randomUUID().toString());
try {
newAccountService.createNewAccount(newUser);
} catch (Exception e) {
throw new RuntimeException(e);
}
user = userService.getUserByUsername(token.getName());
}
return user;
}
}