Using OpenID Connect in WebSphere Application Server Liberty Profile

Comments

Part 1. OIDC support in Liberty Profile

OpenID Connect 1.0 (OIDC) is a simple identity protocol over OAuth 2.0. OIDC is becoming the accepted Internet SSO protocol, and it works well with cloud, mobile, and native applications. OIDC lets a client application (such as a cloud or mobile application) request the identity of the user as an ID token in a standardized, REST-like manner. In addition, the client application can use access tokens to access REST-like Services.

The OIDC provider in Liberty Profile is implemented as an OAuth 2.0 extension, and can be used as a normal OAuth 2.0 authorization provider that issues OAuth 2.0 access tokens, and supports all OAuth 2.0 grant types.

Prerequisites for this tutorial

  • A basic understanding of the OIDC and OAuth 2.0 protocols. While this understanding is not required to perform the configuration steps, it is required in order to evaluate the security impact of OIDC in your enterprise.
  • A basic understanding of Liberty Profile, including familiarity with installation, creating a server, and updating a server configuration.
  • Liberty Profile V8.5.5.4 or later, with OIDC features installed as instructed. Go to the <liberty_root>/bin folder, and issue the following two commands:
    featureManager install openidConnectServer-1.0 --when-file-exists=ignore
    featureManager install openidConnectClient-1.0 --when-file-exists=ignore

OIDC architecture

Key OIDC terms include End-User, Relying Party (RP), and OIDC Provider (OP), as shown in Figure 1:

Figure 1. Key OIDC terms
Key OIDC terms
Key OIDC terms

Liberty Profile V8.5.5.4 introduced two important high-level configuration options:

  • Liberty OIDC Provider – A dedicated Liberty instance can be configured as an OIDC Provider (OP) and an SSO server. Any client application can call an OP-hosted RESTful security service to request or verify end user identity and user profiles.
  • Liberty OIDC Relying Party – A Liberty instance can be configured as an OIDC Relying Party to take advantage of web SSO and use an OIDC Provider as an identity provider. The Liberty security container can then call an OIDC OP to verify the identity of the end-user, instead of having the Liberty security container handle the user verification.

WebSphere Application Server Full Profile V8.5.5.3 also added support for OIDC Relying Party, and can participate in web SSO with an OIDC provider.

Web SSO and optional identity propagation scenario

Figure 2 below shows a typical Web SSO and identity propagation scenario using OIDC, with three Liberty Profile servers in three security domains. These servers could represent individual business units or separate companies. Server RP requests user identity from SSO server OP. Server RP uses already authenticated identity to further request resources from server RS, and server RS verifies caller identity from server OP. Servers RP and RS then use the ID token or JSON object from OP to create a JAAS subject, which represents the user and provides resource access.

Server OP is a SSO server, and therefore the user only needs to log in to OP once. After authentication with OP, the user can request authorized access from multiple secured RP servers that participate in SSO with OP.

Figure 2. OIDC flows in Liberty Profile
OIDC flows in Liberty Profile
OIDC flows in Liberty Profile

