Friday, May 6, 2016

An interop demo between Apache CXF Fediz and Facebook

The previous post showed how to configure the Fediz IdP to interoperate with the Google OpenID Connect provider. In addition to supporting WS-Federation, SAML SSO and OpenID Connect, from the forthcoming 1.3.1 release the Fediz IdP will also support authenticating users via Facebook Connect. Facebook Connect is based on OAuth 2.0 and hence the implementation in Fediz shares quite a lot of common code with the OpenID Connect implementation. In this post, we will show how to configure the Fediz IdP to interoperate with Facebook.

1) Create a new app in the Facebook developer console

The first step is to create a new application in the Facebook developer console. Create a Facebook developer account. Login and register as a Facebook developer. Click on "My Apps" and "Add a New App". Select the "website" platform and create a new app with the name "Fediz IdP". Enter your email, select a Category and click on "Create App ID". Enter "https://localhost:8443/fediz-idp/federation" for the Site URL. Now go to the dashboard for the application and copy the App ID + App Secret and save for later.

2) Testing the newly created Client

To test that everything is working correctly, open a web browser and navigate to, substituting the client id saved in step "1" above:
  • https://www.facebook.com/dialog/oauth?response_type=code&client_id=<app-id>&redirect_uri=https://localhost:8443/fediz-idp/federation&scope=email
Login using your Facebook credentials and grant permission on the OAuth consent screen. The browser will then attempt a redirect to the given redirect_uri. Copy the URL + extract the "code" query String. Open a terminal and invoke the following command, substituting in the secret and code extracted above:
  • curl --data "client_id=<app-id>&client_secret=<app-secret>&grant_type=authorization_code&code=<code>&redirect_uri=https://localhost:8443/fediz-idp/federation" https://graph.facebook.com/v2.6/oauth/access_token
You should see a successful response containing the OAuth 2.0 Access Token.

3) Install and configure the Apache CXF Fediz IdP and sample Webapp

Follow a previous tutorial to deploy the latest Fediz IdP + STS to Apache Tomcat, as well as the "simpleWebapp". Note that you will need to use at least Fediz 1.3.1-SNAPSHOT here for Facebook support. Test that the "simpleWebapp" is working correctly by navigating to the following URL (selecting "realm A" at the IdP, and authenticating as "alice/ecila"):
  • https://localhost:8443/fedizhelloworld/secure/fedservlet 
3.1) Configure the Fediz IdP to communicate with the Facebook IdP

Now we will configure the Fediz IdP to authenticate the user in "realm B" by using the Facebook Connect protocol. Edit 'webapps/fediz-idp/WEB-INF/classes/entities-realma.xml'. In the 'idp-realmA' bean:
  • Change the port in "idpUrl" to "8443". 
In the 'trusted-idp-realmB' bean:
  • Change the "url" value to "https://www.facebook.com/dialog/oauth".
  • Change the "protocol" value to "facebook-connect".
  • Delete the "certificate" and "trustType" properties.
  • Add the following parameters Map, filling in a value for the client secret extracted above: <property name="parameters">
                <util:map>
                    <entry key="client.id" value="<app-id>" />
                    <entry key="client.secret" value="<app-secret>" />
                </util:map>
            </property>
It is possible to specify custom scopes via a "scope" parameter. The default value for this parameter is "email". The token endpoint can be configured via a "token.endpoint" parameter (defaults to "https://graph.facebook.com/v2.6/oauth/access_token"). Similarly, the API endpoint used to retrieve the user's email address can be configured via "api.endpoint", defaulting to "https://graph.facebook.com/v2.6". The Subject claim to be used to insert into a SAML Assertion is defined via "subject.claim", which defaults to "email".

3.2) Update the TLS configuration

By default, the Fediz IdP is configured with a truststore required to access the Fediz STS. However, this would mean that the requests to the Facebook IdP over TLS will not be trusted. to change this, edit 'webapps/fediz-idp/WEB-INF/classes/cxf-tls.xml', and change the HTTP conduit name from "*.http-conduit" to "https://localhost.*". This means that this configuration will only get picked up when communicating with the STS (deployed on "localhost"), and the default JDK truststore will get used when communicating with the Facebook IdP.

