The December 2011 release of Tivoli Federated Identity Manger 6.2.2 includes support for OAuth 1.0 and 2.0 delegated authorization protocols. Included in this support for each protocol are three extensible programming interfaces that may be used to manage certain configuration and persistence information used for OAuth flows. The supported extension interfaces are:
|Title||Description||Link to development material|
|Client Configuration Provider||Permits externalization of the storage and management of OAuth client registration data including client identifiers, secrets and redirect URI’s.||Client Configuration Provider|
|Token Cache||Permits externalized storage and query of OAuth grants and tokens||Token OAuth Token Cache Extension Point|
|Trusted Clients Manager||Permits externalized storage and query of resource owner authorization decisions. This can be used to decide if a resource owner needs to be prompted for consent-to-authorize at the authorize endpoint. It will also be queried when a resource owner wishes to browse their stored consent decisions.||Trusted Clients Manager|
As you can see from the links in the table above there is already detailed development information available for these extension points. The purpose of this particular article is to provide TFIM customers with example ready-to-use plugins (including source) for database-based implementations of these extension points. The examples included in this article are not directly supported by IBM, however the extension points that they utilize are fully supported. Source code is provided allowing you to build, modify and extend the examples as needed for your own requirements. The example code utilizes JDBC API’s to store and retrieve information from DB2, and it should be relatively straight forward to deploy this same pattern for other database types.
There are several simple reasons why these plugins are shipped as example code rather than directly in TFIM itself as supported implementations.
- You will find that you can implement some interesting customized behaviour in these extensions – for example limiting the number of access or refresh tokens that are issued to a client per resource owner, and permitting fine-grained revocation interfaces.
- TFIM does not ship a database license for general use – this is something you either already have and use in your organization or need to acquire if you want to use database storage for the OAuth extensions.
- There are far more database and storage technologies in the market than we can economically test and individually support.
Based on this extensive variability a conscious decision was made to establish support at the point of the programming extension points.
TFIM does ship default implementations for each of these OAuth extension points and for a variety of scenarios these will be sufficient. A persistence-based approach such as that demonstrated in these JDBC implementations will be best suited to scenarios where one or more of the follow apply:
- long-lived access or refresh tokens are issued (use a custom token cache implementation)
- self-registration of clients is desired (use custom client configuration provider and store client config data external to TFIM)
- fine-grained revocation capabilities are required (use a custom trusted clients manager and token cache)
The rest of this article is geared at design and deployment guidelines for the example JDBC plugins in a DB2 environment. The plugins may be used independently however there is one optional revocation capability demonstrated in the trusted client manager extension that requires you also use the JDBC implementation for the token cache extension.
Operating System and Database Preparation
First create an operating-system user called fimjdbc. This will be the username used to connect to the database. For DB2 it’s important this username is 8 characters or less. Set the password to non-expiring and something you can remember. This will need to be configured in WebSphere later.
For the purposes of preparing this article I used the trial edition of DB2 Data Server 9.7 for Linux available from http://www-01.ibm.com/software/data/db2/linux-unix-windows/download.html. My installation was the Enterprise Server Edition with default installation options.
Creating Database, Tables, Indexes and Grants
The commands in this section show all the tables necessary for BOTH OAuth 1.0 and 2.0 implementations used by the example plugins. Of course you can change these table schemas so long as you also make sure the SQL commands executed from the example plugins are modified to suit. Note that the trusted clients manager implementation uses the same table for both OAuth versions however the token cache and external clients manager are different tables for OAuth 1.0 and 2.0. You only need to create tables for the version of OAuth you are using however it causes no problems if you create all the tables.
Using the DB2 command line:
# su – db2inst1 db2 => db2start db2 => CREATE DATABASE OAuthDB db2 => CONNECT TO OAuthDB CREATE TABLE OAuthDBSchema.TRUSTEDCLIENTS \ (USERNAME VARCHAR(256) NOT NULL, \ FEDERATIONID VARCHAR(256) NOT NULL, \ CLIENTID VARCHAR(256) NOT NULL, \ UNIQUEID VARCHAR(256) NOT NULL, \ TRUSTLEVEL VARCHAR(256) NOT NULL) CREATE TABLE OAuthDBSchema.PERMITTEDSCOPES \ (UNIQUEID VARCHAR(256) NOT NULL, \ SCOPE VARCHAR(256) NOT NULL) CREATE TABLE OAuthDBSchema.OAUTH10CACHE \ (LOOKUPKEY VARCHAR(256) NOT NULL, \ FEDERATIONID VARCHAR(256) NOT NULL, \ TYPE VARCHAR(32) NOT NULL, \ CREATEDAT BIGINT, \ LIFETIME INT, \ EXPIRES BIGINT, \ TOKENSTRING VARCHAR(256) NOT NULL, \ CLIENTID VARCHAR(64) NOT NULL, \ SECRET VARCHAR(64), \ USERNAME VARCHAR(64), \ SCOPE VARCHAR(512), \ CALLBACK VARCHAR(256), \ STATEID VARCHAR(64) NOT NULL, \ VERIFIER VARCHAR(64), \ REALM VARCHAR(128)) CREATE TABLE OAuthDBSchema.OAUTH10NONCE \ (LOOKUPKEY VARCHAR(64) NOT NULL, \ CREATEDAT BIGINT, LIFETIME INT, \ EXPIRES BIGINT) CREATE TABLE OAuthDBSchema.OAUTH10CLIENTCONFIG \ (FEDERATIONID VARCHAR(256) NOT NULL, \ CLIENTID VARCHAR(256) NOT NULL, \ CLIENTSECRET VARCHAR(256), \ DISPLAYNAME VARCHAR(256) NOT NULL, \ CALLBACKURI VARCHAR(256), \ ALLOWCALLBACKOVERRIDE INT, \ ENABLED INT) CREATE TABLE OAuthDBSchema.OAUTH20CACHE \ (LOOKUPKEY VARCHAR(256) NOT NULL, \ UNIQUEID VARCHAR(128) NOT NULL, \ FEDERATIONID VARCHAR(256) NOT NULL, \ TYPE VARCHAR(64) NOT NULL, \ SUBTYPE VARCHAR(64), \ CREATEDAT BIGINT, \ LIFETIME INT, \ EXPIRES BIGINT, \ TOKENSTRING VARCHAR(2048) NOT NULL, \ CLIENTID VARCHAR(64) NOT NULL, \ USERNAME VARCHAR(64) NOT NULL, \ SCOPE VARCHAR(512) NOT NULL, \ REDIRECTURI VARCHAR(256), \ STATEID VARCHAR(64) NOT NULL) CREATE TABLE OAuthDBSchema.OAUTH20CLIENTCONFIG \ (FEDERATIONID VARCHAR(256) NOT NULL, \ CLIENTID VARCHAR(256) NOT NULL, \ CLIENTSECRET VARCHAR(256), \ DISPLAYNAME VARCHAR(256) NOT NULL, \ REDIRECTURI VARCHAR(256), \ ENABLED INT) ALTER TABLE OAuthDBSchema.TRUSTEDCLIENTS ADD CONSTRAINT PK_UNIQUEID PRIMARY KEY (UNIQUEID) ALTER TABLE OAuthDBSchema.PERMITTEDSCOPES ADD CONSTRAINT PK_UNIQUEIDSCOPE PRIMARY KEY (UNIQUEID,SCOPE) ALTER TABLE OAuthDBSchema.OAUTH10CACHE ADD CONSTRAINT PK_LOOKUPKEY PRIMARY KEY (LOOKUPKEY) ALTER TABLE OAuthDBSchema.OAUTH10NONCE ADD CONSTRAINT PK_LOOKUPKEY PRIMARY KEY (LOOKUPKEY) ALTER TABLE OAuthDBSchema.OAUTH10CLIENTCONFIG ADD CONSTRAINT PK_FEDIDCLIENTID PRIMARY KEY (FEDERATIONID,CLIENTID) ALTER TABLE OAuthDBSchema.OAUTH20CACHE ADD CONSTRAINT PK_LOOKUPKEY PRIMARY KEY (LOOKUPKEY) ALTER TABLE OAuthDBSchema.OAUTH20CLIENTCONFIG ADD CONSTRAINT PK_FEDIDCLIENTID PRIMARY KEY (FEDERATIONID,CLIENTID) CREATE INDEX OAUTH10CACHE_EXPIRES ON OAUTHDBSCHEMA.OAUTH10CACHE (EXPIRES ASC) CREATE INDEX OAUTH20CACHE_EXPIRES ON OAUTHDBSCHEMA.OAUTH20CACHE (EXPIRES ASC) GRANT ALL ON OAuthDBSchema.TRUSTEDCLIENTS TO USER fimjdbc GRANT ALL ON OAuthDBSchema.PERMITTEDSCOPES TO USER fimjdbc GRANT ALL ON OAuthDBSchema.OAUTH10CACHE TO USER fimjdbc GRANT ALL ON OAuthDBSchema.OAUTH10NONCE TO USER fimjdbc GRANT ALL ON OAuthDBSchema.OAUTH10CLIENTCONFIG TO USER fimjdbc GRANT ALL ON OAuthDBSchema.OAUTH20CACHE TO USER fimjdbc GRANT ALL ON OAuthDBSchema.OAUTH20CLIENTCONFIG TO USER fimjdbc GRANT ALL ON OAuthDBSchema.OAUTH10CLIENTCONFIG TO USER fimjdbc
Example client registrations
Here are a couple of example insert commands that register OAuth 1.0 and 2.0 clients. Naturally you will need to modify these values depending on your federation configuration and point of contact:
INSERT INTO OAuthDBSchema.OAUTH10CLIENTCONFIG \ (FEDERATIONID, \ CLIENTID, \ CLIENTSECRET, \ DISPLAYNAME, \ CALLBACKURI, \ ALLOWCALLBACKOVERRIDE, \ ENABLED) VALUES \ ('https://profile.ibmidentitydemo.com/FIM/sps/oauth10sp/oauth10', \ 'key', \ 'secret', \ 'My Client', \ 'https://profile.ibmidentitydemo.com/FIM/fimivt/oauth/oauthClient.jsp', \ 1, \ 1) INSERT INTO OAuthDBSchema.OAUTH20CLIENTCONFIG \ (FEDERATIONID, \ CLIENTID, \ CLIENTSECRET, \ DISPLAYNAME, \ REDIRECTURI, \ ENABLED) VALUES \ ('https://profile.ibmidentitydemo.com/FIM/sps/oauth20sp/oauth20', \ 'key', \ 'secret', \ 'My Client', \ 'https://profile.ibmidentitydemo.com/FIM/fimivt', \ 1)
Next you need to determine the TCP/IP port used by your DB2 instance if you don’t already have it recorded:
db2=> get dbm cfg
In the output look for line similar to:
TCP/IP Service name (SVCENAME) = db2c_db2inst1
If the value is a string name such as shown above, look up /etc/services to find the port number:
# grep db2c_db2inst1 /etc/services db2c_db2inst1 50003/tcp
Here you can see the port number on my server is 50003. This will be used later in the WebSphere configuration.
JDBC Provider Configuration
Next use the WebSphere administration console to configure a JDBC provider and data source. Navigate to Resources -> JDBC -> JDBC Providers, then:
- Pick a scope (I used server)
- Click New. The wizard will start.
- Database Type: DB2.
- Provider Type: DB2 Universal JDBC Provider.
- Implementation Type: Connection pool data source.
- Click Next.
- Set paths for the JDBC driver. These will be dependent on your DB2 installation:
- DB2_UNIVERSAL_JDBC_DRIVER_PATH = /home/db2inst1/sqllib/java
- DB2_UNIVERSAL_JDBC_DRIVER_NATIVEPATH = /home/db2inst1/sqllib/lib
- Click Next, then Finish.
- Save the config.
Now create a J2C authentication alias for connecting to the database. Navigate to Security->Secure administration, applications and infrastructure->Java Authentication and Authorization Service->J2C authentication data:
- Click New.
- Alias: fimalias
- UserID: fimjdbc <===== This is the operating system user we created originally.
- Password: That user's password.
- Click OK.
- Save Config.
Data Source Configuration
Now create the data source that the plugins will use. Navigate to Resources->JDBC->Data Sources.
- Pick a scope (server is what I used)
- Click New. Wizard will start.
- Data source name: FIM OAuth JDBC Service
- JNDI Name: jdbc/OAuthDB
- Click Next.
- Select an existing JDBC Provider. You should be able to select the DB2 Universal JDBC Driver Provider configured earlier.
- Click Next.
- Database name: OAuthDB
- Driver type: 4
- Server name: <DB2 server> - maybe localhost
- Port number: <See the comments on SVCNAME above - in my example this is 50003>
- Container Managed Persistence: Checked
- Click Next.
- Select the fimalias J2C authentication alias for container-managed authentication.
- Click Next, then Finish.
- Save the config.
You can now test the connection, and TFIM should work when configured with the JDBC plugins for OAuth.
Deploying and configuring the JDBC Plug-ins in TFIM
Using your own build, or the JDBC plug-ins jar attached to this article:
- Copy the jar file to the <fim_install_root>/plugins directory
- Using the TFIM console, navigate to Domain Management -> Runtime Node Management, then:
- Deploy plugins
- Reload configurations
Use the TFIM console to navigate to your OAuth federation properties panel. The custom plugins should now be available in the menu options for each of the plug-in extension types, as shown in the snippets below:
Implementation Details for the Sample Plug-ins
External Client Provider
The JDBC external client provider is very simple. The plug-in provides runtime retrieval only of OAuth client configuration data from the database. Any updates to the database (including registering new clients) must be done by your own processes external to TFIM. TFIM will check that a client is valid and enabled by during the authorization and token phases of an OAuth flow as well as each time that an access token is validated. If you wish to revoke all access to a client you can either delete the client or simply set the enabled column in the database for that client to 0. The only configuration for the module is the JNDI name for the data source which defaults to jdbc/OAuthDB.
As a bonus piece of example code I have included a WebSphere EAR application OAuthJDBC.ear with full source that provides very rudimentary client management. You can add and edit both OAuth 1.0 and 2.0 clients with this application. The application assumes the data source name is jdbc/OAuthDB. The URL's to access the application management pages are:
- OAuth 1.0: https://yourwebsphere/OAuthJDBC/oauth10Clients.jsp
- OAuth 2.0: https://yourwebsphere/OAuthJDBC/oauth20Clients.jsp
The token cache implementations facilitate storage and retrieval of OAuth tokens and grants (including authorization codes and refresh tokens for OAuth 2.0). The token cache implementations include a rudimentary cleanup thread which will periodically remove expired tokens from the database. The plugins support a "cleanup interval" configuration parameter for this purposes which defaults to 300 seconds. Another example configuration item shown in this plug-in for OAuth 2.0 is a facility to restrict the number of refresh tokens that have been issued to a client for a particular resource owner to 1. That means that whenever a client obtains a new refresh token for a particular resource owner (for example from a new authorization code flow initiated by the client) then any existing active refresh token for that client/resource-owner will be deleted. This is an example of how you can implement some of your own constraints regarding grants and tokens within the plug-ins. The module also has a configuration item for the JNDI name for the data source which defaults to jdbc/OAuthDB.
Trusted Clients Manager
The trusted clients manager extension point serves two purposes:
- During authorize endpoint processing it is used to both lookup and store resource owner authorization consent decision information (including scopes) in the database. These decisions are used to if a resource owner needs to be prompted for consent during the resource owner authorization step of an OAuth flow. Provided all the requested scopes have been previously consented by the user, interactive consent will not be required more than once.
- TFIM provides a trusted clients management URL endpoint for each federation. A resource owner can use this endpoint to view (and remove) their remembered consent decisions. TFIM only provides the ability to completely remove the remembered decision - not modify specific attributes associated with it. That said, once you are storing these consent decisions in your own database you may always write your own UI for resource owners to manipulate consent decisions if you have specialized requirements. When a decision is remove the resource owner is typically prompted for consent again on their next visit to the authorize endpoint.
The example plug-in included with this article offers an additional configuraiton option that will only work if the federation is also configured to use the JDBC token cache. The configuration option is a checkbox titled Auto-Revoke Tokens from JDBC Token Cache and when selected the plug-in will delete all existing access tokens and grants for a particular client/resource-owner at the time a resource owner deletes their remembered consent decision for that client. This has the effect of immediately revoking access for that client even if the client has a current non-expired access token.
Debugging and Troubleshooting
All debugging of the modules can be done using the WebSphere trace facility (Troubleshooting -> Logs and Trace in the WebSphere administration console). The plug-ins themselves use a common java package name and as such you can trace all execution of them using the trace string:
- com.tivoli.am.fim.demo.oauth.jdbcplugins_1.0.0.jar Example JDBC plug-ins including source
- OAuthJDBC.ear helper application for managing client registrations
The intent of this article is to provide TFIM customers wishing to leverage OAuth service provider capabilities in TFIM 6.2.2 with practical example implementation code for database-managed persistence of state and client configuration data. You should also note that by using custom implementations of the plug-in extension points you can implement advanced requirements around token lifetime management and revocation whilst still leverage TFIM for endpoint managmement, token creation and a rich set of authorization enforcement points.
For any further information on the TFIM OAuth implementation or for advice on your own development of plug-ins, please contact me.