OIDC features

  • Endpoints: The OIDC provider in Liberty Profile is implemented as an OAuth 2.0 extension. In addition to providing all OIDC functions, the OIDC provider supports all OAuth 2.0 functions. The Liberty OIDC provider supports the following endpoints:
    • Authorization endpoint
    • Token endpoint
    • Introspection endpoint
    • UserInfo endpoint
    • Discovery endpoint
    • Coverage map endpoint
    • Registration endpoint
    Figure 3. OIDC 1.0 and OAuth 2.0 endpoints in Liberty Profile
     OIDC 1.0 and OAuth 2.0 endpoints in Liberty Profile
    OIDC 1.0 and OAuth 2.0 endpoints in Liberty Profile
  • Grant types: Liberty OIDC supports all OAuth 2.0 grant types, including:
    • authorization_code
    • implicit
    • refresh_token
    • client_credentials
    • password
    • urn:ietf:params:oauth:grant-type:jwt-bearer
  • Multiple tenant: Liberty Profile server supports multiple OIDC provider instances, and each provider instance has its own endpoint URLs, consent form, login form, and management of its own clients and tokens.
  • Flexible authentication: In addition to direct authenticattion with the LDAP user registry, Liberty OIDC can be configured to delegate user authentication to a third-party authentication service, such as a social network site, a SPNEGO server, or a SAML identity provider, by using custom TAI. With Liberty OIDC, you can easily integrate OIDC with existing enterprise identity services.
  • Trust and integrity: The Liberty OIDC provider digitally signs an ID token with the client's own secret key or with the provider's private key, and the ID token is always accompanied by the access token.
  • Administrated and secured dynamic client registration.
  • Highly customizable provider: The Liberty OIDC provider provides customized consent forms, login forms, token mediation, and authorization mediation.
  • ID token: The ID token has enough claims to recreate a JAAS subject, and can be extended to provide custom claims. The following JSON is a typical ID token created by Liberty OP:
    Sample ID token in Liberty
    {
    	"iss":"https://acme.com/oidc/endpoint/idp",
    	"sub":"marissa",
    	"aud":"voting_application",
    	"iat":1385062578,
    	"exp":1385066178
    	"at_hash":”6820876yree” ,        
    	“realmName”: “ldap://acme.com:389”,    
    	"uniqueSecurityName": "cn=malissa, o=acme",   
    	“groupIds”: [
    	    “cn=board member, o=acme”, “cn=executive member, o=acme”
    	 ] , 
    }
  • Repositories: The OIDC provider has two types of repositories:
    • localStore -- Registered clients are stored locally in server.xml within localStore elements. ID token, access tokens, and refresh token are in memory, and user consents are cached in an HTTP session. In localStore mode, client registration endpoint only supports read operation.
    • databaseStore -- Registered clients, access token, ID token, refresh token, and user consent are stored in a database. Tokens and user consents are persistent and are automatically recovered even after OP restart. The full functions of client registration endpoint -- including create, read, update, and delete -- are available only in databaseStore mode.

Liberty Profile has implemented the full OIDC specification. For more information on OIDC, see the OpenID Connect website. For more information on configuring OIDC in Liberty Profile, see the topic OpenID Connect in the WebSphere Application Server knowledge center.

Part 2. A Liberty Profile OIDC web SSO example

Part 2 shows you how to set up a basic OIDC web SSO scenario with Liberty Profile.

Figure 4. OIDC web SSO scenario
OIDC web SSO scenario
OIDC web SSO scenario

Setting up the OIDC provider

Create a Liberty server instance for the OIDC provider: Go to the <liberty_root>/bin folder and run server create <server_name>. This example uses the server name oidcServer. Find the server.xml file at <liberty_root>/usr/servers/ oidcServer/server.xml, open it in an editor, and make the following changes:

  1. In the <featureManager> section, add all of the following features that are missing from your server definition. These updates will enable features required by the OIDC provider:
    Required Liberty feature list
     <featureManager>
            <feature>openidConnectServer-1.0</feature>
            <feature>ssl-1.0</feature>
            <feature>appSecurity-2.0</feature>
            <feature>servlet-3.0</feature>    
    </featureManager>

    You can add <feature>ldapRegistry-3.0</feature> if you want to use LDAP user registry for user authentication, and you must use LDAP if you want to use the OIDC useriInfo endpoint.

  2. Update httpEndpoint with the machine hostname to handle requests for remote testing, and make sure an https port is defined:
    <httpEndpoint id="defaultHttpEndpoint"
            host="op.example.com"       httpPort="80"       httpsPort="443" />
  3. Add a KeyStore to enable SSL:
    <keyStore id="defaultKeyStore" password="keypass" />
  4. Add some sample users for testing, if an LDAP registry is not configured:
    <basicRegistry id="basic" realm=" OpBasicRealm ">
    		<user name="user1" password="security" />
    </basicRegistry>

Defining the OAuth provider