3.3) Update Fediz STS claim mapping

The STS will receive a SAML Token created by the IdP representing the authenticated user. The Subject name will be the email address of the user as configured above. Therefore, we need to add a claims mapping in the STS to map the principal received to some claims. Edit 'webapps/fediz-idp-sts/WEB-INF/userClaims.xml' and just copy the entry for "alice" in "userClaimsREALMA", changing "alice" to your email address used to log into Facebook.

Finally, restart Fediz to pick up the changes (you may need to remove the persistent storage first).

4) Testing the service

To test the service navigate to:
  • https://localhost:8443/fedizhelloworld/secure/fedservlet
Select "realm B". You should be redirected to the Facebook authentication page. Enter the user credentials you have created. You will be redirected to Fediz, where it creates a SAML token representing the act of authentication via the trusted third party IdP,  and redirects to the web application.

Friday, April 29, 2016

An interop demo between Apache CXF Fediz and Google OpenID Connect

The previous post introduced some of the new features in Apache CXF Fediz 1.3.0. One of the new enhancements is that the Fediz IdP can now delegate WS-Federation (and SAML SSO) authentication requests to a third party IdP via OpenID Connect. An article published in February showed how it is possible to interoperate between Fediz and the Keycloak OpenID Connect provider. In this post, we will show how to configure the Fediz IdP to interoperate with the Google OpenID Connect provider.

1) Create a new client in the Google developer console

The first step is to create a new client in the Google developer console to represent the Apache CXF Fediz IdP. Login to the Google developer console and create a new project. Click on "Enable and Manage APIs" and then select "Credentials". Click on "Create Credentials" and select "OAuth client id". Configure the OAuth consent screen and select "web application" as the application type. Specify "https://localhost:8443/fediz-idp/federation" as the authorized redirect URI. After creating the client, a pop-up window will specify the newly created client id and secret. Save both values locally. The Google documentation is available here.

2) Testing the newly created Client

It's possible to see the Google OpenId Connect configuration by navigating to:
  • https://accounts.google.com/.well-known/openid-configuration
This tells us what the authorization and token endpoints are, both of which we will need to configure the Fediz IdP. To test that everything is working correctly, open a web browser and navigate to, substituting the client id saved in step "1" above:
  • https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=<client-id>&redirect_uri=https://localhost:8443/fediz-idp/federation&scope=openid
Login using your google credentials and grant permission on the OAuth consent screen. The browser will then attempt a redirect to the given redirect_uri. Copy the URL + extract the "code" query String. Open a terminal and invoke the following command, substituting in the secret and code extracted above:
  • curl -u <client-id>:<secret> --data "client_id=<client-id>&grant_type=authorization_code&code=<code>&redirect_uri=https://localhost:8443/fediz-idp/federation" https://www.googleapis.com/oauth2/v4/token
You should see a succesful response containing (amongst other things) the OAuth 2.0 Access Token and the OpenId Connect IdToken, containing the user identity.

3) Install and configure the Apache CXF Fediz IdP and sample Webapp

Follow a previous tutorial to deploy the latest Fediz IdP + STS to Apache Tomcat, as well as the "simpleWebapp". Note that you will need to use Fediz 1.3.0 here for OpenId Connect support. Test that the "simpleWebapp" is working correctly by navigating to the following URL (selecting "realm A" at the IdP, and authenticating as "alice/ecila"):
  • https://localhost:8443/fedizhelloworld/secure/fedservlet 
3.1) Configure the Fediz IdP to communicate with the Google IdP

Now we will configure the Fediz IdP to authenticate the user in "realm B" by using the OpenID Connect protocol. Edit 'webapps/fediz-idp/WEB-INF/classes/entities-realma.xml'. In the 'idp-realmA' bean:
  • Change the port in "idpUrl" to "8443". 
