You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OpenID-Connect-Java-Spring-.../openid-connect-client
Justin Richer a03129f70a
added documentation updates
13 years ago
..
src added documentation updates 13 years ago
.gitignore removed eclipse files from repository 13 years ago
.springBeans
README.md added documentation updates 13 years ago
pom.xml

README.md

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;
        }
    }