OIDC is built on top of the OAuth 2.0 protocol, and you must configure a valid OAuth service provider, including the appropriate oauth-roles and oauthProvider elements.

Create oauth-role: Any user that is authorized to use OIDC must have an oauth-role. For illustration, this example assigns the oauth-role to all authenticated users:

    <oauth-roles>
        <authenticated>
            <special-subject type="ALL_AUTHENTICATED_USERS" />  
        </authenticated>
    </oauth-roles>

Create oauthProvider element: This element defines a custom login page, custom consent form, various timeout parameters, client stores, and so on. Liberty OIDC provides default values for most required parameters, and the only required configuration is to define a client store. This example uses localStore:

    <oauthProvider id="Oauth"  >
        <localStore
        </localStore>
     </oauthProvider>

Register clients: The Liberty OIDC provider requires all clients to be registered and stored in localStore or databaseStore. Each registered client contains name, secret, displayname, redirect, scope, preAuthorizedScope, and some other attributes. The client will be registered later. Here is a sample client in localStore:

<client name="oidcclient" secret="{xor}LDo8LTor"
                displayname="sample client"
                redirect="https://client.example.com:443/oauthclient/redirect.jsp"
                scope="openid profile scope1 email phone address"
                preAuthorizedScope="openid profile"
                enabled="true"/>

Defining the OIDC provider

The next step is to create an openidConnectProvider element, assign an ID value, and create a reference to the previously created oauthProvider.

The ID value is important. For example, if you set the openidConnectProvider element ID value as "OP" using id="OP", the "OP" string is used in OIDC endpoint URLs, and your authorization endpoint is https://<host_name>:<port_number>/oidc/endpoint/OP/authorize, and the token endpoint is https://<host_name>:<port_number>/oidc/endpoint/OP/token.

You can change the ID value to define different OIDC endpoints. Here is a simple OIDC provider configuration that defines the authorization endpoint as https://op.example.com:443/oidc/endpoint/OP/authorize:

<openidConnectProvider id="OP" oauthProviderRef="Oauth" >
</openidConnectProvider>

Sample OIDC provider:

    <openidConnectProvider id="OP" oauthProviderRef="Oauth" >
    </openidConnectProvider>   
    <oauthProvider id="Oauth"  >
        <localStore>            
            <client name="oidcclient" secret="{xor}LDo8LTor"
                displayname="client001"
                redirect="https://client.example.com:443/oauthclient/redirect.jsp"
                scope="openid profile scope1 email phone address"
                preAuthorizedScope="openid profile" 
                enabled="true"/>            
        </localStore>
    </oauthProvider>
    <oauth-roles>
        <authenticated>
            <special-subject type="ALL_AUTHENTICATED_USERS" />  
        </authenticated>
    </oauth-roles>

The OIDC provider setup is complete. Here is a sample server.xml file that provides OIDC services:

Liberty OP configuration with dummy client
<server>
    <featureManager>       
        <feature>openidConnectServer-1.0</feature>
        <feature>ssl-1.0</feature>
        <feature>appSecurity-2.0</feature>
        <feature>servlet-3.0</feature>
    </featureManager>   

    <basicRegistry id="basic" realm="OpBasicRealm">
        <user name="user1" password="security" />
        <user name="user2" password="security" />
    </basicRegistry> 

    <keyStore id="defaultKeyStore" password="keyspass" />

    <httpEndpoint host="op.example.com" httpPort="80" httpsPort="443"    
       id="defaultHttpEndpoint"/>

    <oauth-roles>
        <authenticated>
            <special-subject type="ALL_AUTHENTICATED_USERS" />  
        </authenticated>
    </oauth-roles>

    <openidConnectProvider id="OP" oauthProviderRef="Oauth" >
    </openidConnectProvider>

    <oauthProvider id="Oauth" >
        <localStore>            
            <client name="dummy" secret="{xor}LDo8LTor"
                displayname="dummy"
                redirect="https://localhost:8020/oauthclient/redirect.jsp"
                scope="openid profile scope1 email phone address"
                preAuthorizedScope="openid" 
                enabled="true"/>
        </localStore>
    </oauthProvider> 