In the 'trusted-idp-realmB' bean:
  • Change the "url" value to "https://accounts.google.com/o/oauth2/v2/auth".
  • Change the "protocol" value to "openid-connect-1.0".
  • Delete the "certificate" and "trustType" properties.
  • Add the following parameters Map, filling in a value for the client secret extracted above: <property name="parameters">
                <util:map>
                    <entry key="client.id" value="<client-id>" />
                    <entry key="client.secret" value="<secret>" />
                    <entry key="token.endpoint" value="https://accounts.google.com/o/oauth2/token" />
                    <entry key="scope" value="openid profile email"/>
                    <entry key="jwks.uri" value="https://www.googleapis.com/oauth2/v3/certs" />
                    <entry key="subject.claim" value="email"/>
                </util:map>
            </property>
There are a few additional properties configured here compared to the previous tutorial. It is possible to specify custom scopes via the "scope" parameter. In this case we are requesting the "profile" and "email" scopes. The default value for this parameter is "openid". In addition, rather than validating the signed IdToken using a local certificate, here we are specifying a value for "jwks.uri", which is the location of the signing key. The "subject.claim" property specifies the claim name from which to obtain the Subject name, which is inserted into a SAML Token that is sent to the STS.

3.2) Update the TLS configuration

By default, the Fediz IdP is configured with a truststore required to access the Fediz STS. However, this would mean that the requests to the Google IdP over TLS will not be trusted. to change this, edit 'webapps/fediz-idp/WEB-INF/classes/cxf-tls.xml', and change the HTTP conduit name from "*.http-conduit" to "https://localhost.*". This means that this configuration will only get picked up when communicating with the STS (deployed on "localhost"), and the default JDK truststore will get used when communicating with the Google IdP.

3.3) Update Fediz STS claim mapping

The STS will receive a SAML Token created by the IdP representing the authenticated user. The Subject name will be the email address of the user as configured above. Therefore, we need to add a claims mapping in the STS to map the principal received to some claims. Edit 'webapps/fediz-idp-sts/WEB-INF/userClaims.xml' and just copy the entry for "alice" in "userClaimsREALMA", changing "alice" to your Google email address.

Finally, restart Fediz to pick up the changes (you may need to remove the persistent storage first).

4) Testing the service

To test the service navigate to:
  • https://localhost:8443/fedizhelloworld/secure/fedservlet
Select "realm B". You should be redirected to the Google authentication page. Enter the user credentials you have created. You will be redirected to Fediz, where it converts the received JWT token to a token in the realm of Fediz (realm A) and redirects to the web application.

Monday, April 25, 2016

Apache CXF Fediz 1.3.0 released

A new major release (1.3.0) of Apache CXF Fediz was released a few weeks ago. There are some major dependency updates as part of this release:
  • The core Apache CXF dependency is updated from the 3.0.x branch to the 3.1.x branch (3.1.6 to be precise)
  • The Spring dependency of the IdP is updated from the 3.2.x branch to the 4.1.x branch.
Fediz contains a number of container plugins to support the Passive Requestor Profile of WS-Federation. The 1.3.0 release now supports container plugins for:
  • Websphere
  • Jetty 8 and 9 (new)
  • Apache Tomcat 7 and 8 (new)
  • Spring Security 2 and 3
  • Apache CXF.
The Identity Provider (IdP) service has the following new features:
  • The IdP now supports protocol bridging with OpenId Connect IdPs (see previous article on an interop demo with Keycloak).
  • The IdP is now capable of supporting the SAML SSO protocol natively, in addition to the Passive Requestor Profile of WS-Federation.
  • A new IdP service is now available which supports OpenId Connect by leveraging Apache CXF. By default it delegates authentication to the existing Fediz IdP using WS-Federation.
In a nutshell, the Fediz 1.3.0 IdP supports user authentication via the WS-Federation, SAML SSO and OpenId Connect protocols, and it can also bridge between all of these different protocols. This is a compelling selling point of Fediz, and one I will explore more in some forthcoming articles.

