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.

Monday, February 1, 2016

An interop demo between Apache CXF Fediz and Keycloak

Last week, I showed how to use the Apache CXF Fediz IdP as an identity broker with a real-world SAML SSO IdP based on the Shibboleth IdP (as opposed to an earlier article which used a mocked SAML SSO IdP). In this post, I will give similar instructions to configure the Fediz IdP to act as an identity broker 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: urn:org:apache:cxf:fediz:idp:realm-A
  • Client protocol: saml
  • Client SAML Endpoint: https://localhost:8443/fediz-idp/federation
Once the client is created you will see more configuration options:
  • Select "Sign Assertions"
  • Select "Force Name ID Format".
  • Valid Redirect URIs: https://localhost:8443/*
Now go to the "SAML Keys" tab of the newly created client. Here we will have to import the certificate of the Fediz IdP so that Keycloak can validate the signed SAML requests. Click "Import" and specify:
  • Archive Format: JKS
  • Key Alias: realma
  • Store password: storepass
  • Import file: stsrealm_a.jks
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 SAML Response from Keycloak. Select "Realm Settings" (for "realmb") and click on the "Keys" tab. Copy and save the value specified in the "Certificate" textfield.

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". 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 SAML SSO 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/saml".
  • Change the "protocol" value to "urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser".
  • Change the "certificate" value to "keycloak.cert".
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.

The STS also needs to trust the Keycloak signing certificate. Copy keycloak.cert into 'webapps/fediz-idp-sts/WEB-INF/classes". In this directory import the keycloak.cert into the STS truststore via:
  • keytool -keystore ststrust.jks -import -file keycloak.cert -storepass storepass -alias keycloak
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 SAML token to a token in the realm of Fediz (realm A) and redirects to the web application.