</server>

Setting up OIDC Relying Party

Create a new server in Liberty Profile: Go to the <liberty_root>/bin folder and run server create <server_name>. This example uses the server name oidcRP. Find the server.xml file at <liberty_root>/usr/servers/<server_name>/server.xml, open it in an editor, and make the following changes:

  1. In the <featureManager> section, add all of the following features that are missing from your server definition. These updates will enable features required by the OIDC Relying Party:
    Feature list in Liberty RP
    <featureManager>       
            <feature>ssl-1.0</feature>
            <feature>jsp-2.2</feature>
            <feature>servlet-3.0</feature>
            <feature>appSecurity-2.0</feature>
            <feature>openidConnectClient-1.0</feature>
        </featureManager>

    <feature>ldapRegistry-3.0</feature> is not required unless Relying Party wants to verify that the user name in id_token from the OIDC provider exists in the RP registry.

  2. Update httpEndpoint with the machine hostname to handle requests for remote testing, and make sure an https port is defined. If you create an OIDC provider and relying party on the same server, then make sure they use different httpPort and httpsPort.
        <httpEndpoint id="defaultHttpEndpoint"
                      host="rp.example.com"
                      httpPort="80"
                      httpsPort="443" />
  3. Add a keystore to enable SSL: <keyStore id="defaultKeyStore" password="keypass" />. The default keystore and truststore are located in <liberty_root>/usr/servers/<server_name>/resources/ security/key.jks. Be sure to import the OIDC provider certificate into the truststore, since it is required for the RP to receive tokens from the OpenID provider.
  4. Create an openidConnectClient element to contain the OIDC provider information. Here is an example of the openidConnectClient element:
    <openidConnectClient id="oidcRP" 	
        clientId="rp" 		
        clientSecret="{xor}LDo8LTor"	
        authorizationEndpointUrl="https://op.example.com:443/oidc/endpoint/OP/authorize"     
        tokenEndpointUrl="https://op.example.com:443/oidc/endpoint/OP/token"> 	
    </openidConnectClient>

Inside openidConnectClient, you must define "clientId" and "clientSecret", which as "client_id" and "client_secret" are the client's credentials in OP as defined in the OAuth 2.0 client metadata. Depending on the arrangement between RP and OP, these two values can be provided by either RP or OP. They will be edited later.

Liberty OIDC RP provides default values for its redirect_url and requested scope. By default, Liberty OP requests a scope of "opened profile," and the administrator can use the attribute "scope" to modify the required scope. The "redirect_url" is calculated as https://<host name>:<ssl port>/oidcclient/redirect/oidcRP, where oidcclient/redirect is fixed, and other parts can be changed. The /oidcRP has to be the ID value of the openidConnectClient component, and both the host name and ssl port are calculated in the web container.

Registering RP in OP

In order to accept an authorization request from RP, OP must register an RP as an OAuth client:

  1. Prepare RP registration data: As a client, RP registration data includes name, secret, displayname, redirect, scope, preAuthorizedScope, and other attributes. Usually, RP provides redirect to OP during registration, and OP will assign all others, but it is up to negotiation between OP and RP. Assume that RP provides only redirect, as https://rp.example.com:443/oidcclient/redirect/oidcRP.
  2. Add RP to the OP client store, localStore. Assusing OP assigns RP's client data except for redirect with values of name="rp_1", displayname = "test RP", Secret = "RP's secret", and scope = "opened profile email address phone", then the client entry for RP is:
    <client name="RP_1" secret="RP's secret"  displayname="test RP"
    redirect=" https://rp.example.com:443/oidcclient/redirect/oidcRP "
    scope="openid profile email phone address"   enabled="true"/>
  3. Edit the openidConnectClient element in RP server.xml to modify clientId and clientSecret. The openidConnectClient element in RP's server.xml is defined as:
    <openidConnectClient id="oidcRP" 		
        clientId="RP_1" 		
        clientSecret="RP's secret"
        authorizationEndpointUrl="https://op.example.com:443/oidc/endpoint/OP/authorize"     
        tokenEndpointUrl="https://op.example.com:443/oidc/endpoint/OP/token"> 	
    </openidConnectClient>