Wednesday, March 2, 2016

Using the CXF failover feature to authenticate to multiple Apache Syncope instances

A couple of years ago, I described a testcase that showed how an Apache CXF web service endpoint could send a username/password received via WS-Security to Apache Syncope for authentication. In this article, I'll extend that testcase to make use of the CXF failover feature. The failover feature allows the client to use a set of alternative addresses when the primary endpoint address is unavailable. For the purposes of the demo, instead of a single Apache Syncope instance, we will set up two instances which share the same internal storage. When the first/primary instance goes down, the failover feature will automatically switch to use the second instance.

1) Set up the Apache Syncope instances

1.a) Set up a database for Internal Storage

Apache Syncope persists internal storage to a database via Apache OpenJPA. For the purposes of this demo, we will set up MySQL. Install MySQL in $SQL_HOME and create a new user for Apache Syncope. We will create a new user "syncope_user" with password "syncope_pass". Start MySQL and create a new Syncope database:
  • Start: sudo $SQL_HOME/bin/mysqld_safe --user=mysql
  • Log on: $SQL_HOME/bin/mysql -u syncope_user -p
  • Create a Syncope database: create database syncope; 
1.b) Set up containers to host Apache Syncope

We will deploy Syncope to Apache Tomcat. Download Tomcat + extract it twice (calling it first-instance + second-instance). In both instances, edit 'conf/context.xml', and uncomment the the "<Manager pathname="" />" configuration. Also in 'conf/context.xml', add a datasource for internal storage:

<Resource name="jdbc/syncopeDataSource" auth="Container"
    type="javax.sql.DataSource"
    factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
    testWhileIdle="true" testOnBorrow="true" testOnReturn="true"
    validationQuery="SELECT 1" validationInterval="30000"
    maxActive="50" minIdle="2" maxWait="10000" initialSize="2"
    removeAbandonedTimeout="20000" removeAbandoned="true"
    logAbandoned="true" suspectTimeout="20000"
    timeBetweenEvictionRunsMillis="5000" minEvictableIdleTimeMillis="5000"
    jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
    org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
    username="syncope_user" password="syncope_pass"
    driverClassName="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/syncope?characterEncoding=UTF-8"/>

The next step is to enable a way to deploy applications to Tomcat using the Manager app. Edit 'conf/tomcat-users.xml' in both instances and add the following:

<role rolename="manager-script"/>
<user username="manager" password="s3cret" roles="manager-script"/>

Next, download the JDBC driver jar for MySQL and put it in Tomcat's 'lib' directory in both instances. Edit 'conf/server.xml' of the second instance, and change the port to "9080", and change the other ports to avoid conflict with the first Tomcat instance. Now start both Tomcat instances.

1.c) Install Syncope to the containers

Download and run the Apache Syncope installer. Install it to Tomcat using MySQL as the database. For more info on this, see a previous tutorial. Run this twice to install Syncope in both Apache Tomcat instances.

1.d) Configure the container to share the same database

Next we need to configure both containers to share the same database. Edit 'webapps/syncope/WEB-INF/classes/persistenceContextEMFactory.xml' in the first instance, and change the 'openjpa.RemoteCommitProvider' to:
  • <entry key="openjpa.RemoteCommitProvider" value="tcp(Port=12345, Addresses=127.0.0.1:12345;127.0.0.1:12346)"/>
Similarly, change the value in the second instance to:
  • <entry key="openjpa.RemoteCommitProvider" value="tcp(Port=12346, Addresses=127.0.0.1:12345;127.0.0.1:12346)"/>
This is necessary to ensure data consistency across the two Syncope instances. Please see the Syncope cluster page for more information.

1.e) Add users

In the first Tomcat instance running on port 8080, go to http://localhost:8080/syncope-console, and add two new roles "employee" and "boss". Add two new users, "alice" and "bob" both with password "security". "alice" has both roles, but "bob" is only an "employee". Now logout, and login to the second instance running on port 9080. Check that the newly created users are available.

2) The CXF testcase