Exchanging the certificate

OIDC RP must import OIDC OP certificate into RP's trust store in order for RP to request tokens from OP. This example uses the same SSL key file for both OP and RP.

Deploying applications to RP

  1. Download the sample web application testpage.war at the bottom of the tutorial. Install the application by copying it to <liberty_root>/usr/servers/<server_name>/apps/. You can use any web application that has security constraints and roles defined.
  2. Configure the web application for secure access, as shown below:
    <application type="war" id="testpage" name="testpage" 
                location="${server.config.dir}/apps/testpage.war">
        <application-bnd> 
            <security-role name="All Role">
                <special-subject type="ALL_AUTHENTICATED_USERS" />
            </security-role>
        </application-bnd>
    </application>

Validating OIDC web SSO scenario

  1. Start OIDC provider: <liberty_root>/bin/server start oidcOP
  2. Start OIDC RP: <liberty_root>/bin/server start oidcRP
  3. Point web browser to: https://rp.example.com:443/testpage/
  4. Follow the instructions. At the end, you should see:
    Figure 5
    Figure 5
    Figure 5
Sample RP server.xml
<server>
    <featureManager>       
        <feature>ssl-1.0</feature>
        <feature>jsp-2.2</feature>
        <feature>servlet-3.0</feature>
        <feature>appSecurity-2.0</feature>
        <feature>openidConnectClient-1.0</feature>
    </featureManager>

    <openidConnectClient id="RP"  
           scope="openid profile email photo"
           clientId="rp"
           clientSecret="secret"       
       authorizationEndpointUrl="https://op.example.com:443/oidc/endpoint/OP/authorize"
           tokenEndpointUrl="https://op.example.com:443/oidc/endpoint/OP/token" >
    </openidConnectClient>

    <keyStore id="defaultKeyStore" password="keyspass" />     

    <httpEndpoint host="rp.example.com" httpPort="80" httpsPort="443" 
            id="defaultHttpEndpoint"/>        

    <application type="war" id="testpage" name="testpage" 
         location="${server.config.dir}/apps/testpage.war">
        <application-bnd>
            <security-role name="All Role">
                <special-subject type="ALL_AUTHENTICATED_USERS" />
            </security-role>
        </application-bnd>
    </application>  
</server>
Sample OP server.xml
<server>
    <featureManager>       
        <feature>openidConnectServer-1.0</feature>
        <feature>ssl-1.0</feature>
        <feature>appSecurity-2.0</feature>
        <feature>servlet-3.0</feature>
    </featureManager>   

    <basicRegistry id="basic" realm="OpBasicRealm">
        <user name="testuser" password="testuserpwd" />
        <user name="user1" password="security" />
        <user name="user2" password="security" />
    </basicRegistry> 

    <keyStore id="defaultKeyStore" password="keyspass" />

    <httpEndpoint host="op.example.com" httpPort="80" httpsPort="443" id="defaultHttpEndpoint"/>

    <oauth-roles>
        <authenticated>
            <special-subject type="ALL_AUTHENTICATED_USERS" />  
        </authenticated>
    </oauth-roles>

    <openidConnectProvider id="OP" oauthProviderRef="Oauth" >
    </openidConnectProvider>

    <oauthProvider id="Oauth"  >
        <localStore>   
            <client name="rp" secret="{xor}LDo8LTor"
                displayname="rp"
                redirect="https://rp.example.com:443/oidcclient/redirect/RP"
                scope="openid profile scope1 email phone address"
                enabled="true"/>
        </localStore>
    </oauthProvider> 
</server>

Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Middleware, Cloud computing
ArticleID=997503
ArticleTitle=Using OpenID Connect in WebSphere Application Server Liberty Profile
publish-date=02112015