The CXF testcase is available in github:
  • cxf-syncope-failover: This project contains a number of tests that show how an Apache CXF service endpoint can use the CXF Failover feature, to authenticate to different Apache Syncope instances.
A CXF client sends a SOAP UsernameToken to a CXF Endpoint. The CXF Endpoint has been configured (see cxf-service.xml) to validate the UsernameToken via the SyncopeUTValidator, which dispatches the username/passwords to Syncope for authentication via Syncope's REST API.

The SyncopeUTValidator is configured to use the CXF failover feature with the address of the primary Syncope instance ("first-instance" above running on 8080). It is also configured with a list of alternative addresses to try if the first instance is down (in this case the "second-instance" running on 9080).

The test makes two invocations. The first should successfully authenticate to
the first Syncope instance. Now the test sleeps for 10 seconds after prompting
you to kill the first Syncope instance. It should successfully failover to the
second Syncope instance on the second invocation.

Friday, February 26, 2016

Support for OpenId Connect protocol bridging in Apache CXF Fediz 1.3.0

Apache CXF Fediz 1.3.0 will be released in the near future. One of the new features of Fediz 1.2.0 (released last year) was the ability to act as an identity broker with a SAML SSO IdP. In the 1.3.0 release, Apache CXF Fediz will have the ability to act as an identity broker with an OpenId Connect IdP. In other words, the Fediz IdP can act as a protocol bridge between the WS-Federation and OpenId Connect protocols. In this article, we will look at an interop test case with Keycloak.

1) Install and configure Keycloak

Download and install the latest Keycloak distribution (tested with 1.8.0). Start keycloak in standalone mode by running 'sh bin/standalone.sh'.

1.1) Create users in Keycloak

First we need to create an admin user by navigating to the following URL, and entering a password:
  • http://localhost:8080/auth/
Click on the "Administration Console" link, logging on using the admin user credentials. You will see the configuration details of the "Master" realm. For the purposes of this demo, we will create a new realm. Hover the mouse pointer over "Master" in the top left-hand corner, and click on "Add realm". Create a new realm called "realmb". Now we will create a new user in this realm. Click on "Users" and select "Add User", specifying "alice" as the username. Click "save" and then go to the "Credentials" tab for "alice", and specify a password, unselecting the "Temporary" checkbox, and reset the password.

1.2) Create a new client application in Keycloak

Now we will create a new client application for the Fediz IdP in Keycloak. Select "Clients" in the left-hand menu, and click on "Create". Specify the following values:
  • Client ID: realma-client
  • Client protocol: openid-connect
  • Root URL: https://localhost:8443/fediz-idp/federation
Once the client is created you will see more configuration options:
  • Select "Access Type" to be "confidential".
Now go to the "Credentials" tab of the newly created client and copy the "Secret" value. This will be required in the Fediz IdP to authenticate to the token endpoint in Keycloak.

1.3) Export the Keycloak signing certificate

Finally, we need to export the Keycloak signing certificate so that the Fediz IdP can validate the signed JWT Token from Keycloak. Select "Realm Settings" (for "realmb") and click on the "Keys" tab. Copy and save the value specified in the "Certificate" textfield.

1.4) Testing the Keycloak configuration

It's possible to see the Keycloak OpenId Connect configuration by navigating to:
  • http://localhost:8080/auth/realms/realmb/.well-known/openid-configuration
This tells us what the authorization and token endpoints are, both of which we will need to configure the Fediz IdP. To test that everything is working correctly, open a web browser and navigate to:
  • localhost:8080/auth/realms/realmb/protocol/openid-connect/auth?response_type=code&client_id=realma-client&redirect_uri=https://localhost:8443/fediz-idp/federation&scope=openid
Login using the credentials you have created for "alice". Keycloak will then attempt to redirect to the given "redirect_uri" and so the browser will show a connection error message. However, copy the URL + extract the "code" query String. Open a terminal and invoke the following command, substituting in the secret and code extracted above:
  • curl -u realma-client:<secret> --data "client_id=realma-client&grant_type=authorization_code&code=<code>&redirect_uri=https://localhost:8443/fediz-idp/federation" http://localhost:8080/auth/realms/realmb/protocol/openid-connect/token
You should see a succesful response containing (amongst other things) the OAuth 2.0 Access Token and the OpenId Connect IdToken, containing the user identity.

2) Install and configure the Apache CXF Fediz IdP and sample Webapp

Follow a previous tutorial to deploy the latest Fediz IdP + STS to Apache Tomcat, as well as the "simpleWebapp". Note that you will need to use Fediz 1.3.0 here (or the latest SNAPSHOT version) for OpenId Connect support. Test that the "simpleWebapp" is working correctly by navigating to the following URL (selecting "realm A" at the IdP, and authenticating as "alice/ecila"):
  • https://localhost:8443/fedizhelloworld/secure/fedservlet
2.1) Configure the Fediz IdP to communicate with Keycloak

Now we will configure the Fediz IdP to authenticate the user in "realm B" by using the OpenId Connect protocol. Edit 'webapps/fediz-idp/WEB-INF/classes/entities-realma.xml'. In the 'idp-realmA' bean:
  • Change the port in "idpUrl" to "8443". 
In the 'trusted-idp-realmB' bean:
  • Change the "url" value to "http://localhost:8080/auth/realms/realmb/protocol/openid-connect/auth".
  • Change the "protocol" value to "openid-connect-1.0".
  • Change the "certificate" value to "keycloak.cert". 
  • Add the following parameters Map, filling in a value for the client secret extracted above: <property name="parameters">
                <util:map>
                    <entry key="client.id" value="realma-client"/>
                    <entry key="client.secret" value="<secret>"/>
                    <entry key="token.endpoint" value="http://localhost:8080/auth/realms/realmb/protocol/openid-connect/token"/>
                </util:map>
            </property>
     
2.2) Configure Fediz to use the Keycloak signing certificate

Copy 'webapps/fediz-idp/WEB-INF/classes/realmb.cert' to a new file called 'webapps/fediz-idp/WEB-INF/classes/keycloak.cert'. Edit this file + delete the content between the "-----BEGIN CERTIFICATE----- / -----END CERTIFICATE-----" tags, pasting instead the Keycloak signing certificate as retrieved in step "1.3" above.

Restart Fediz to pick up the changes (you may need to remove the persistent storage first).

3) Testing the service

To test the service navigate to:
  • https://localhost:8443/fedizhelloworld/secure/fedservlet
Select "realm B". You should be redirected to the Keycloak authentication page. Enter the user credentials you have created. You will be redirected to Fediz, where it converts the received JWT token to a token in the realm of Fediz (realm A) and redirects to the web application.

Wednesday, February 17, 2016

Apache CXF Fediz 1.2.2 released

Apache CXF Fediz 1.2.2 has been released. The issues fixed can be seen here. Highlights include:
  • The core Apache CXF dependency is updated to the recent 3.0.8 release.
  • A new HomeRealm Discovery Service based on Spring EL is available in the IdP.
  • Support for configurable token expiration validation in the plugins has been added.
  • Various fixes for the websphere container plugin have been added.
A new feature in 1.2.2 is the ability to specify a constraint in the IdP on the acceptable 'wreply' value for a given service. When the IdP successfully authenticates the end user, it will issue the WS-Federation response to the value specified in the initial request in the 'wreply' parameter. However, this could be exploited by a malicious third party to redirect the end user to a custom address, where the issued token could be retrieved. In 1.2.2, there is a new property associated with the Application in the IdP called 'passiveRequestorEndpointConstraint'. This is a regular expression on the acceptable value for the 'wreply' endpoint associated with this Application. If this property is not specified, a warning is logged in the IdP. For example:

Monday, February 15, 2016

Javascript Object Signing and Encryption (JOSE) support in Apache CXF - part IV

This is the fourth and final article in a series of posts on support for Javascript Object Signing and Encryption (JOSE) in Apache CXF. The first article covered how to sign content using JWS, while the second article showed how to encrypt content using JWE. The third article described how to construct JWT Tokens, how to sign and encrypt them, and how they can be used for authentication and authorization in Apache CXF. In this post, we will show how the CXF Security Token Service (STS) can be leveraged to issue and validate JWT Tokens.

1) The CXF STS

Apache CXF ships with a powerful and widely deployed STS implementation that has been covered extensively on this blog before. Clients interact with the STS via the SOAP WS-Trust interface, typically asking the STS to issue a (SAML) token by passing some parameters. The STS offers the following functionality with respect to tokens:
  • It can issue SAML (1.1 + 2.0) and SecurityContextTokens.
  • It can validate SAML, UsernameToken and BinarySecurityTokens.
  • It can renew SAML Tokens
  • It can cancel SecurityContextTokens.
  • It can transform tokens from one type to another, and from one realm to another.
Wouldn't it be cool if you could ask the STS to issue and validate JWT tokens as well? Well that's exactly what you can do from the new CXF 3.1.5 release! If you already have an STS instance deployed to issue SAML tokens, then you can also issue JWT tokens to different clients with some simple configuration changes to your existing deployment.

2) Issuing JWT Tokens from the STS

Let's look at the most common use-case first, that of issuing JWT tokens from the STS. The client specifies a TokenType String in the request to indicate the type of the desired token. There is no standard WS-Trust Token Type for JWT tokens as there is for SAML Tokens. The default implementation that ships with the STS uses the token type "urn:ietf:params:oauth:token-type:jwt" (see here).

The STS maintains a list of TokenProvider implementations, which it queries in turn to see whether it is capable of issuing a token of the given type. A new implementation is available to issue JWT Tokens - the JWTTokenProvider. By default tokens are signed via JWS using the STS master keystore (this is controlled via a "signToken" property of the JWTTokenProvider). The keystore configuration is exactly the same as for the SAML case. Tokens can also be encrypted via JWE if desired. Realms are also supported in the same way as for SAML Tokens.

The claims inserted into the issued token are obtained via a JWTClaimsProvider Object configured in the JWTTokenProvider. The default implementation adds the following claims:
  • The current principal is added as the Subject claim.
  • The issuer name of the STS is added as the Issuer claim.
  • Any claims that were requested by the client via the WS-Trust "Claims" parameter (that can be handled by the ClaimManager of the STS).
  • The various "time" constraints, such as Expiry, NotBefore, IssuedAt, etc.
  • Finally, it adds in the audience claim obtained from an AppliesTo address and the wst:Participants, if either were specified by the client.
The token that is generated by the JWTTokenProvider is in the form of a String. However, as the token will be included in the WS-Trust Response, the String must be "wrapped" somehow to form a valid XML Element. A TokenWrapper interface is defined to do precisely this. The default implementation simply inserts the JWT Token into the WS-Trust Response as the text content of a "TokenWrapper" Element.
3) Validating JWT Tokens in the STS

As well as issuing JWT Tokens, the STS can also validate them via the WS-Trust Validate operation. A new TokenValidator implementation is available to validate JWT tokens called the JWTTokenValidator. The signature of the token is first validated by the STS truststore. Then the time related claims of the token are checked, e.g. is the token expired or is the current time before the NotBefore time of the token, etc.

A useful feature of the WS-Trust validate operation is the ability to transform tokens from one type to another. Normally, a client just wants to know if a token is valid or not, and hence receives a "yes/no" response from the STS. However, if the client specifies a TokenType that doesn't corresponds to the standard "Status" TokenType, but instead corresponds to a different token, the STS will validate the received token and then generate a new token of the desired type using the principal associated with the validated token.

This "token transformation" functionality is also supported with the new JWT implementation. It is possible to transform a SAML Token into a JWT Token, and vice versa, something that could be quite useful in a deployment where you need to support both REST and SOAP services for example. Using a JWT Token as a WS-Trust OnBehalfOf/ActAs token is also supported.