Jan Bernhardt

Subscribe to Jan Bernhardt feed
This blog focuses on technical solutions around security and application integration tasksJan Bernhardthttp://www.blogger.com/profile/04467648148765096890noreply@blogger.comBlogger23125
Updated: 2 hours 28 min ago

Basic Authentication with Talend ESB Webservice

Mon, 03/13/2017 - 17:27
In this Blog post I'll show you how to use Username/Password authentication with a Talend ESB WebService. First with a UsernameToken inside the SOAP Header and second by using BasicAuthentication.


Prepare your Test Service1.) Start Talend Studio (6.3.1)

2.) Create a new Service
2.1) Name your Service: EchoService
2.2) Select: Create new Service

3.) Import Schema

4.) Implement Service
4.1) Assign Job to operation
4.2) Select: Create a new Job and Assign it to this Service Operation
4.3) Add tXMLMap to your job
4.4) Import Schema to your input and output mapping from your repository
4.5) Define your mapping

5.) Test your Service
5.1) Run your Service inside Studio
5.2) Send a Test Request (for example with SoapUI) to your service at http://localhost:8090/services/EchoService
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://www.talend.org/service/">
<soapenv:Header/>
<soapenv:Body>
<ser:EchoServiceOperationRequest>
<in>Hello World!</in>
</ser:EchoServiceOperationRequest>
</soapenv:Body>
</soapenv:Envelope>You should get the following response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<tns:EchoServiceOperationResponse xmlns:tns="http://www.talend.org/service/">
<out>Hello World!</out>
</tns:EchoServiceOperationResponse>
</soap:Body>
</soap:Envelope>
UsernameToken Authentication1.) Active Username /Passwort ESB Runtime Option

2.) Export Service to Runtime


3.) Start Runtime

to be continued....
Basic Authenticationtbd...
Categories: Jan Bernhardt

Kerberos Debugging in Java

Mon, 02/06/2017 - 15:06
Working with Kerberos can easily cause a lot of trouble. Troubleshooting can take several hours.
In this blog I'll show you what will help you best when using Kerberos with Java for example to secure a Hadoop cluster.

When Kerberos is not working as expected it is important to understand why. Enabling Kerberos debug logging is a very valuable resource to understand what is happening.
To enable Kerberos debugging you need to set the following JVM property:
-Dsun.security.krb5.debug=true
Now read your log file very carefully. This will help you to understand what is missing.

Usually you will define your Kerberos configuration within your C:\Windows\krb5.ini or /etc/krb5.conf file. Make sure that your hostname mapping to your Kerberos realm is correct in here.
There are also a few other JVM properties that are usually not required, but can be useful to override/define your configuration at application startup:
-Djava.security.krb5.kdc=hostname.of-your.kerberos.server
-Djava.security.krb5.realm=YOUR.KERBEROS.REALM
-Djava.security.auth.login.config=file:/C:/Programme/Tomcat-IDP/conf/kerberos.jaas
Kerberos is very sensitive to DNS configuration.
Here are some more shell commands that are very helpful to test if Kerberos is working in general (outside of your Java application):
# Login with a specific keytab file
kinit -k -t /path/to/your/keytab

# List all local available tokens. After kinit there should be at least your tgt token.
klist

# Request a ticket for a specific service. Check if the service is registered correctly at your Kerberos server.
kvno service/hostname@domain
https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/kvno.html

Here is a sample configuration for your krb5.ini file:
[libdefaults]
default_realm = HORTONWORKSHA.COM
dns_lookup_kdc = false
dns_lookup_realm = false
ticket_lifetime = 86400
renew_lifetime = 604800
forwardable = true
default_tgs_enctypes = rc4-hmac
default_tkt_enctypes = rc4-hmac
permitted_enctypes = rc4-hmac
udp_preference_limit = 1
kdc_timeout = 3000

[realms]
CLOUDERAHA.COM = {
kdc = talend.cloudera57
admin_server = talend.cloudera57
}
HORTONWORKSHA.COM = {
kdc = hdp24masternode
admin_server = hdp24masternode
}

[domain_realm]
.hortonworksha.com = HORTONWORKSHA.COM
hortonworksha.com = HORTONWORKSHA.COM
.clouderaha.com = CLOUDERAHA.COM
clouderaha.com = CLOUDERAHA.COM
And here is another example for a JAAS configuration file (first for a normal user and second for a technical service account):
alice {
com.sun.security.auth.module.Krb5LoginModule required
refreshKrb5Config=true
debug=true
principal="alice";
};

sts {
com.sun.security.auth.module.Krb5LoginModule required
debug=true
refreshKrb5Config=true
useKeyTab=true
storeKey=true
keyTab="file:c:/Users/jbernhardt/workspace/kerberos/sts.keytab"
principal="sts/tal-csg01";
};
If you want to define the location for your cached tickets, set the following system property accordingly:
# Windows Style
set KRB5CCNAME="C:\Users\jbernhardt\krb5cc_jbernhardt"

# Linux Style
export KRB5CCNAME="/home/jbernhardt/krb5cc_jbernhardt"
Sample Java Code:
package test;

import java.io.IOException;
import java.net.URL;
import java.security.Principal;
import java.util.Set;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

public class JaasLoginTest {

public static void main(String argv[]) {
URL conf = JaasLoginTest.class.getClassLoader().getResource("jaas.conf");
System.setProperty("java.security.auth.login.config", conf.toString());
System.setProperty("java.security.krb5.realm", "TEST.TALEND.DE");
System.setProperty("java.security.krb5.kdc", "192.168.0.100");
System.setProperty("sun.security.krb5.debug", "true");

// Only needed when not using the ticket cache
CallbackHandler callbackHandler = new CallbackHandler() {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
((NameCallback)callback).setName("alice");
}
if (callback instanceof PasswordCallback) {
((PasswordCallback)callback).setPassword("password".toCharArray());
}
}

}
};

try {
LoginContext lc = new LoginContext("alice", callbackHandler);
// LoginContext lc = new LoginContext("sts", callbackHandler);
lc.login();
Subject subject = lc.getSubject();
Set<Principal> principals = subject.getPrincipals();
Set<Object> credentials = subject.getPrivateCredentials();
System.out.println("OK: " + principals);
System.out.println("OK: " + credentials);
} catch (LoginException e) {
e.printStackTrace();
}
}
}
Useful Links
  1. Basic Concepts for the Kerberos Protocol
  2. How the Kerberos Version 5 Authentication Protocol Works
  3. Kerberos Cross Authentication
Categories: Jan Bernhardt

How to enable Fediz Plugin Logging

Thu, 09/22/2016 - 14:43
If you are using the Apache Fediz plugin to enable WS-Federation Support for your Tomcat container, you will not see any log statements from the Fediz Plugin by default. Especially when testing or analyzing issues with the plugin you will be interested in actually seeing some log statements from the plugin.

In this blog post I'll explain to you what need to be done to get all DEBUG log level statements from the Apache Fediz Tomcat Plugin using Log4J.
Apache Tomcat tells you how to enable logging on the container level.
1. Adding DependenciesFirst you need to ensure that the required libraries are available within your classpath. This can be done in one of two ways:
a) Adding Maven Dependencies to the Fediz Tomcat PluginAdd the following dependency to cxf-fediz/plugins/tomcat7/pom.xml:
<project . . .>
. . .
<dependencies>
<dependency>
<groupid>org.slf4j</groupid>
<artifactid>slf4j-log4j12</artifactid>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
. . .
</project>
Now build the plugin again mvn clean package and deploy the content of cxf-fediz/plugins/tomcat7/target/fediz-tomcat7-1.3.0-zip-with-dependencies.zip into your tomcat/lib/fediz folder.
b) Adding lib files directly to your lib folderAdd slf4j and log4j libs (in the desired version) to your fediz plugin dependencies:

2. Adding Log4J configuration fileOnce your dependencies are added to your Tomcat installation, you need to add a log4j.properties file to your tomcat/lib folder. Here is an example content for this file:
# Loggers
log4j.rootLogger = WARN, CATALINA, CONSOLE
log4j.logger.org.apache.cxf.fediz = DEBUG, CONSOLE, FEDIZ
log4j.additivity.org.apache.cxf.fediz = false

# Appenders
log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender
log4j.appender.CATALINA.File = ${catalina.base}/logs/catalina.out
log4j.appender.CATALINA.Append = true
log4j.appender.CATALINA.Encoding = UTF-8
log4j.appender.CATALINA.DatePattern = '.'yyyy-MM-dd
log4j.appender.CATALINA.layout = org.apache.log4j.PatternLayout
log4j.appender.CATALINA.layout.ConversionPattern = %d [%t] %-5p %c %x - %m%n

log4j.appender.FEDIZ = org.apache.log4j.DailyRollingFileAppender
log4j.appender.FEDIZ.File = ${catalina.base}/logs/fediz-plugin.log
log4j.appender.FEDIZ.Append = true
log4j.appender.FEDIZ.Encoding = UTF-8
log4j.appender.FEDIZ.Threshold = DEBUG
log4j.appender.FEDIZ.DatePattern = '.'yyyy-MM-dd
log4j.appender.FEDIZ.layout = org.apache.log4j.PatternLayout
log4j.appender.FEDIZ.layout.ConversionPattern = %d [%t] %-5p %c %x - %m%n

log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Encoding = UTF-8
log4j.appender.CONSOLE.Threshold = INFO
log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern = %d [%t] %-5p %c %x - %m%n

Now restart your tomcat container and you will see Fediz Info logs on your console and Debug messages within tomcat/logs/fediz-plugin.log.
Categories: Jan Bernhardt

Syncope User Synchronisation with a Database

Thu, 09/01/2016 - 13:12
In a previous post I explained how to setup a datasource for an embedded H2 database and how to use it with the Karaf DB JAAS plugin.

In this post, I'll explain to you how to setup Syncope to synchronize users from that database into syncope. Of course you can also use any other database with a matching JDBC driver.
Install SyncopeIn this post I'll refer to the Syncope Installation which comes with the Talend 6.1.1 installer. If you need to setup Syncope manually, please take a look at some posts from Colm.
Setup DB Connection ModuleSyncope uses connid to connect to other backend systems like LDAP.
You need to download the DB connid bundle and follow the installation instructions.
  1. Open webapps/syncope/WEB-INF/classes/connid.properties and define your connid bundle location:
    Windows Style:connid.locations=file:/C:/Talend/6.1.1/apache-tomcat/webapps/syncope/WEB-INF/connid/Linux Style:connid.locations=file:/opt/Talend-6.1.1/apache-tomcat/webapps/syncope/WEB-INF/connid/
  2. Create the defined folder and copy your downloaded connid bundle (jar) into it
  3. Download and copy your required JDBC driver to your tomcat/lib folder
  4. Restart Syncope / Tomcat
  5. Login to Syncope Console: http://localhost:8080/syncope-console/
    Default-Username: admin
    Default-Password: password
Setup DB ConnectorNext you need to setup a connection to your database, before you can define any synchronization pattern.
  1. Switch to Resources -> Connectors and click Create
  2. Enter your connection name and select your connid bundle:
  3. Configure your connection settings:
    Since Syncope expects SHA1 hashes to be Uppercase you must set this checkbox, or otherwise your users will not be able to authenticate against syncope with their synchronized password.

    In Syncope 2.x and newer it will also be possible to avoid user password synchronization, but instead to do a "pass-through authentication". This will be especially helpful if your passwords are not just hashed but also salted and encrypted.
  4. Perform a connection test by clicking on the top right world icon of the configuration tab

    If you are experiencing connection problems, take a look into the  tomcat/logs/core-connid.log file for detailed information.
  5. Select all checkboxes on the capabilities tab:
  6. Save your connection
    Define DB Resource Now you can setup a new resource to define the attribute matching from syncope internal DB and external DB.
    1. Click on Resources -> Resources -> Create
    2.  Switch to user mapping tab
    3. Click Save
    Add Synchronization Task To import users from your database you need to setup a synchronization task.
    1. Click on Task ->  Synchronization Tasks -> Create
    2. Click Save
    3. Execute your new synchronization task
      If your run was successful you will see alice as a new user under Users.
      Create a new UserTo test user propagation, you must create a new user and add this user to the H2-users Resource.
      1. Click Users -> List -> Create
      2. Select Resource
      3. Save
      You will now find Bob in your H2 database.

      I was not able to do a role synchronization with my DB backend, due to missing support in the UI / connid handler.
      Categories: Jan Bernhardt

      Custom JSSE Truststore to enable XKMS Certificate Validation

      Mon, 08/29/2016 - 08:56
      Recently I was involved in a project which uses a central XKMS Server for certificate and trust management. This was all working fine within the Talend runtime with a custom wss4j crypto provider. However the need raised to perform client certificate validations (mutal SSL) with Apache Fediz running inside an Apache Tomcat server.


      Usually I would use a JKS truststore for Tomcat to add trusted certificates (CAs). However this was not possible for this project, because all certificates will be managed inside an LDAP accessible via a XKMS service. Searching for a solution to extend Tomcat to support XKMS based certificate validation I came across the JSSE Standard.

      Reading throw the documentation was not so straightforward and clear. But searching through the internet finally helped me to achieve my goal. In this blog post, I'll show you what I had to do, to enabled XKMS based SSL certificate validation in Tomcat. To manage your SSL truststore settings you can use standard System or Tomcat properties:

      System PropertiesTomcat PropertiesPurposejavax.net.ssl.trustStoretruststoreFileSystem location for JKS truststore filejavax.net.ssl.trustStorePasswordtruststorePassPassword for JKS truststorejavax.net.ssl.trustStoreProvidertruststoreProviderProvider for a truststoreFactory implementationjavax.net.ssl.trustStoreTypetruststoreTypeType of your truststore. Default is "JKS"n/atrustManagerClassNameCustom trust manager class to use to validate client certificates
      Settings are considered in the following order:
      1. Tomcat truststore properties
      2. System Properties
      3. Tomcat keystore properties
      4. Default Values
      If a trustManagerClassName is set, this implementation will be used and all other truststore settings will be ignored. If a truststore provider is defined any Java standard provider will be ignored.

      You can review this behavior in the Tomcat JSSESocketFactory init method.

      The easiest way to achieve my goal was to implement my own XKMSTrustManager implementing the javax.net.ssl.X509TrustManager interface.
      public class XKMSTrustManager implements X509TrustManager {

      private static final Logger LOG = LoggerFactory.getLogger(XKMSTrustManager.class);

      private XKMSInvoker xkms;

      public XKMSTrustManager() throws MalformedURLException {
      XKMSService xkmsService = new XKMSService(
      URI.create(System.getProperty("xkms.wsdl.location", "http://localhost:8040/services/XKMS/?wsdl"))
      .toURL());
      xkms = new XKMSInvoker(xkmsService.getXKMSPort());
      }

      @Override
      public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
      LOG.debug("Check client trust for: {}", chain);
      validateTrust(chain);
      }

      @Override
      public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
      LOG.debug("Check server trust for: {}", chain);
      validateTrust(chain);
      }

      @Override
      public X509Certificate[] getAcceptedIssuers() {
      return new X509Certificate[] {};
      }

      protected void validateTrust(X509Certificate[] chain) throws CertificateException {
      if (chain == null) {
      throw new CertificateException("Certificate chain is null");
      }

      if (!xkms.validateCertificate(chain)) {
      LOG.error("Certificate chain is not trusted: {}", chain);
      throw new CertificateException("Certificate chain is not trusted");
      }
      }
      }
      Tomcat\conf\server.xml
      <Server port="9005" shutdown="SHUTDOWN">

        <Service name="Catalina">

          <Connector port="9443" protocol="org.apache.coyote.http11.Http11Protocol"
                     maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
                     keystoreFile="idp-ssl-key.jks"
                     keystorePass="tompass"
                     trustManagerClassName="org.talend.ws.security.jsse.XKMSTrustManager"
                     clientAuth="true"
                     sslProtocol="TLS" />

        </Service>

      </Server>


      However setting a trustManager is only possible if this option is provided by your application or if you have access to the source code of the SSL SocketFactory. In all other cases you will have to implement your own Security Provider providing your own truststore factory. This task is much more challenging. During my internet research for this topic I found several pages, which should be a good reference for you, if you have to go this way:

      JCA Reference Guide - Crypto Provider

      Howto Implement a JCA Provider

      JSSE Reference Guide - Customized Certificate Storage

      Custom CA Truststore in addition to System CA Truststore
      HowTo Register global security provider
      <java-home>/lib/security/java.security
      security.provider.2=sun.security.provider.Sun
      security.provider.3=sun.security.rsa.SunRsaSign
      security.provider.4=sun.security.provider.SunJCE Advantage: Multiple providers. Adding just the "missing piece".
      Disadvantage: System wide configuration

      Override security provider settings with system properties
      Changing Security Settings via CodeSecurity.insertProviderAt(new FooBarProvider(), 1);
      Register a TrustManagerput("TrustManagerFactory.SunX509", "sun.security.ssl.TrustManagerFactoryImpl$SimpleFactory");
      put("TrustManagerFactory.PKIX", "sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory");Using a Custom Certificate Trust Store
      Sun JSSE Provider Implementation
      Categories: Jan Bernhardt

      Apache Fediz with Client Certificate Authentication (X.509)

      Thu, 08/04/2016 - 12:25
      In this blog post I will explain how to generate your own SSL key-pair to perform certificate based authentication for SSO purposes with Apache Fediz IDP.
      Client Key AuthenticationGenerate Key-PairI like to use the keystore-explorer under windows, because it makes certificate management very easy. You don't have to lookup console commands but instead you get nice Wizards to get it all done. If you are running linux I can recommend this page to you, because it contains the most common Java Keytool commands you will need.

      After starting keystore-explorer create a new keykeystore (PKCS #12). Next click generate keypair. RSA with 2.048 bit should be fine. Now you should enter your name and after that click on extensions to define an "Extended Key Usage" for "TLS Web Client Authentication":



      Make sure that this extension flag is really set for your key-pair. I first tried without this extension and I could not get any of my browsers to even show me a certificate selection popup when authentication against the IDP.
      Since you will have to import your personal certificate to the IDP truststore later on, I would recommend to you to export your public certificate at this step:



      Import Key-Pair to your BrowserOnce your key generation was successful, you need to add this key-pair to your browser:

      In Chrome you need to open your settings -> extended settings ->  HTTPS/SSL -> Manage Certificates -> Import select your p12 certificate and make sure that all extensions from the certificate are included:


      Since chrome and IE will use the same certificate store. So there is no need to do this twice if you have done this once for one of the two.

      For Firefox you need to go to Options -> Advanced -> Certificates -> View Certificates -> Your Certificates -> Import


      I had to restart my machine before my browsers would show me the option to select my certificates for client authentication. Some articles in the internet also recommended to add the IDP URL to your list of trusted sides in the Internet Explorer.Setup Fediz IDPYou can find a full IDP / Web-App setup instruction in one of my previous articles. In this article I will only highlight steps that are related to SSL slient authentication.

      Add SSL support to your tomcat conf/server.xml
      <Connector port="9443" protocol="org.apache.coyote.http11.Http11Protocol"
      maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
      keystoreFile="idp-ssl-key.jks"
      keystorePass="tompass"
      truststoreFile="idp-ssl-trust.jks"
      truststorePass="ispass"
      truststoreType="JKS"
      clientAuth="want"
      sslProtocol="TLS" />
      If you want all clients to authenticate with a client SSL Certificate against your IDP you must set the clientAuth attribute to "true" instead of "want". However if you want to support multiple authentication styles even without a client certificate you should set clientAuth to "want".

      Open your idp-ssl-trust.jks with your keystore-explorer to import your personal certificate from your desktop (see previous export step above).
      Validate SetupOpen your browser to the Fediz Hello World page: https://localhost:9443/fediz-idp/. Your browser should show you a selection popup for your client certificate:


      If you imported this certificate correctly to your tomcat IDP truststore you should now see a "Hello World!" welcome page from Fediz.

      Please also take a look at colms blog about this topic.
      Categories: Jan Bernhardt

      Karaf JDBC JAAS Module

      Wed, 07/20/2016 - 17:58
      Karaf relys on JAAS for user authentication. JAAS makes it possible to plugin multiple modules for this purpose. By default Karaf will use the karaf realm with a JAAS module getting its user and role information from a property file: runtime/etc/users.properties

      In this blog post I will show you how to use the Karaf JAAS console commands and how to setup a JDBC module to authenticate against a database.

      All code was tested on Karaf version 4.0.3 JDBC SetupRegister DatasourceAt first you need to install the Karaf JDBC feature:
      karaf@trun()> feature:install jdbc
      karaf@trun()> feature:install pax-jdbc-derby
      Next you can create a new Datasource:
      karaf@root()> jdbc:ds-create -dn derby -url "jdbc:derby:users;create=true" -u db_admin usersWith the -dn derby option you define a datasource of type derby. Alternative you could also use generic, oracle, mysql, postgres, h2, hsql as your datasource type. Please make sure to install also the matching jdbc pax feature for your datasource type.
      The -u db_admin option defines the datasource username. Finally jaas_realm is the datasource name.
      Add sample data jdbc:execute users CREATE TABLE users ( username VARCHAR(255) PRIMARY KEY NOT NULL, password VARCHAR(255) NOT NULL );
      jdbc:execute users CREATE TABLE roles ( username VARCHAR(255) NOT NULL, role VARCHAR(255) NOT NULL, PRIMARY KEY (username,role) );
      jdbc:execute users INSERT INTO users values('alice','e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4');
      jdbc:execute users INSERT INTO roles values('alice','manager');Validate your input:
      karaf@trun()> jdbc:query users SELECT * FROM roles
      ROLE | USERNAME
      ------------------
      manager | aliceJAAS Console CommandsKaraf provides some nice console commands to manage your JAAS realms.
      List JAAS realms with assigned moduleskaraf@trun()> jaas:realm-list
      Index | Realm Name | Login Module Class Name
      -----------------------------------------------------------------------------------
      1     | karaf      | org.apache.karaf.jaas.modules.properties.PropertiesLoginModule
      2     | karaf      | org.apache.karaf.jaas.modules.publickey.PublickeyLoginModule
      3     | karaf      | org.apache.karaf.jaas.modules.audit.FileAuditLoginModule
      4     | karaf      | org.apache.karaf.jaas.modules.audit.EventAdminAuditLoginModuleList users and assigned roleskaraf@trun()> jaas:realm-manage --realm karaf

      karaf@trun()> jaas:user-list
      User Name | Group      | Role
      --------------------------------------
      tadmin    | admingroup | admin
      tadmin    | admingroup | manager
      tadmin    | admingroup | viewer
      tadmin    | admingroup | systembundles
      tadmin    |            | sl_admin
      tesb      | admingroup | admin
      tesb      | admingroup | manager
      tesb      | admingroup | viewer
      tesb      | admingroup | systembundles
      tesb      |            | sl_maintain
      karaf     | admingroup | admin
      karaf     | admingroup | manager
      karaf     | admingroup | viewer
      karaf     | admingroup | systembundles
      List userskaraf@trun()> jaas:realm-manage --realm karaf

      karaf@trun()> jaas:user-list
      User Name | Group      | Role
      --------------------------------------
      tadmin    | admingroup | admin
      tadmin    | admingroup | manager
      tadmin    | admingroup | viewer
      tadmin    | admingroup | systembundles
      tadmin    |            | sl_admin
      tesb      | admingroup | admin
      tesb      | admingroup | manager
      tesb      | admingroup | viewer
      tesb      | admingroup | systembundles
      tesb      |            | sl_maintain
      karaf     | admingroup | admin
      karaf     | admingroup | manager
      karaf     | admingroup | viewer
      karaf     | admingroup | systembundles

      karaf@trun()> jaas:cancelAdding a userkaraf@trun()> jaas:realm-manage --realm karaf
      karaf@trun()> jaas:user-add alice secret
      karaf@trun()> jaas:update
      If you execute "List users" again you will see alice added to the realm. You will also find alice added to the users.properties file.
      Install JDBC JAAS ModuleRegister ModuleCreate a file db_jaas.xml within the deploy folder of your karaf installation:
      <?xml version="1.0" encoding="UTF-8"?>
      <!--
      Licensed to the Apache Software Foundation (ASF) under one or more
      contributor license agreements. See the NOTICE file distributed with
      this work for additional information regarding copyright ownership.
      The ASF licenses this file to You under the Apache License, Version 2.0
      (the "License"); you may not use this file except in compliance with
      the License. You may obtain a copy of the License at
      http://www.apache.org/licenses/LICENSE-2.0
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
      -->
      <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
      xmlns:jaas="http://karaf.apache.org/xmlns/jaas/v1.0.0"
      xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
      xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">

      <!-- Allow usage of System properties, especially the karaf.base property -->
      <ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]"/>

      <!-- AdminConfig property place holder for the org.apache.karaf.jaas -->
      <cm:property-placeholder persistent-id="org.apache.karaf.jaas.db" update-strategy="reload">
      <cm:default-properties>
      <cm:property name="encryption.name" value="basic"/>
      <cm:property name="encryption.enabled" value="true"/>
      <!--cm:property name="encryption.prefix" value="{CRYPT}"/>
      <cm:property name="encryption.suffix" value="{CRYPT}"/-->
      <cm:property name="encryption.algorithm" value="SHA1"/>
      <cm:property name="encryption.encoding" value="hexadecimal"/>
      <cm:property name="detailed.login.exception" value="true"/>
      <cm:property name="audit.file.enabled" value="true"/>
      <cm:property name="audit.file.file" value="$[karaf.data]/security/audit.log"/>
      <cm:property name="audit.eventadmin.enabled" value="true"/>
      <cm:property name="audit.eventadmin.topic" value="org/apache/karaf/login"/>
      </cm:default-properties>
      </cm:property-placeholder>

      <jaas:config name="karaf" rank="10">

      <jaas:module className="org.apache.karaf.jaas.modules.jdbc.JDBCLoginModule" flags="required">
      datasource = osgi:javax.sql.DataSource/(osgi.jndi.service.name=users)
      query.password = SELECT PASSWORD FROM USERS WHERE USERNAME=?
      query.role = SELECT ROLE FROM ROLES WHERE USERNAME=?
      insert.user = INSERT INTO USERS VALUES(?,?)
      insert.role = INSERT INTO ROLES VALUES(?,?)
      delete.user = DELETE FROM USERS WHERE USERNAME=?
      delete.role = DELETE FROM ROLES WHERE USERNAME=? AND ROLE=?
      delete.roles = DELETE FROM ROLES WHERE USERNAME=?
      encryption.enabled = ${encryption.enabled}
      encryption.name = ${encryption.name}
      encryption.algorithm = ${encryption.algorithm}
      encryption.encoding = ${encryption.encoding}
      detailed.login.exception = ${detailed.login.exception}
      </jaas:module>
      <jaas:module className="org.apache.karaf.jaas.modules.audit.FileAuditLoginModule" flags="optional">
      enabled = ${audit.file.enabled}
      file = ${audit.file.file}
      </jaas:module>
      <jaas:module className="org.apache.karaf.jaas.modules.audit.EventAdminAuditLoginModule" flags="optional">
      enabled = ${audit.eventadmin.enabled}
      topic = ${audit.eventadmin.topic}
      </jaas:module>
      </jaas:config>

      </blueprint>By adding a configuration file org.apache.karaf.jaas.db.cfg to your etc folder you will be able to update the configuration of your jaas bundle during runtime.
      encryption.enabled = true
      encryption.name = basic
      encryption.algorithm = SHA1
      encryption.encoding = hexadecimal
      detailed.login.exception = trueNow you can login to Karaf via SSH with you alice DB user.
      ssh -p 8101 alice@localhostPassword will be a: secret
      Categories: Jan Bernhardt

      Apache Fediz installation in a productive environment

      Fri, 02/05/2016 - 21:19
      In this article I'll explain to you what to do and what to be aware of, when you want to user Fediz IDP in production.

      Basically you need to change all default passwords and certificates.

      If you will use Tomcat as user Servlet container I'll also give you some tips how to secure tomcat best, so that an attacker will have a hard time breaking into your system.

      IDP ChangesRemove Filesrm -f services\idp\src\main\resources\entities-realmb.xml
      rm -f services\idp\src\main\resources\mystskey.cer
      rm -f services\idp\src\main\resources\realm.properties
      rm -f services\idp\src\main\resources\realma.cert
      rm -f services\idp\src\main\resources\realmb.cert
      rm -f services\idp\src\main\resources\stsKeystoreB.properties
      rm -f services\idp\src\main\resources\stsrealm_a.jks
      rm -f services\idp\src\main\resources\stsrealm_b.jks
      rm -f services\idp\src\main\webapp\WEB-INF\idp-config-realma.xml
      rm -f services\idp\src\main\webapp\WEB-INF\idp-config-realmb.xml
      Rename Filesmv services\idp\src\main\resources\entities-realmA.xml services\idp\src\main\resources\entities-realm-myCompany.xml
      mv services\idp\src\main\resources\stsKeystoreA.properties services\idp\src\main\resources\stsKeystoreMyCompany.properties
      Modify Files
      • Change fediz-idp to idp as finalName in services\idp\pom.xml. This will hide your used IDP product within your URL and will make it easier if you will ever want to change to a different product.
      • Apply the following changes to your entities-realm-myCompany.xm
        • Rename all realmA settings to realmYourCompany
        • Change your realm identifier 
        • Use http:// or urn: at the beginning of your realm identifier to ensure interoperability with Microsoft ADFS
        • Change Keystore settings, especially the certificatePassword
        • Update stsUrl and idpUrl to reflect your installation
        • Remove fedizhelloworld from your applications
        • Remove oidc application if not used, or update passiveRequestorEndpointConstraint if oidc will be used
        • Remove or update all trustedIdps
      • Regenerate your own IDP SSL keys for services\idp\src\main\resources\idp-ssl-key.jks and store new certificate in services\idp\src\main\resources\idp-ssl-trust.jks. Remove all other certificates in idp-ssl-trust.jks
      • Update settings for your database in services\idp\src\main\resources\persistence.properties
      • Change passwords in services\idp\src\main\resources\stsKeystoreMyCompany.properties
      • Change usernames and passwords in services\idp\src\main\resources\users.properties. Use Bcrypt passwords instead of plaintext passwords.
      • Update wsdlLocation to reflect your STS URL in services\idp\src\main\webapp\WEB-INF\idp-servlet.xml
      • Ensure correct realm value within your services\idp\src\main\webapp\WEB-INF\web.xml
      • Apply the following changes within services\idp\src\main\webapp\WEB-INF\security-config.xml
        • Change realm identifier in federationEntryPoint
        • Enable bCryptPasswordEncoder
        • Enable form-login if desired and provide custom login screen in services/idp/src/main/webapp/WEB-INF/views/signinform.jsp
        • Remove all authentication alternatives, which you don't need
        • Change username and password in securityProperties if you need certificate based authentication
        • Remove all stsUPPortFilter settings, because they are only usefull for demo setups when your STS runs within the same Tomcat as your IDP.
        • Update all wsdlLocation to match your STS URL
      Create Files
      • Provide your own keystore at services\idp\src\main\resources\stsrealm_myCompany.jks. This one should be the same as the one used later for the STS.
      STS Changes Remove Filesrm -f services\sts\src\main\resources\stsrealm_a.jks
      rm -f services\sts\src\main\resources\stsrealm_b.jks
      rm -f services\sts\src\main\resources\realma.cert
      rm -f services\sts\src\main\resources\realmb.cert
      rm -f services\sts\src\main\webapp\WEB-INF\file.xml
      rm -f services\sts\src\main\webapp\WEB-INF\passwords.xml
      rm -f services\sts\src\main\webapp\WEB-INF\userClaims.xml
      Rename Filesmv services\sts\src\main\resources\stsKeystoreA.properties services\sts\src\main\resources\stsKeystore.propertiesModify Files
      • Add dependency to services\sts\pom.xml (only needed if you want to use JEXL for claim mappings)
      <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-jexl</artifactId>
          <version>2.1.1</version>
          <scope>runtime</scope>
      </dependency>
      • Change private key password in keystore and in Callbackhandler: services\sts\src\main\java\org\apache\cxf\fediz\service\sts\PasswordCallbackHandler.java 
      • Replace  with your own keystore.properties file.
      • Change passwords in services\sts\src\main\resources\stsTruststore.properties 
      • Change log level in services\sts\src\main\resources\log4j.properties 
      • Remove all certificates in services\sts\src\main\resources\ststrust.jks and add your own.
      • Change user accounts in services\sts\src\main\webapp\WEB-INF\passwords.xml 
      • Do the following changes within: services\sts\src\main\webapp\WEB-INF\cxf-transport.xml
        • Import file with user realm configuration, like ldap.xml
        • Change Relationship settings
        • Add Claim Hanlder (if needed)
        • Rename all realmA in text to realmYourCompany
        • Remove all realmB settings / beans / endpoints
      Create Files
      • Add ClaimMapping Scripts (if needed)
        services\sts\src\main\resources\claimMapping-trusted-realm.script 
      • Add you own keystore services\sts\src\main\resources\stsrealm_myCompany.jks
      Tomcat InstallationTomcat Home 
      Only download and install Tomcat manually, if your distribution does not provide a tomcat installation. System based installation is usually better, because you will receive Tomcat (security) updates automatically with your other system updates!
      1. Download latest Tomcat Version:
      https://tomcat.apache.org/download-70.cgi

      2. Extract Tomcat to /usr/share/

      3. Create a symbolic link pointing to your latest tomcat download:
      ln -s /usr/share/apache-tomcat-7.0.67 /usr/share/tomcat
      Using of symbolic links will make it easier to switch to newer versions later on.
      4. Restrict tomcat installation
      # Create tomcat group
      groupadd tomcat

      # Set ownership of all files to root and provide tomcat access via group ownership
      chown -R root:tomcat /usr/share/tomcat/

      # Remove redundant files and folders
      rm -f /usr/share/tomcat/bin/*.bat
      rm -rf /usr/share/tomcat/temp
      rm -rf /usr/share/tomcat/work
      rm -rf /usr/share/tomcat/logs
      rm -rf /usr/share/tomcat/webapps

      # Make all normal files readonly
      find /usr/share/tomcat/ -type f -exec chmod 640 {} +
      chmod 750 /usr/share/tomcat/bin/*.sh

      # Allow tomcat to access all tomcat folders
      find /usr/share/tomcat/ -type d -exec chmod 770 {} +
      IDP Tomcat SetupSetup a tomcat base environment for your IDP
      # Create folders
      mkdir /usr/share/tomcat-idp
      mkdir /usr/share/tomcat-idp/conf
      mkdir /usr/share/tomcat-idp/logs
      mkdir /usr/share/tomcat-idp/temp
      mkdir /usr/share/tomcat-idp/webapps
      mkdir /usr/share/tomcat-idp/work

      # Copy conf files
      cp /usr/share/tomcat/conf/* /usr/share/tomcat-idp/conf/

      # Copy your war file to webapps
      cp ~/idp.war /usr/share/tomcat-idp/webapps/
      Create a system startup script /etc/init.d/tomcat-idp file, with the following content:

      #!/bin/bash
      #
      # tomcat7 This shell script takes care of starting and stopping Tomcat-IDP
      # Forked from: https://gist.github.com/valotas/1000094
      #
      # chkconfig: - 80 20
      #
      ### BEGIN INIT INFO
      # Provides: tomcat-idp
      # Required-Start: $network $syslog
      # Required-Stop: $network $syslog
      # Default-Start:
      # Default-Stop:
      # Description: Release implementation for Servlet 2.5 and JSP 2.1
      # Short-Description: start and stop tomcat-idp
      ### END INIT INFO

      ## Source function library.
      #. /etc/rc.d/init.d/functions
      export CATALINA_HOME=/usr/share/tomcat
      export CATALINA_BASE=/usr/share/tomcat-idp
      export JAVA_HOME=/usr/java/default
      export JAVA_OPTS="-Dfile.encoding=UTF-8 \
      -Djava.net.preferIPv4Stack=true \
      -Djava.net.preferIPv4Addresses=true \
      -Dnet.sf.ehcache.skipUpdateCheck=true \
      -XX:+DoEscapeAnalysis \
      -XX:+UseConcMarkSweepGC \
      -XX:+CMSClassUnloadingEnabled \
      -XX:+UseParNewGC \
      -XX:MaxPermSize=128m \
      -Xms512m -Xmx512m"
      export PATH=$JAVA_HOME/bin:$PATH
      SHUTDOWN_WAIT=20
      USER=tomcat-idp
      tomcat_pid() {
      echo `ps aux | grep org.apache.catalina.startup.Bootstrap | grep -v grep | awk '{ print $2 }'`
      }

      start() {
      pid=$(tomcat_pid)
      if [ -n "$pid" ]
      then
      echo "Tomcat is already running (pid: $pid)"
      else
      # Start tomcat
      echo "Starting $USER"
      ulimit -n 100000
      umask 007
      /bin/su -p -s /bin/sh $USER $CATALINA_HOME/bin/startup.sh
      fi


      return 0
      }

      stop() {
      pid=$(tomcat_pid)
      if [ -n "$pid" ]
      then
      echo "Stoping $USER"
      /bin/su -p -s /bin/sh $USER $CATALINA_HOME/bin/shutdown.sh

      let kwait=$SHUTDOWN_WAIT
      count=0;
      until [ `ps -p $pid | grep -c $pid` = '0' ] || [ $count -gt $kwait ]
      do
      echo -n -e "\nwaiting for processes to exit";
      sleep 1
      let count=$count+1;
      done

      if [ $count -gt $kwait ]; then
      echo -n -e "\nkilling processes which didn't stop after $SHUTDOWN_WAIT seconds"
      kill -9 $pid
      fi
      else
      echo "$USER is not running"
      fi

      return 0
      }

      case $1 in
      start)
      start
      ;;
      stop)
      stop
      ;;
      restart)
      stop
      start
      ;;
      status)
      pid=$(tomcat_pid)
      if [ -n "$pid" ]
      then
      echo "$USER is running with pid: $pid"
      else
      echo "$USER is not running"
      fi
      ;;
      esac
      exit 0
      Register Tomcat for autostart:
      chkconfig tomcat-idp onStart, Wait and Stop Tomcat
      /etc/init.d/tomcat-idp start
      tail -f /usr/share/tomcat-idp/logs/catalina.out
      /etc/init.d/tomcat-idp stop
      Your idp.war file should now be extracted.
      Copy the idp keystore to your tomcat-idp root folder

      cp /usr/share/tomcat-idp/webapps/idp/WEB-INF/classes/idp-ssl-key.jks /usr/share/tomcat-idp/
      Adjust settings of /usr/share/tomcat-idp/server.xml
      • Remove all out-commented blockes to improve readability.
      • Change shutdown password to something more complex:

      <server port="8005" shutdown="ComPlexWord">
      • Enable SSL Support
      <connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol">
                     maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
                     keystoreFile="idp-ssl-key.jks"
                     keystorePass="complexPassword"
                     sslProtocol="TLS" />
      • Disable autoDeploy

      <Host appbase="webapps" name="localhost"
                  unpackWARs="true" autoDeploy="false">
      . . .
      </host>
      Update file permissions
      # Create tomcat-idp user
      useradd -d /usr/share/tomcat-idp tomcat-idp
      useradd -G tomcat tomcat-idp

      # Set ownership of all files to root and provide tomcat-idp access via group ownership
      chown -R root:tomcat-idp /usr/share/tomcat-idp/

      # Make all normal files readonly
      find /usr/share/tomcat-idp/ -type f -exec chmod 640 {} +

      # Allow tomcat-idp to change all files in temp and work
      find /usr/share/tomcat-idp/temp/ -type f -exec chmod 660 {} + 
      find /usr/share/tomcat-idp/work/ -type f -exec chmod 660 {} +

      # Allow tomcat-idp to access all tomcat folders
      find /usr/share/tomcat-idp/ -type d -exec chmod 770 {} +

      # Log files can only be appended by tomcat-idp but not read
      chmod 730 /usr/share/tomcat-idp/logs

      # Tomcat-IDP will not be able to deploy further applications by its own
      chmod 750 /usr/share/tomcat-idp/webapps
      Start Tomcat-IDP again and check if startup was successful
      /etc/init.d/tomcat-idp start
      tail -f /usr/share/tomcat-idp/logs/catalina.out
      STS Tomcat Setup
      It can be recommended to install the STS on a different / dedicated server. In this blog post I will assume that you install IDP and STS on the same machine and therefore need to change port configuration for your tomcat instance.
      The STS Tomcat setup is almost the same as for the IDP.
      Setup a tomcat base environment for your STS
      # Create folders
      mkdir /usr/share/tomcat-sts
      mkdir /usr/share/tomcat-sts/conf
      mkdir /usr/share/tomcat-sts/logs
      mkdir /usr/share/tomcat-sts/temp
      mkdir /usr/share/tomcat-sts/webapps
      mkdir /usr/share/tomcat-sts/work

      # Copy conf files
      cp /usr/share/tomcat/conf/* /usr/share/tomcat-sts/conf/

      # Copy your war file to webapps
      cp ~/sts.war /usr/share/tomcat-sts/webapps/Create a system startup script /etc/init.d/tomcat-sts file, with the following content:

      #!/bin/bash
      #
      # tomcat7 This shell script takes care of starting and stopping Tomcat-STS
      # Forked from: https://gist.github.com/valotas/1000094
      #
      # chkconfig: - 80 20
      #
      ### BEGIN INIT INFO
      # Provides: tomcat-sts
      # Required-Start: $network $syslog
      # Required-Stop: $network $syslog
      # Default-Start:
      # Default-Stop:
      # Description: Release implementation for Servlet 2.5 and JSP 2.1
      # Short-Description: start and stop tomcat-sts
      ### END INIT INFO

      ## Source function library.
      #. /etc/rc.d/init.d/functions
      export CATALINA_HOME=/usr/share/tomcat
      export CATALINA_BASE=/usr/share/tomcat-sts
      export JAVA_HOME=/usr/java/default
      export JAVA_OPTS="-Dfile.encoding=UTF-8 \
      -Djava.net.preferIPv4Stack=true \
      -Djava.net.preferIPv4Addresses=true \
      -Dnet.sf.ehcache.skipUpdateCheck=true \
      -XX:+DoEscapeAnalysis \
      -XX:+UseConcMarkSweepGC \
      -XX:+CMSClassUnloadingEnabled \
      -XX:+UseParNewGC \
      -XX:MaxPermSize=128m \
      -Xms512m -Xmx512m"
      export PATH=$JAVA_HOME/bin:$PATH
      SHUTDOWN_WAIT=20
      USER=tomcat-sts

      tomcat_pid() {
      echo `ps aux | grep org.apache.catalina.startup.Bootstrap | grep -v grep | awk '{ print $2 }'`
      }

      start() {
      pid=$(tomcat_pid)
      if [ -n "$pid" ]
      then
      echo "Tomcat is already running (pid: $pid)"
      else
      # Start tomcat
      echo "Starting $USER"
      ulimit -n 100000
      umask 007
      /bin/su -p -s /bin/sh $USER $CATALINA_HOME/bin/startup.sh
      fi


      return 0
      }

      stop() {
      pid=$(tomcat_pid)
      if [ -n "$pid" ]
      then
      echo "Stoping $USER"
      /bin/su -p -s /bin/sh $USER $CATALINA_HOME/bin/shutdown.sh

      let kwait=$SHUTDOWN_WAIT
      count=0;
      until [ `ps -p $pid | grep -c $pid` = '0' ] || [ $count -gt $kwait ]
      do
      echo -n -e "\nwaiting for processes to exit";
      sleep 1
      let count=$count+1;
      done

      if [ $count -gt $kwait ]; then
      echo -n -e "\nkilling processes which didn't stop after $SHUTDOWN_WAIT seconds"
      kill -9 $pid
      fi
      else
      echo "$USER is not running"
      fi

      return 0
      }

      case $1 in
      start)
      start
      ;;
      stop)
      stop
      ;;
      restart)
      stop
      start
      ;;
      status)
      pid=$(tomcat_pid)
      if [ -n "$pid" ]
      then
      echo "$USER is running with pid: $pid"
      else
      echo "$USER is not running"
      fi
      ;;
      esac
      exit 0
      Register Tomcat for autostart:
      chkconfig tomcat-sts onStart, Wait and Stop Tomcat
      /etc/init.d/tomcat-sts start
      tail -f /usr/share/tomcat-sts/logs/catalina.out
      /etc/init.d/tomcat-sts stopYour sts.war file should now be extracted.
      Copy the sts keystore to your tomcat-sts root folder:
      cp /usr/share/tomcat-sts/webapps/sts/WEB-INF/classes/idp-ssl-key.jks /usr/share/tomcat-sts/
      cp /usr/share/tomcat-sts/webapps/sts/WEB-INF/classes/idp-ssl-trust.jks /usr/share/tomcat-sts/Adjust settings of /usr/share/tomcat-sts/server.xml
      • Remove all out-commented blockes to improve readability.
      • Change shutdown password to something more complex:

      <server port="9005" shutdown="ComPlexWord">
      • Enable SSL Support
      <connector port="9443" protocol="org.apache.coyote.http11.Http11Protocol"
                     maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
                     keystoreFile="idp-ssl-key.jks"
                     keystorePass="complexpassword"
                     truststoreFile="idp-ssl-trust.jks"
                     truststorePass="anotherComplexWord"
                     truststoreType="JKS"
                     clientAuth="want"
                     sslProtocol="TLS" />
      • Disable autoDeploy
      <host appbase="webapps" name="localhost"
                  unpackWARs="true" autoDeploy="false">
      . . .
      </host>Update file permissions
      # Create tomcat-sts user
      useradd -d /usr/share/tomcat-sts tomcat-sts
      useradd -G tomcat tomcat-sts

      # Set ownership of all files to root and provide tomcat-sts access via group ownership
      chown -R root:tomcat-sts /usr/share/tomcat-sts/

      # Make all normal files readonly
      find /usr/share/tomcat-sts/ -type f -exec chmod 640 {} +

      # Allow tomcat-sts to change all files in temp and work
      find /usr/share/tomcat-sts/temp/ -type f -exec chmod 660 {} +
      find /usr/share/tomcat-sts/work/ -type f -exec chmod 660 {} +

      # Allow tomcat-sts to access all tomcat folders
      find /usr/share/tomcat-sts/ -type d -exec chmod 770 {} +

      # Log files can only be appended by tomcat-sts but not read
      chmod 730 /usr/share/tomcat-sts/logs

      # Tomcat-STS will not be able to deploy further applications by its own
      chmod 750 /usr/share/tomcat-sts/webapps
      Start Tomcat-STS again and check if startup was successful
      /etc/init.d/tomcat-sts start
      tail -f /usr/share/tomcat-sts/logs/catalina.out
      Categories: Jan Bernhardt

      Understanding Spring Web-Flow in Apache Fediz - Part 2

      Sat, 01/16/2016 - 15:46
      After explaining in Part 1 of this topic how the Spring Web-Flow will be initiated I'm going to review the actual flow in some more detail in this post.

      The signin request flow can be customized within the WEB-INF/flows/federation-signin-request.xml file. The standard flow looks like this:


      The bold line shows a normal login flow, when the user was not authenticated earlier and no 3rd party IDP is involved.
      TO BE CONTINUED...
      Categories: Jan Bernhardt

      Understanding Spring Web-Flow in Apache Fediz - Part 1

      Thu, 01/07/2016 - 16:20
      When I started to work with Apache Fediz, most of the actions looked like magic to me, because I was not able to understand how Spring Security and Spring Web Flow have been used in Apache Fediz. After several hours of learning and investigation I finally understood how all this works together.

      In this post I would like to share with you from what I understood of how Fediz works internally.
      When you take a look inside the WEB-INF/web.xml you will find three URL mappings:
      <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
        <servlet-name>idp</servlet-name>
        <url-pattern>/federation</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
      <servlet-name>metadata</servlet-name>
      <url-pattern>/FederationMetadata/2007-06/FederationMetadata.xml</url-pattern>
      </servlet-mapping>
      • /services/* provides access to all REST services for updating the IDP configuration
      • /FederationMetadata/2007-06/FederationMetadata.xml provides access to the generated IDP metadata document which is usually used in application wizards to setup the correct IDP configuration
      • /federation provides access to the configured Spring Web-Flow

      If you take a look inside the WEB-INF/web.xml you will see that the /federation URL is linked with org.springframework.web.servlet.DispatcherServlet. The initialization of the Spring Flow however takes place within the WEB-INF/idp-servlet.xml.
      So how is the DispatcherServlet linked to the idp-servlet.xml?
      The key to the answer is Spring MVC which uses Convention over Configuration (CoC). Spring will search for a file with the same name as the servlet-name and ending with "-servlet.xml". To apply this knowledge you can see that the /federation URL is mapped to the servlet name idp and thus the default configuration spring file which will be loaded from the DispatcherServlet is idp-servlet.xml
      Spring ConfigurationWithin the idp-servlet.xml you will find a spring configuration to setup the Spring Web Flow.
      First Spring will do a component scan to instantiate and autowire all beans with a @Component annotation located within the beans package. These beans usually provide specific actions which are executed within the flows.
      <context:component-scan base-package="org.apache.cxf.fediz.service.idp.beans" />
      Now the JSP views and HTML resources like images will be made available for the spring web flow:
      <mvc:resources mapping="/images/**" location="/resources/images/" />
      <mvc:view-controller path="/" view-name="index" />
      <mvc:view-controller path="/federation/up/login" view-name="signinform" />

      <bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/WEB-INF/views/" />
      <property name="suffix" value=".jsp" />
      </bean>

      <bean id="viewFactoryCreator"
      class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
      <property name="viewResolvers">
      <list>
      <ref local="viewResolver" />
      </list>
      </property>
      </bean>

      <bean id="expressionParser"
      class="org.springframework.webflow.expression.WebFlowOgnlExpressionParser" />

      <webflow:flow-builder-services id="builder"
      view-factory-creator="viewFactoryCreator" expression-parser="expressionParser" />
      At next the actual spring web flows will get registered. All beans setup within this spring config will be available within the web flows.
      <webflow:flow-registry id="flowRegistry"
          flow-builder-services="builder">
          <webflow:flow-location
              path="/WEB-INF/flows/federation-validate-request.xml" id="federation" />
          <webflow:flow-location
              path="/WEB-INF/flows/federation-validate-request.xml" id="federation/up" />
          <webflow:flow-location
              path="/WEB-INF/flows/federation-validate-request.xml" id="federation/krb" />
          <webflow:flow-location
              path="/WEB-INF/flows/federation-validate-request.xml" id="federation/clientcert" />
          <webflow:flow-location 
              path="/WEB-INF/flows/federation-signin-request.xml" id="signinRequest" />
          <webflow:flow-location 
              path="/WEB-INF/flows/federation-signin-response.xml" id="signinResponse" />
      </webflow:flow-registry>
      Adding security restrictions to the flow will allow spring security to be included in the flow for user authentication.
      <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping" p:flowRegistry-ref="flowRegistry"
          p:order="2">
      </bean>

      <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
          <webflow:flow-execution-attributes>
              <webflow:always-redirect-on-pause value="false" />
          </webflow:flow-execution-attributes>

          <webflow:flow-execution-listeners>
              <webflow:listener ref="securityFlowExecutionListener" />
          </webflow:flow-execution-listeners>
      </webflow:flow-executor>

      <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter" p:flowExecutor-ref="flowExecutor" />

      <bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener">
          <property name="accessDecisionManager" ref="accessDecisionManager" />
      </bean>

      <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
          <property name="decisionVoters">
              <list>
                  <bean class="org.springframework.security.access.vote.RoleVoter">
                      <property name="rolePrefix" value="ROLE_" />
                  </bean>
                  <bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
              </list>
          </property>
      </bean>
      The spring security configuration can be found within the WEB-INF/security-config.xml file.
      Categories: Jan Bernhardt

      Register trusted 3rd party IDP with SAML Web Browser SSO Profile

      Fri, 12/18/2015 - 23:35
      In this Post I'll explain how to configure Apache Fediz IDP so that it can be used with a trusted 3rd party IDP based on SAML Web Browser SSO Profile.

      In my previous posts about Apache Fediz I focused on the WS-Federation passive protocol only since it is the successor standard for the SAML Web Browser SSO Profile. But in some cases you will have to establish a federated trust relation with an IDP how does not support the WS-Federation Standard yet, but only the older SAML Web Browser SSO Profile.


      I'll explain how to register a SAML trusted IDP at the IDP as well as how to setup a demonstrator. Please also take a look at Colms post about this topic. PreconditionsI would assume that you have the fedizhelloworld demo application already running within your Tomcat container, as well as the Fediz IDP & STS in a second Tomcat  container. If you don't know how to do this, you will find a detailed instruction in my previous post about Fediz, as well as this post from Colm.
      Install SAML IDPFediz IDP itself does not support the SAML Web Browser Profile as a primary IDP protocol. So you cannot use Fediz IDP so login based on SAML Web Browser Profile. But you can register a 3rd party IDP based on that profile when Fediz is acting as a Service Provider (client).

      For purposes of integration testing however the Fediz Project provides a mockup implementation of a SAML IDP which we will use for demonstration purposes here. To build the war file you should do the following:

      1. Clone the Fediz Sources on your computer with GIT
      > git clone -v https://github.com/apache/cxf-fediz.git
      2. Goto the systestfolder and build the systest with maven
      > cd cxf-fediz/systests/federation/samlIdpWebapp/
      > mvn -Pfastinstall
      3. Copy war file to tomcat webapps folder
      > cp target/*.war ${tomcat.fediz.idp.home}/webapps
      4. Start Fediz-IDP and Fediz Demo app (if not already done yet)
      > ${tomcat.fediz.idp.home}/bin/startup.sh
      > ${tomcat.fediz.rp.home}/bin/startup.sh Register 3rd Party IDPNext you must register the SAML SSO IDP at your Fediz-IDP so that you can choose it as your home realm at the login process. This can be done via a REST API since version 1.2.0.

      The REST Service API requires a Basic user authentication. Default username is admin and password is password.
      1. Register a new 3rd Party IDP
      POST https://localhost:9443/fediz-idp/services/rs/trusted-idps
      <ns2:trustedIdp id="0" xmlns:ns2="http://org.apache.cxf.fediz/">
         <realm>urn:org:apache:cxf:fediz:idp:realm-C</realm>
         <url>https://localhost:9443/samlssoidp/samlsso</url>
         <name>Realm C</name>
         <description>SAML Web SSO</description>
         <protocol>urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser</protocol>
         <trustType>PEER_TRUST</trustType>
         <certificate>realmb.cert</certificate>
         <federationType>FEDERATE_IDENTITY</federationType>
         <cacheTokens>true</cacheTokens>
         <parameters>
            <entry>
               <key>support.deflate.encoding</key>
               <value>true</value>
            </entry>
         </parameters>
      </ns2:trustedIdp>2. Assign this new 3rd Party IDP to your Realm-A Fediz IDP
      POST https://localhost:9443/fediz-idp/services/rs/idps/urn%3Aorg%3Aapache%3Acxf%3Afediz%3Aidp%3Arealm-A/trusted-idps
      <ns2:trustedIdp xmlns:ns2="http://org.apache.cxf.fediz/">
          <realm>urn:org:apache:cxf:fediz:idp:realm-C</realm>  
      </ns2:trustedIdp> Test: Perform a federated Login
      Make sure to delete any localhost cookies within your browser. Otherwise your preferred home realm could be stored within a cookie and therefore your would not see a home realm selection screen.
      Now you can perform a login by invoking the following URL:
      https://localhost:8443/fedizhelloworld/secure/fedservlet

      You should see a home realm selection screen, with our new SAML SSO IDP which you should select. Next you should see a Basic user authentication window. Here you can login with ALICE and ECILA as your password.


      After some redirect you should see the demo page with your federated user account from alice:

      Review Redirects in DetailIf you use a monitoring tool like Fiddler, you will be able to analyze the redirects in greater detail.

      After invoking the fedizhelloworld demo app, I'll get redirected to the Fediz IDP:
      GET https://localhost:9443/fediz-idp/federation
           ?wa=wsignin1.0
          &wreply=https%3A%2F%2Flocalhost%3A8443%2Ffedizhelloworld%2Fsecure%2Ffedservlet
          &wtrealm=urn%3Aorg%3Aapache%3Acxf%3Afediz%3Afedizhelloworld
           &wct=2015-12-18T17%3A45%3A36.860Z
          &wctx=b9220a8a-5802-41a2-9128-a2ba649a72bc
      The Fediz IDP will show you the home realm selection page and after selection will redirect you to the SAML SSO IDP
      GET https://localhost:9443/samlssoidp/samlsso
          ?SAMLRequest=nVNdb9owFP0rkd9TEkihuQIkBpqG1G0pZHvYm3FuiiXHznydlu3Xz07TlofBpL3545xzj8%2B9nhNv1LiFVeeOeoc%2FOyQXrYjQOmn02mjqGrR7tE9S4Lfd%2FYIdnWsJRiNlBFdHQw7yLJuMaqzk71hWbVih5YHOoo2Xk7rfXGQGB0QmUIcliz4aK7D3tGA1V4Qs2m4WTMzq5DDNeFxPJkmc3aXTOM9nWZxM0yrnh6ziSeqRVHAi%2BYTvXKIOt5oc127Bxkl6G6fjOL0r0xlktzDJb2Z5%2BoNFhTXOCKM%2BSF1J%2FbhgndVgOEkCzRskcAL2q8%2F3ML5J4PACIvhUlkVcfN2XLPqOlvq3egCLTo3SBC8JX9dqh8JsOe%2Fh0Du25wrXBfhry9iyh9lH4C0XRwRxqqFvDviEwSJXTbyaj87LDEVb%2BOJ1t5vCKCl%2BRSulzPPaE5wP0tkO%2B7Y03F13Ek5kFdc9FNoQCDnUjkX7Iug%2FdFzJWqIdZP7hlI3e3A3jiVU%2FGH42HZ5ctDZNy62kkDqeuHBvGZ7D1sontMP6%2FxO9BBMggrY%2FDlP3bGwVpgiF91larqk11r3G%2FTdHy%2BHywvver8%2B%2F6PIP
           &RelayState=962c9908-489c-4ea2-b1a4-090e180c91f3
          &SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1
          &Signature=PyFe4kjQLWUoatdKZ0uZig27CSgrIZpmgU%2FiGL86KW8JIeVgAEIm9StYwdPUWiJO9KMM5wKmd9o6tWjFM7oIEtv8yIYo%2Fcr1nX7qDj5QRd2ni2akDH61OdV%2FvPECS0auRolW1vDwT6qwnqBFNC1KWSJXXpHu0bk7HXRkfnyA3p557ZECunsYsPhMp1JfaGQJUP8tw2LR0HNweoL7NA%2FbKU8lzwKrIcmJ7kFsYC2OrW3TucfqruQ0hrQYIvHFyISwqc7uWRgiGo8KhvTuw1pg2JvpZJZq%2F50OWHGWLWuE5QKT2C5yjJeb7xch4gPkg4PIBJCqrENZSE7OZWIcb%2Fydjw%3D%3D
      You can use for example Notepad++ to decode SAML-P Requests & Responses.


      After decoding the SAMLRequest you will see the following SAML AuthnRequest:
      <saml2p:AuthnRequest AssertionConsumerServiceURL="https://localhost:9443/fediz-idp/federation" Destination="https://localhost:9443/samlssoidp/samlsso" ForceAuthn="false" ID="c7f0b64a-f330-4816-9974-061d9ab4da01" IsPassive="false" IssueInstant="2015-12-18T17:45:39.791Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
          <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">urn:org:apache:cxf:fediz:idp:realm-A</saml2:Issuer>
          <saml2p:NameIDPolicy AllowCreate="true" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" SPNameQualifier="urn:org:apache:cxf:fediz:idp:realm-A"/>
          <saml2p:RequestedAuthnContext Comparison="exact">
              <saml2:AuthnContextClassRef xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
          </saml2p:RequestedAuthnContext>
      </saml2p:AuthnRequest>

      After login at the SAML IDP the user will be redirected back to Fediz-IDP:
      GET https://localhost:9443/fediz-idp/federation
          ?SAMLResponse=rVdZk6JMFn33V1TYj4YFCYhAdFUEiwsKgiIqvEywJIuySYLbrx%2B0ytqmqqfniwkfJC93OffcJDn8Rk6aEAW3gKjIMwQfZOmpzUBI9hkHdH2A97sUSwZdF7heF2dpD3dZlqEDov0gZ/egZf7U9voB7tKU0w1IEu9SDKC7LNunujgNfNZxKd/BQRODUA3lDFVOVj21CRz0uoDoAmYJ%2BhzV40j2kSFxu/2wgiWK86xxecTbD6c0yRD3gvSpXZcZlzsoRlzmpBBxlccZvKpwjStXlHmVe3nSfv59c%2BduBcuPGf6cwEEIllVTuv0cVVWBOAxLcs9JohxVHEtRJHZNglAe%2B8X98jf2sdZr5YIzKqeq0ZelmPvwYeUkNfwzDnTz5oza8yBCbez5tciXtBx/x3sb3L9YQDE0QzBdzyV7XQr0mC7ju0yXpkjAeC5OeX38/zCFv%2BXwNeaE4qf2lc6GzePx%2BHgkH/MyxAgcB9hGVQwvgqnTjW%2BAPNhEoZirzkVD0Zcml43xy2j/%2BZj8ht44zBo2S/gK1Ec/4MQxnMUaHx/F4a/2Wyz05SzIb0vRyfIsbiDEF%2BcKVYVVlPsPfBLmZVxF6Y8EAPyauAtPXtcDVPbrOuuP0P4y0SeEJXK6KHLAa64FDGAJG2ofzIX81P71d9vkFrosnQwFeZmiz8v/DQ/MDjDJC%2Bh30b2tV2h/n/B7prD/xCjFIUTVP6HtA2UvSW4P6jM7r1aVQScStV%2BeeJ3igaqVnjMrhOPTDcBH55vhjfCX5Zet8jbal4ghSA3AWtlZP1qzmglsIDn9%2BcLaXlYXRpdwYOIRT1L0ft9bDFczYjY3Ombp7Ox0Wep1NauKmtFxeROWKbVH/WGhRoJTVbxrni9o6MtLeWPLOl8SypJQsWQqOEzMFpI2cQNWm4S14eY4bxm8MdY3YDZVjkSA2QsKGEHQIS8nuTfWjoW58cb%2Bsq8IU1zbTGNKK5SL2wlPoifyNCmpaDE9r3GgT3HEb6roMoQBEpyOp5xlpIdqudrpQ8OzmfzSmXWGmTvU0m2fKdw5sdlsjsuB5UwjhtqPowu6jGpS1EUlV7cFpdRAHa51TzpT%2B10RwJnhgXHfww5jw%2BxjEFvXwWgsshB6grGa0HYmx8I0M87Hp6c36j9wfaV/Cs9vo9j0cFZyKudtIV4PmqB5jiv4rMqyeFyKIu%2BkIX%2BUBT6UB75DXIoLPxPC3T7axSP2iAv8HA15iV%2Boc%2BYozS1pNZ9Lg6NtLMyhujTl4zjyZq3m6jjbDo7NPz6TVGLdGNVtY5MG1Jtt%2B3OSMBzEKo%2BPRGM/ahmyS0rzQVPZ5HlKFqQjf3WY8nmDci4OvZ6A4Wd3aNmZsc1YgyZrfc/Kwnq%2BQgQ8ODZl4sdDAOnDKmnttZrXGDraTjB6EBpurY0TVY9lyyUTH1IpXO7pQWdTrPMTrgwY5WzM/RN2AAYDVsRkqoA862/iZiO4lNnCiRTfb2YwpvsLYIr83K7cqDgsoTdJ/SznHbfQ5MnO2CfzTKsYoqT8zWlgDXumXMb2KJ5bKr0zB41oOBQtfQgunfLEwHJLx8yomkNdW8fbKjMTex%2BOjsrgsMNnRH%2B0kEOgjTvJaFQQxWiHy%2BxYXbDaebnDFrvhYQcF1OKTvbIXk%2B15m4tbf4E0ewQ1tEaCR9jZqgOgop2yogeZ3KFjK5/V0t6ubEZNwSEZM5M1PQY6H6oCz4%2B2LXnJB6qAj3hg%2BlI4XwvCgoDYycysYguUgFY3qrbeL0pfWW2I7Tfbhdcabga8crLZFnAmEhwO1BAPPEhHghhtzqYHtIk3LE4nwk4vENcu61PEW%2BuQzDdEXzVn4aK389VcnwSBSEiRiUiTXHeElrWY4ltxX9S4ilnUIDxR8f4cjj3eWS54PcVGlb1DnaQDlcW4Y2BbfRWRl2m8xjEz2M2ts9Xb7e04ETJpo7fsodpkMDNPZyGrsDGRns364lUFu8lZdbS2lr3zcBojF2SH2TQkC2sydegqCwKgqGR4glvRSz1hPj0nZUtvzs2%2BnljRmXV2m9U4nUvlBYwR5E8l5lPWAXWsMhqXqR0N93W191V5YExGs%2Bb0Ho%2BtxTDVdBe/CGqNU1QLapHX0cwTpYzrCyywbFqlUj4bj0YnZ5pY5IB/PQC%2BPtRvxpfHHvt4IHw6MO4yw6jdLfSq%2B3LWyB1Zehg2rxun%2BlkHgUdws8R%2BN7i5cnWGCug1MKDffuYVWRzcBclLyi/1xDwL4mvgVdu9vMz%2BrLq8lHOhU8Ky/YdM16YfeN8vG1H51AZE/xFvfuCfiflZXmmZVvJBBcuvMrKHv8vIRdN2EcOr1vxBrAXQjy/dq1ZrrmB5g/queb9r5Ou9e8%2BNjx9fHdAVngAb8uFPEhfYf9dD4/cmt2s/vr7XG7KqMvZekHy%2B9XwbUhlyTuE0wpbzTgF3649r%2BuNK6CRpl7%2Bjfwv6avhUAPva23vNKsquHwQwbeh9uC3/u67/FN0kreCp%2Bs4mJo2Sb6TM8x/Fvsd5V7/GrDd/x7z033v5Jtd3Nz8b3/p5N9/1//tX0H23Pv8b    
          &RelayState=962c9908-489c-4ea2-b1a4-090e180c91f3Usually you would receive a POST message from the SAML IDP instead of a GET redirect, as done here by the mockup IDP.
      A decoded SAMLResponse will look like this:
      <saml2p:Response ID="1772f107-14b9-4d27-9d21-f3a1cf655648" InResponseTo="41e5c1dc-7cfe-446e-b475-3fb7a40b5432" IssueInstant="2015-12-18T14:16:24.456Z" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
          <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://localhost:9443/samlssoidp/samlsso</saml2:Issuer>
          <saml2p:Status>
              <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
          </saml2p:Status>
          <saml2:Assertion ID="_42311918-c677-4773-86b7-e5b42340354d" IssueInstant="2015-12-18T14:16:24.456Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="saml2:AssertionType">
              <saml2:Issuer>https://localhost:9443/samlssoidp/samlsso</saml2:Issuer>
              <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
                  <ds:SignedInfo>
                      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                      <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                      <ds:Reference URI="#_42311918-c677-4773-86b7-e5b42340354d">
                          <ds:Transforms>
                              <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                              <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                          </ds:Transforms>
                          <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                          <ds:DigestValue>IvKTW0enrkTeXynCf9Aj0053fXM=</ds:DigestValue>
                      </ds:Reference>
                  </ds:SignedInfo>
                  <ds:SignatureValue>GcHGaMUt6zM+tdKty0yXWOniNXh5V8JfYrCkjerx1LPI7K2w4oWCTfGtZ4TCnAhoKZQsyA3+VDpFg25HZIMjJVznazHPQ1idjvy5zlKdmG+jaHf+JWrKMuCb3w3UPJwWFEod49BnLGgLkBORr4rgWi0c4eloSK8NCnBHOXwhJHxkw5nUe+FIiuLpuIxbWnNddsWr7091ImuawDxfYfPDJuxaXX19EP2nx2zc7oQrmbqZAIIftao87OeAr2hfg4BDpAO7kFQC+Iw2B4pkmLUnJNyzspfU96bN9HaNvhBZSWD1HEdTzkHaNr9r1h9SSksBZLdmT8+p0+QidzNXN5H/sw==</ds:SignatureValue>
                  <ds:KeyInfo>
                      <ds:X509Data>
                          <ds:X509Certificate>MIICwTCCAamgAwIBAgIEda2zpzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZSRUFMTUIwHhcN
      MTUwNjEwMTU0NDM2WhcNMjUwNDE4MTU0NDM2WjARMQ8wDQYDVQQDEwZSRUFMTUIwggEiMA0GCSqG
      SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCFc5B/0ybFYZnSjn9S63uPq9IBWQVs2evaZ4U0wvfe6vVl
      qOuAO86hjJ/6EgSbuOHlMPiIYb3lde4meTq6E+XpWox0LE8LySQdx/v1S81V2JKL1on7XiCcCb4U
      02m0qXNei67R1UCAQZtbhpvTecJmdnoAabpOIJkSqlQnOt82r4dXxEYF5UIriZGiQYM6kUE61dvp
      PF1z+rx8erj6i8GtQePOWijtnUlZqgGwLEvk0N27GRIg1OH+lGGp2pGk0I9HMR9OyTk/RkFvkeBs
      AlqLqCljyjoCjdRsOZGeOsWsBc2ZnV+1eLOxnp5e8oa6iYoNuDqZtZ8Mm1vlH8JW6H1PAgMBAAGj
      ITAfMB0GA1UdDgQWBBR2e/xUnYpj1Lf6MXMOWqRrdLVX2jANBgkqhkiG9w0BAQsFAAOCAQEALxZ9
      1aJDeFEMg0fce6hBChXyUc1OJcFpxx2Zmze0OzWxhAYWg3oX27MUNgR5kdMoPJffC2DhUs3U3W+B
      YRK0jCqpu0M/Y4Egx4iqygHcAaTRAPm/GtZks+l+eLRH+S/jPVh3zKiW0/UfkQYyY5kqZilBnDXP
      ZFMEgxUncP9e9L9i2myUuzctp9Xo9MGWYT5yFKisb1nvNKg3pYJKa6tnff1LM3gxejCcmcBQKylr
      PA1M7PlYhy9akXVHmQDrz1HseAxr/d4Yvs+YrhHrmZhFqutqdMIESJGNithHHYRFmOPb0zBMu044
      eOhc+OUx4LHuzep/nKtmDoNHGGxaKlY3EA==</ds:X509Certificate>
                      </ds:X509Data>
                  </ds:KeyInfo>
              </ds:Signature>
              <saml2:Subject>
                  <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">ALICE</saml2:NameID>
                  <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                      <saml2:SubjectConfirmationData Address="127.0.0.1" InResponseTo="41e5c1dc-7cfe-446e-b475-3fb7a40b5432" NotOnOrAfter="2015-12-18T14:21:24.456Z" Recipient="https://localhost:9443/fediz-idp/federation"/>
                  </saml2:SubjectConfirmation>
              </saml2:Subject>
              <saml2:Conditions NotBefore="2015-12-18T14:16:24.456Z" NotOnOrAfter="2015-12-18T14:21:24.456Z">
                  <saml2:AudienceRestriction>
                      <saml2:Audience>urn:org:apache:cxf:fediz:idp:realm-A</saml2:Audience>
                  </saml2:AudienceRestriction>
              </saml2:Conditions>
              <saml2:AuthnStatement AuthnInstant="2015-12-18T14:16:24.456Z">
                  <saml2:AuthnContext>
                      <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:AuthnContextClassRef>
                  </saml2:AuthnContext>
              </saml2:AuthnStatement>
          </saml2:Assertion>
      </saml2p:Response>


      Fediz-IDP is configured to do a identity mapping and will return a SAML token back to the demo application according to WS-Federation.
      POST https://localhost:8443/fedizhelloworld/secure/fedservlet

      wa=wsignin1.0
      &wresult=%3CRequestSecurityTokenResponseCollection+xmlns%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fws-sx%2Fws-trust%2F200512%22+xmlns%3Ans2%3D%22http%3A%2F%2Fwww.w3.org%2F2005%2F08%2Faddressing%22+xmlns%3Ans3%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2F2004%2F01%2Foasis-200401-wss-wssecurity-utility-1.0.xsd%22+xmlns%3Ans4%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2F2004%2F01%2Foasis-200401-wss-wssecurity-secext-1.0.xsd%22+xmlns%3Ans5%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fws-sx%2Fws-trust%2F200802%22%3E%3CRequestSecurityTokenResponse%3E%3CTokenType%3Ehttp%3A%2F%2Fdocs.oasis-open.org%2Fwss%2Foasis-wss-saml-token-profile-1.1%23SAMLV2.0%3C%2FTokenType%3E%3CRequestedSecurityToken%3E%3Csaml2%3AAssertion+xmlns%3Asaml2%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Aassertion%22+xmlns%3Axsd%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema%22+xmlns%3Axsi%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2FXMLSchema-instance%22+ID%3D%22_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436%22+IssueInstant%3D%222015-12-18T17%3A45%3A39.933Z%22+Version%3D%222.0%22+xsi%3Atype%3D%22saml2%3AAssertionType%22%3E%3Csaml2%3AIssuer%3ESTS+Realm+A%3C%2Fsaml2%3AIssuer%3E%3Cds%3ASignature+xmlns%3Ads%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23%22%3E%3Cds%3ASignedInfo%3E%3Cds%3ACanonicalizationMethod+Algorithm%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2F10%2Fxml-exc-c14n%23%22%2F%3E%3Cds%3ASignatureMethod+Algorithm%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256%22%2F%3E%3Cds%3AReference+URI%3D%22%23_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436%22%3E%3Cds%3ATransforms%3E%3Cds%3ATransform+Algorithm%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23enveloped-signature%22%2F%3E%3Cds%3ATransform+Algorithm%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2F10%2Fxml-exc-c14n%23%22%3E%3Cec%3AInclusiveNamespaces+xmlns%3Aec%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2F10%2Fxml-exc-c14n%23%22+PrefixList%3D%22xsd%22%2F%3E%3C%2Fds%3ATransform%3E%3C%2Fds%3ATransforms%3E%3Cds%3ADigestMethod+Algorithm%3D%22http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmlenc%23sha256%22%2F%3E%3Cds%3ADigestValue%3EOB6hSosPWjhA5dxCE%2BF3eFAC4dRu%2FZxFT9XO%2B9tXBAI%3D%3C%2Fds%3ADigestValue%3E%3C%2Fds%3AReference%3E%3C%2Fds%3ASignedInfo%3E%3Cds%3ASignatureValue%3EOAZHDiqlANZXtK0UPfrusUTAf1E9hrPHjUw9kB0sP24RMtxjIfcJ0UFTIb1gBHMqGz%2BbxPJozH7c6O%2F%2F2OYa5V3eRDadQOqnxKvReDh8YjHqs641uhdNqlJl9SogWsm7MPmznmwB5jRLqCaQpTQDfFnjwHXPgxwcASh1i3anfYSpJebnq4ipC3%2Flyuy99xXb1tQoai6hgdRiPs5ragYUPLqE9bIrULj%2FOTbuXY4ikKcNBHltKzAhPJtvaVDzgUkAKRYNBk64te1vRTCYYdMWXjMjA%2FC2obHhIB4zA5eMjxoMPmZHe7ZxVVRiB938S%2FJW%2B4ysJvoVdFX2FTmqRmIKhA%3D%3D%3C%2Fds%3ASignatureValue%3E%3Cds%3AKeyInfo%3E%3Cds%3AX509Data%3E%3Cds%3AX509Certificate%3EMIICwTCCAamgAwIBAgIEINqJ9TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZSRUFMTUEwHhcN%0D%0AMTUwNjEwMTU0NDE3WhcNMjUwNDE4MTU0NDE3WjARMQ8wDQYDVQQDEwZSRUFMTUEwggEiMA0GCSqG%0D%0ASIb3DQEBAQUAA4IBDwAwggEKAoIBAQCJDSXn2lDR%2BJM%2BAsJarFG3%2FXGH7K%2B9AfAbQIz2IgB9MCpO%0D%0AKVWTUPCvuo1I%2BFp5nEGreuHYLEwgIiam3o%2BC9tvpLgtDDaDkmXjDzkWpk8z6%2Bim72HZ%2FODF93Rqw%0D%0AjIiY5ZCzgDumFyPzdKiGwChThamidy%2Brd6oheSoi6qRVSMMcnwiEUmvkfFvV3izXRqeT5nGQwsin%0D%0Ay9mCEiGx8jkfxP%2B%2BH0RQjVjhOwzfQ7epsR7dTQNf2ZhkBR3o6wKV9QnF2IBWHZpA9EK58rWU9H6j%0D%0AG7b631rYvwsbOUF9HcZ8DI2BFh%2B4p18jDN%2FfnjNGSLr9rYOExpsIiF1cHBK7Tr7WwCmDAgMBAAGj%0D%0AITAfMB0GA1UdDgQWBBRHy0qYoLm9jx%2F1L6r61NznHKun2jANBgkqhkiG9w0BAQsFAAOCAQEAR9rU%0D%0A5Sp1FsOErdvKNFqeaKl0oq6Fuz7BWcGm2kK6%2B1ZbWE8IOv6Vh%2BBlLuOe5hF7aLUbm8UIjhKsmg0M%0D%0AEy5MBwkBZktT1qhQteMuiKgYR7CxayCxO0f125RYvvwntJa5rI7bUrzOqX29VQD1qQ%2FTb%2B08fULT%0D%0AL7oURP%2Bg88Ff99dn3IpO4VZxZdsbl4%2BKZRtqQvPAdXNYjOajJtPzS489%2B%2FDtfWJ6wPm%2F7YZ4did4%0D%0A1fYcrdwyEZ15L0%2F5i931z7sztNickm5WhO40qEVDKN6KrlV2Eyea0%2B933v2Pwe4resTlko9G2T5h%0D%0AdEaSbvht2Q%2FJOMMmT91daeto2oS8HTKhTA%3D%3D%3C%2Fds%3AX509Certificate%3E%3C%2Fds%3AX509Data%3E%3C%2Fds%3AKeyInfo%3E%3C%2Fds%3ASignature%3E%3Csaml2%3ASubject%3E%3Csaml2%3ANameID+Format%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A1.1%3Anameid-format%3Aunspecified%22+NameQualifier%3D%22http%3A%2F%2Fcxf.apache.org%2Fsts%22%3Ealice%3C%2Fsaml2%3ANameID%3E%3Csaml2%3ASubjectConfirmation+Method%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Acm%3Abearer%22%2F%3E%3C%2Fsaml2%3ASubject%3E%3Csaml2%3AConditions+NotBefore%3D%222015-12-18T17%3A45%3A39.890Z%22+NotOnOrAfter%3D%222015-12-18T18%3A45%3A39.890Z%22%3E%3Csaml2%3AAudienceRestriction%3E%3Csaml2%3AAudience%3Eurn%3Aorg%3Aapache%3Acxf%3Afediz%3Afedizhelloworld%3C%2Fsaml2%3AAudience%3E%3C%2Fsaml2%3AAudienceRestriction%3E%3C%2Fsaml2%3AConditions%3E%3Csaml2%3AAttributeStatement%3E%3Csaml2%3AAttribute+Name%3D%22http%3A%2F%2Fschemas.xmlsoap.org%2Fws%2F2005%2F05%2Fidentity%2Fclaims%2Frole%22+NameFormat%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Aattrname-format%3Aunspecified%22%3E%3Csaml2%3AAttributeValue+xsi%3Atype%3D%22xsd%3Astring%22%3EUser%3C%2Fsaml2%3AAttributeValue%3E%3C%2Fsaml2%3AAttribute%3E%3Csaml2%3AAttribute+Name%3D%22http%3A%2F%2Fschemas.xmlsoap.org%2Fws%2F2005%2F05%2Fidentity%2Fclaims%2Fgivenname%22+NameFormat%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Aattrname-format%3Aunspecified%22%3E%3Csaml2%3AAttributeValue+xsi%3Atype%3D%22xsd%3Astring%22%3EAlice%3C%2Fsaml2%3AAttributeValue%3E%3C%2Fsaml2%3AAttribute%3E%3Csaml2%3AAttribute+Name%3D%22http%3A%2F%2Fschemas.xmlsoap.org%2Fws%2F2005%2F05%2Fidentity%2Fclaims%2Fsurname%22+NameFormat%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Aattrname-format%3Aunspecified%22%3E%3Csaml2%3AAttributeValue+xsi%3Atype%3D%22xsd%3Astring%22%3ESmith%3C%2Fsaml2%3AAttributeValue%3E%3C%2Fsaml2%3AAttribute%3E%3Csaml2%3AAttribute+Name%3D%22http%3A%2F%2Fschemas.xmlsoap.org%2Fws%2F2005%2F05%2Fidentity%2Fclaims%2Femailaddress%22+NameFormat%3D%22urn%3Aoasis%3Anames%3Atc%3ASAML%3A2.0%3Aattrname-format%3Aunspecified%22%3E%3Csaml2%3AAttributeValue+xsi%3Atype%3D%22xsd%3Astring%22%3Ealice%40realma.org%3C%2Fsaml2%3AAttributeValue%3E%3C%2Fsaml2%3AAttribute%3E%3C%2Fsaml2%3AAttributeStatement%3E%3C%2Fsaml2%3AAssertion%3E%3C%2FRequestedSecurityToken%3E%3CRequestedAttachedReference%3E%3Cns4%3ASecurityTokenReference+xmlns%3Awsse11%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2Foasis-wss-wssecurity-secext-1.1.xsd%22+wsse11%3ATokenType%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2Foasis-wss-saml-token-profile-1.1%23SAMLV2.0%22%3E%3Cns4%3AKeyIdentifier+ValueType%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2Foasis-wss-saml-token-profile-1.1%23SAMLID%22%3E_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436%3C%2Fns4%3AKeyIdentifier%3E%3C%2Fns4%3ASecurityTokenReference%3E%3C%2FRequestedAttachedReference%3E%3CRequestedUnattachedReference%3E%3Cns4%3ASecurityTokenReference+xmlns%3Awsse11%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2Foasis-wss-wssecurity-secext-1.1.xsd%22+wsse11%3ATokenType%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2Foasis-wss-saml-token-profile-1.1%23SAMLV2.0%22%3E%3Cns4%3AKeyIdentifier+ValueType%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fwss%2Foasis-wss-saml-token-profile-1.1%23SAMLID%22%3E_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436%3C%2Fns4%3AKeyIdentifier%3E%3C%2Fns4%3ASecurityTokenReference%3E%3C%2FRequestedUnattachedReference%3E%3Cwsp%3AAppliesTo+xmlns%3Awsp%3D%22http%3A%2F%2Fwww.w3.org%2Fns%2Fws-policy%22+xmlns%3Awst%3D%22http%3A%2F%2Fdocs.oasis-open.org%2Fws-sx%2Fws-trust%2F200512%22%3E%3Cwsa%3AEndpointReference+xmlns%3Awsa%3D%22http%3A%2F%2Fwww.w3.org%2F2005%2F08%2Faddressing%22%3E%3Cwsa%3AAddress%3Eurn%3Aorg%3Aapache%3Acxf%3Afediz%3Afedizhelloworld%3C%2Fwsa%3AAddress%3E%3C%2Fwsa%3AEndpointReference%3E%3C%2Fwsp%3AAppliesTo%3E%3CLifetime%3E%3Cns3%3ACreated%3E2015-12-18T17%3A45%3A39.890Z%3C%2Fns3%3ACreated%3E%3Cns3%3AExpires%3E2015-12-18T18%3A45%3A39.890Z%3C%2Fns3%3AExpires%3E%3C%2FLifetime%3E%3C%2FRequestSecurityTokenResponse%3E%3C%2FRequestSecurityTokenResponseCollection%3E
      &wctx=b9220a8a-5802-41a2-9128-a2ba649a72bc
      &wtrealm=urn%3Aorg%3Aapache%3Acxf%3Afediz%3Afedizhelloworld
      The final wresult which will be sent to the demo app looks like this:
      <RequestSecurityTokenResponseCollection xmlns="http://docs.oasis-open.org/ws-sx/ws-trust/200512" xmlns:ns2="http://www.w3.org/2005/08/addressing" xmlns:ns3="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns4="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns5="http://docs.oasis-open.org/ws-sx/ws-trust/200802">
      <RequestSecurityTokenResponse>
      <TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</TokenType>
      <RequestedSecurityToken>
      <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436" IssueInstant="2015-12-18T17:45:39.933Z" Version="2.0" xsi:type="saml2:AssertionType">
      <saml2:Issuer>STS Realm A</saml2:Issuer>
      <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
      <ds:Reference URI="#_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436">
      <ds:Transforms>
      <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
      <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
      <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xsd"/>
      </ds:Transform>
      </ds:Transforms>
      <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
      <ds:DigestValue>OB6hSosPWjhA5dxCE+F3eFAC4dRu/ZxFT9XO+9tXBAI=</ds:DigestValue>
      </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>OAZHDiqlANZXtK0UPfrusUTAf1E9hrPHjUw9kB0sP24RMtxjIfcJ0UFTIb1gBHMqGz+bxPJozH7c6O//2OYa5V3eRDadQOqnxKvReDh8YjHqs641uhdNqlJl9SogWsm7MPmznmwB5jRLqCaQpTQDfFnjwHXPgxwcASh1i3anfYSpJebnq4ipC3/lyuy99xXb1tQoai6hgdRiPs5ragYUPLqE9bIrULj/OTbuXY4ikKcNBHltKzAhPJtvaVDzgUkAKRYNBk64te1vRTCYYdMWXjMjA/C2obHhIB4zA5eMjxoMPmZHe7ZxVVRiB938S/JW+4ysJvoVdFX2FTmqRmIKhA==</ds:SignatureValue>
      <ds:KeyInfo>
      <ds:X509Data>
      <ds:X509Certificate>MIICwTCCAamgAwIBAgIEINqJ9TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZSRUFMTUEwHhcN
      MTUwNjEwMTU0NDE3WhcNMjUwNDE4MTU0NDE3WjARMQ8wDQYDVQQDEwZSRUFMTUEwggEiMA0GCSqG
      SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCJDSXn2lDR+JM+AsJarFG3/XGH7K+9AfAbQIz2IgB9MCpO
      KVWTUPCvuo1I+Fp5nEGreuHYLEwgIiam3o+C9tvpLgtDDaDkmXjDzkWpk8z6+im72HZ/ODF93Rqw
      jIiY5ZCzgDumFyPzdKiGwChThamidy+rd6oheSoi6qRVSMMcnwiEUmvkfFvV3izXRqeT5nGQwsin
      y9mCEiGx8jkfxP++H0RQjVjhOwzfQ7epsR7dTQNf2ZhkBR3o6wKV9QnF2IBWHZpA9EK58rWU9H6j
      G7b631rYvwsbOUF9HcZ8DI2BFh+4p18jDN/fnjNGSLr9rYOExpsIiF1cHBK7Tr7WwCmDAgMBAAGj
      ITAfMB0GA1UdDgQWBBRHy0qYoLm9jx/1L6r61NznHKun2jANBgkqhkiG9w0BAQsFAAOCAQEAR9rU
      5Sp1FsOErdvKNFqeaKl0oq6Fuz7BWcGm2kK6+1ZbWE8IOv6Vh+BlLuOe5hF7aLUbm8UIjhKsmg0M
      Ey5MBwkBZktT1qhQteMuiKgYR7CxayCxO0f125RYvvwntJa5rI7bUrzOqX29VQD1qQ/Tb+08fULT
      L7oURP+g88Ff99dn3IpO4VZxZdsbl4+KZRtqQvPAdXNYjOajJtPzS489+/DtfWJ6wPm/7YZ4did4
      1fYcrdwyEZ15L0/5i931z7sztNickm5WhO40qEVDKN6KrlV2Eyea0+933v2Pwe4resTlko9G2T5h
      dEaSbvht2Q/JOMMmT91daeto2oS8HTKhTA==</ds:X509Certificate>
      </ds:X509Data>
      </ds:KeyInfo>
      </ds:Signature>
      <saml2:Subject>
      <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQualifier="http://cxf.apache.org/sts">alice</saml2:NameID>
      <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"/>
      </saml2:Subject>
      <saml2:Conditions NotBefore="2015-12-18T17:45:39.890Z" NotOnOrAfter="2015-12-18T18:45:39.890Z">
      <saml2:AudienceRestriction>
      <saml2:Audience>urn:org:apache:cxf:fediz:fedizhelloworld</saml2:Audience>
      </saml2:AudienceRestriction>
      </saml2:Conditions>
      <saml2:AttributeStatement>
      <saml2:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml2:AttributeValue xsi:type="xsd:string">User</saml2:AttributeValue>
      </saml2:Attribute>
      <saml2:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml2:AttributeValue xsi:type="xsd:string">Alice</saml2:AttributeValue>
      </saml2:Attribute>
      <saml2:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml2:AttributeValue xsi:type="xsd:string">Smith</saml2:AttributeValue>
      </saml2:Attribute>
      <saml2:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
      <saml2:AttributeValue xsi:type="xsd:string">alice@realma.org</saml2:AttributeValue>
      </saml2:Attribute>
      </saml2:AttributeStatement>
      </saml2:Assertion>
      </RequestedSecurityToken>
      <RequestedAttachedReference>
      <ns4:SecurityTokenReference xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0">
      <ns4:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID">_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436</ns4:KeyIdentifier>
      </ns4:SecurityTokenReference>
      </RequestedAttachedReference>
      <RequestedUnattachedReference>
      <ns4:SecurityTokenReference xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0">
      <ns4:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID">_d11b7fa4-3dc2-4b81-a29e-c80cc6e20436</ns4:KeyIdentifier>
      </ns4:SecurityTokenReference>
      </RequestedUnattachedReference>
      <wsp:AppliesTo xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
      <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
      <wsa:Address>urn:org:apache:cxf:fediz:fedizhelloworld</wsa:Address>
      </wsa:EndpointReference>
      </wsp:AppliesTo>
      <Lifetime>
      <ns3:Created>2015-12-18T17:45:39.890Z</ns3:Created>
      <ns3:Expires>2015-12-18T18:45:39.890Z</ns3:Expires>
      </Lifetime>
      </RequestSecurityTokenResponse>
      </RequestSecurityTokenResponseCollection>

      Categories: Jan Bernhardt

      Liferay Portal Integration with Fediz OpenID Connect

      Thu, 12/17/2015 - 11:24
      I was given the task to provide a security solution to enable SSO in a Liferay portal based on OpenID Connect with the Apache Fediz OIDC Service. In this post I'll explain how to get this done.

      You will need Apache Fediz version 1.3.0 or higher, if you want to setup this use case by yourselfInstall Liferay Portal with Fediz PluginFirst download and extract Liferay bundled with Tomcat. (I used liferay-portal-6.2-ce-ga5)

      Enable HTTPs port in liferay-portal-6.2-ce-ga5/tomcat-7.0.62/conf/server.xml
      <Server port="8005" shutdown="SHUTDOWN">

           . . .

          <Service name="Catalina">

               . . .

              <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
                   maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
                   keystoreFile="idp-ssl-key.jks"
                   keystorePass="tompass"
                   clientAuth="false"
                   sslProtocol="TLS" />

          </Service>
      </Server>
      I reused the idp-ssl-key.jks file from the IDP Tomcat to keep things simple. Of course you can also use a different keystore. Your keystore should be stored in your tomcat root folder liferay-portal-6.2-ce-ga5/tomcat-7.0.62/.

      Now you can start Liferay for a first setup. Simply execute liferay-portal-6.2-ce-ga5/tomcat-7.0.62/bin/startup.sh

      After Tomcat startup is complete you can invoke the server page at https://localhost:8443/.

      I continued with the default values (except for the Email address) and simply clicked "Finish Configuration". After that you have to wait until Liferay installation is complete. Next you will see the license confirmation page, which you need to confirm. After that you can set your password reminder as well as the administration password.

      The OpenID Connect Extension which we will install in the following section requires a user group UnityUser which will be applied for all new users when then login the very first time to the portal. Therefore we must create this user group first under control panel -> Users -> User Groups -> Add


      Your Liferay Portal is now up and running so lets continue next on enabling OpenID Connect for your portal.
      Register Liferay Portal at OIDC ProviderAfter starting your OIDC Service, you can register the Liferay Portal under the following URL: https://localhost:9443/fediz-oidc/clients/register.

      You will need the client Identifier as well as the Client Secret in the following section.
      Install OpenIdConnect Extension for LiferayYou will need the OpenIdConnectLiferay extension, which you can clone from Github. Further information on installing this extension can be found at the authors webpage.

      You'll need to update the configuration within it.infn.ct.security.liferay.openidconnect.utils.Authenticator

      public Authenticator(State state) {
          authC = new ClientSecretBasic(new ClientID("hLiSIY6b1X_0Jg"), new Secret("llPySiI1aEwyIgsnyBu6aA"));
          this.state = state;
          try {
              callback = new URI("https://localhost:8443/c/portal/login");
      oauthS = new URI("https://localhost:9443/fediz-oidc/idp/authorize");
              tokenS = new URI("https://localhost:9443/fediz-oidc/oauth2/token");
              userS = new URI("https://localhost:9443/fediz-oidc/users/userinfo");
              tokenCertSign = new URI("https://localhost:9443/fediz-oidc/jwk/keys");
              issuer = "accounts.fediz.com";
              aud = "hLiSIY6b1X_0Jg";
          } catch (URISyntaxException ex) {
              _log.error(ex);
          }
      }

      Now you can build and deploy this extension.
      $ mvn clean install
      You will find a jar file with all extensions at OpenIdConnectLiferay/target/OpenIdConnectLiferay-0.1-jar-with-dependencies.jar. You need to copy this jar file to liferay-portal-6.2-ce-ga5/tomcat-7.0.62/lib/ext/.

      Next you need to create (or modify if it already exists) the following file liferay-portal-6.2-ce-ga5/tomcat-7.0.62/webapps/ROOT/WEB-INF/classes/portal-ext.properties to activate the OpenID Connect Login handler:
      auto.login.hooks=\
      it.infn.ct.security.liferay.openidconnect.OpenIdConnectAutoLogin,\
      com.liferay.portal.security.auth.CASAutoLogin,\
      com.liferay.portal.security.auth.FacebookAutoLogin,\
      com.liferay.portal.security.auth.NtlmAutoLogin,\
      com.liferay.portal.security.auth.OpenIdAutoLogin,\
      com.liferay.portal.security.auth.OpenSSOAutoLogin,\
      com.liferay.portal.security.auth.RememberMeAutoLogin,\
      com.liferay.portal.security.auth.SiteMinderAutoLogin

      Now you should restart your tomcat and after that you can invoke the following login URL: https://localhost:8443/c/portal/login?openIdLogin=true. This time you should get redirected to Fediz-IDP for user authentication (login). After successful login you should be able to see your portal again with an active user.

      For debugging purposes is can also be helpful to increase the log level by adding the following line at liferay-portal-6.2-ce-ga5/tomcat-7.0.62/conf/logging.properties
      it.infn.ct.security.liferay.openidconnect.level = FINE
      Test your Setup: Login with OpenID ConnectNow you can validate if your setup is working as expected. Open the following URL in your browser:
      https://localhost:8443/c/portal/login?openIdLogin=true

      Make sure to logout first, if you are still logged in at your portal.
      You should get redirected to the Fediz IDP Login page and after login (bob:bob) you should get redirected back to your Liferay portal.
      When you login the very first time, you should see a screen to confirm the "Terms of Use". After that you will be asked to enter a new password. This password can be used to login without SSO / OpenID Connect so you should choose a complex password. As long as you use SSO however you will never be ask again to enter this password. Same applies to the password reminder.
      Once that is done, you will see the start screen of the Liferay Portal.
      Now you can logout and login again with the above URL. This time you will login directly without any additional steps/questions.

      You have been successful!
      Categories: Jan Bernhardt

      Fediz with OpenID Connect Support and WS-Federation Bridge (2/2)

      Mon, 12/14/2015 - 14:46
      Setup a DemonstratorIn this article I'll explain how to setup a demonstrator for the use case described in my previous post.
      Setup Fediz IDP & OIDCFirst you need to setup the Fediz IDP as usual. To get the OIDC Service working you also need to do the following:
      1. Install Fediz Plugin for the Fediz IDP Server (usually you would do this for the client application only)
        For the fediz_config.xml you can use the sample provided with the OIDC Service.
      2. Download or build the OIDC service and then deploy the fediz-oidc.war file to your webapps folder (same place where you deployed STS & IDP)
      Register an OpenID Connect Client From the perspective of OpenID Connect the Web Portal takes the role of a OIDC client. So the client must be registered up front. After starting the OIDC service you can invoke the following URL in your browser:

      https://localhost:9443/fediz-oidc/clients/register
      For Client Name and Client Description you can enter any human readable and meaningful value related to your service.
      OIDC supports Confidential as well as Public as a Client Type. If your Client is a Public client (for example your client is java code running inside the browser of a user), no client secret will be generated, since the secret could not be protected anyway. The normal use case however should be Confidential.
      Your redirect URI will be the final URL from which the OIDC service will redirect the user with the generated code. Normally this will be a OIDC Client service from your app consuming the code value and exchanging it for an access and ID token.
      If your application (OIDC Client) is  bound to a fix Home Realm (all users from this app will always login at the same home realm), then you can select this HomeRealm here. In this case users will not see a home realm selection screen but will be redirected to the correct home realm IDP directly.

      After submitting you client information you will see the generated client_id and client_secret. These values need to be set at the web portal.
      Understanding the Web PortalYour web portal can by any kind of (java) web application. To enable OpenID Connect support at my web application I need to add a Relaying Party (RP) OIDC Handler at a URL of my choice.
      <!--
      OIDC RP endpoint: authenticates a user by redirecting a user to OIDC Provider, and redirects the user
      to the initial application form once the authentication is done
      -->
      <jaxrs:server id="oidcRpServer" address="/oidc">
      <jaxrs:serviceBeans>
      <bean class="org.apache.cxf.rs.security.oidc.rp.OidcRpAuthenticationService">
      <!-- This state manager is shared between this RP endpoint and the oidcRpFilter which protects
      the application endpoint, the RP endpoint sets an OIDC context on it and the filter checks
      the context is available -->
      <property name="clientTokenContextManager" ref="stateManager" />
      <!-- Where to redirect to once the authentication is complete -->
      <property name="defaultLocation" value="/app/service/start" />
      </bean>
      </jaxrs:serviceBeans>

      <jaxrs:providers>
      <!-- the filter which does the actual work for obtaining an OIDC context.
      It redirect to the OIDC Provider, exchanges an authorization code for access token,
      extracts OIDC IdToken and makes it all available as OidcCientTokenContext
      -->
      <bean id="rpOidcRequestFilter" class="org.apache.cxf.rs.security.oidc.rp.OidcClientCodeRequestFilter">
      <property name="clientCodeStateManager">
      <!-- This state manager creates an OAuth2 'state' parameter and saves it in the HTTP session -->
      <bean class="org.apache.cxf.rs.security.oauth2.client.MemoryClientCodeStateManager">
      <property name="generateNonce" value="true"/>
      </bean>
      </property>
      <property name="scopes" value="openid refreshToken" />
      <property name="accessTokenServiceClient" ref="atServiceClient" />
      <property name="idTokenReader">
      <bean class="org.apache.cxf.rs.security.oidc.rp.IdTokenReader">
      <!-- disable it if the local key store or the client secret is used to validate ID Tokens -->
      <property name="jwkSetClient" ref="jwkSetClient"/>
      <property name="issuerId" value="accounts.fediz.com"/>
      </bean>
      </property>
      <property name="consumer" ref="consumer" />
      <property name="authorizationServiceUri" value="https://localhost:9443/fediz-oidc/idp/authorize" />
      <property name="startUri" value="rp" />
      <property name="completeUri" value="/" />
      </bean>

      <!-- JAX-RS provider that makes OidcClientTokenContext available as JAX-RS @Context -->
      <ref bean="clientTokenContextProvider" />
      </jaxrs:providers>
      </jaxrs:server>
       
      <!-- The state manager shared between the RP and application endpoints -->
      <bean id="stateManager" class="org.apache.cxf.rs.security.oauth2.client.MemoryClientTokenContextManager"/>
      <!-- WebClient for requesting an OAuth2 Access token.
      rpOidcRequestFilter uses it to exchange a code for a token -->

      <jaxrsclient:client id="atServiceClient" threadSafe="true"
      address="https://localhost:9443/fediz-oidc/oauth2/token"
      serviceClass="org.apache.cxf.jaxrs.client.WebClient">
      <jaxrsclient:headers>
      <entry key="Accept" value="application/json"/>
      </jaxrsclient:headers>
      <jaxrsclient:providers>
      <bean class="org.apache.cxf.jaxrs.provider.FormEncodingProvider">
      <property name="expectedEncoded" value="true"/>
      </bean>
      </jaxrsclient:providers>
      </jaxrsclient:client>

      <!-- Client id and secret allocated by OIDC ClientRegistrationService -->
      <bean id="consumer" class="org.apache.cxf.rs.security.oauth2.client.Consumer">
      <property name="clientId" value="-7TdKEwzkf5BSQ"/>
      <property name="clientSecret" value="q6ys7349uXMIgOu1kXNFTQ"/>
      </bean>
       
      <!-- JAX-RS provider that makes OidcClientTokenContext available as JAX-RS @Context -->
      <bean id="clientTokenContextProvider" class="org.apache.cxf.rs.security.oauth2.client.ClientTokenContextProvider"/>

      <!-- disable it if the local key store or the client secret is used to validate ID Tokens -->
      <jaxrsclient:client id="jwkSetClient" threadSafe="true"
      address="https://localhost:9443/fediz-oidc/jwk/keys"
      serviceClass="org.apache.cxf.jaxrs.client.WebClient">
      <jaxrsclient:headers>
      <entry key="Accept" value="application/json"/>
      </jaxrsclient:headers>
      <jaxrsclient:providers>
      <bean class="org.apache.cxf.rs.security.jose.jaxrs.JsonWebKeysProvider"/>
      </jaxrsclient:providers>
      </jaxrsclient:client>

      For being able to use the access token when invoking a REST Backend-Service you must add the access token as an Authorization Header within your Request. For this purpose you must first create a JAX-RS Client and also inject the OIDC-Context to your application.
      <jaxrsclient:client id="backendServiceClient" threadSafe="true"
         address="https://localhost:8082/backendService" serviceClass="org.apache.cxf.jaxrs.client.WebClient">
         <jaxrsclient:headers>
             <entry key="Accept" value="application/json"/>
         </jaxrsclient:headers>
         <jaxrsclient:providers>
            <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"/>
         </jaxrsclient:providers>
      </jaxrsclient:client>
      Within your code you need to inject the OidcClientTokenContext, for example via @Context annotation:
      @Context
      private OidcClientTokenContext oidcContext;
      Within your method when just before invoking the REST Service you must add the authorization header as follows:
      ClientAccessToken accessToken = oidcContext.getToken();
      backendServiceClient.authorization(accessToken);
      Understanding the REST ServiceLets take a DemoService as an example to explain the OIDC/Auth2 Integration at the backend REST Service.First lets take a loog a the REST Interface. You will note an annotation @Scopes which will require that the used access token was provided for the scope userinfo. The other method will also require an access token, but without any scope enforcement.
      import javax.ws.rs.GET;
      import javax.ws.rs.Path;
      import javax.ws.rs.Produces;
      import javax.ws.rs.QueryParam;

      import org.apache.cxf.rs.security.oauth2.filters.Scopes;

      @Path("/")
      public class DemoService {
         
          @GET
          @Path("/public")
          public String getGreeting() {
              return "Hello World!";
          }
         
          @GET
          @Path("/secure")
          @Scopes("userinfo")
          public String getPersonalGreeting(@QueryParam("name") String name) {
              return "Hello " + name + "!";
          }
      }

      Within your Spring configuration you need to add two filters. The OAuthRequestFilter is needed to ensure that the provided access token is still valid, and the OAuthScopesFilter checks that the required scope for invoking a certain method will matches with the scope of the access token.
      <bean id="demoService" class="org.apache.service.DemoService"/>

      <jaxrs:server id="demoService" address="/greeting">
          <jaxrs:serviceBeans>
              <ref bean="demoService"/>
          </jaxrs:serviceBeans>
          <jaxrs:providers>
              <bean class="org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter">
               <property name="tokenValidator">
      <bean class="org.apache.cxf.rs.security.oauth2.filters.AccessTokenValidatorClient">
               <property name="tokenValidatorClient" ref="tokenValidatorClient"/>
           </bean>
               </property>
        </bean>
          <bean class="org.apache.cxf.rs.security.oauth2.filters.OAuthScopesFilter">
              <property name="securedObject" ref="demoService"/>
          </bean>
        </jaxrs:providers>
      </jaxrs:server>

      <jaxrsclient:client id="tokenValidatorClient"
      address="https://localhost:9443/fediz-oidc/oauth2/validate"
      serviceClass="org.apache.cxf.jaxrs.client.WebClient">
      <jaxrsclient:headers>
      <entry key="Content-Type" value="application/x-www-form-urlencoded"/>
      <entry key="Accept" value="application/xml"/>
      </jaxrsclient:headers>
      <jaxrsclient:providers>
      <bean class="org.apache.cxf.jaxrs.provider.FormEncodingProvider">
      <property name="expectedEncoded" value="true"/>
      </bean>
      </jaxrsclient:providers>
      </jaxrsclient:client>
      Since validation is not yet standardized  the tokenValidatorClient communicates with the OIDC service in a proprietary manner.

      Categories: Jan Bernhardt

      Fediz with OpenID Connect Support and WS-Federation Bridge (1/2)

      Wed, 12/09/2015 - 16:53
      I'm currently engaged for a big company to provide a solution that allows this company to offer various (REST) services to their partners while these services are hosted and maintained by the company but users can login to these services with accounts managed within their own partner network.

      This solution should work for Web-Portals, Mobile Apps & Desktop Applications.

      First I was skeptical if it will be possible to find one solution fitting all theses different use cases. But I think I actually did find a very interesting solution. In this post I'll explain the overall architecture of this solution. In my next posts I'll tell you how to get a Liferay Web-Portal integrated as well as a mobile App based on Android.

      WS-Federation normally uses SAML Tokens for user authentication. This is fine for container based security solutions, when the user wants to login to a web-portal. But modern web applications (e.g. AJAX based) tend to be executed primarily in the Browser, invoking REST backend services directly from within the Browser.
      Handling XML based tokens (incl. XML signature validation) is just a too heavy burden for this type of applications. Also handling lifetime issues with SAML Token could require a Token exchange with an STS. But an STS only provides a SOAP interface according to WS-Trust. It is not feasible for a AJAX Web Application to handle SOAP communication including XML security. Browser based applications should be light-weight and thus they prefer talking to REST services.

      Token Types & Token Handling JSON Web Tokens (JWT) are similar to SAML tokens but instead of being XML based they are provided in a JSON format. Since JSON is the preferred document format for REST services, they fit much better to a REST Service compared to a XML based SAML Token.

      One step closer to the solution could be to provide a STS that can issue JWT tokens as well as SAML tokens. This could help to exchange SAML token to JWT token and vice versa in cases when you need both. SAML for SOAP services and JWT for REST services. Colm O hEigeartaigh has already done some improvements to the CXF STS to support JWT tokens at the STS.

      Instead of requesting a SAML Token with the following TokenType http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0 you can now also request a JWT token with this TokenType urn:ietf:params:oauth:token-type:jwt.

      This way you can request or even exchange JWT tokens simply by setting the expected TokenType value. When sending a SAML or JWT token OnBehalfOf your token request you can exchange one format for the other.
      What is currently missing to bridge both token types for SOAP and REST scenarios would be a REST interface for the STS. This would make it a lot easier for REST clients to interact with an STS. Since there are no standards for a STS REST API, I'll design a REST interface by myself discuss it with the CXF community and then add it to the STS soon.

      OpenID ConnectI would see OpenID Connect as the successor standard for WS-Federation which was the successor standard of the SAML Web-Profile. You can see that the OIDC sequence flow is still similar to the prior protocols but that it also learned and improved a lot from the older standards.

      OIDC is also based on the OAuth2 standard and thus inherits also all benefits and drawbacks of this protocol.

      Many big internet companies support OpenID Connect like Google, Facebook, Twitter, etc..
      OpenID Connect is related but not equal to OpenID on which some of the bigger companies dropped already the support for (like Google).
      Simple Sequence FlowOpenID connect is based on OAuth2 and thus provides several sequence flows. Which one you will choose will depend on your circumstances. In this blog I'll focus on the authorization code flow only.

      (1) The user invokes the WebPortal (same would apply if the user invokes a REST Service).
      GET https://demo.portal.com/webPortal/index.html HTTP/1.1
      Host: demo.portal.comto be continued...
      (2) Since there is no existing session with the user, the server response with a redirect to the OIDC Server (line breaks added to improve readability).
      HTTP/1.1 303 See Other
      Location: https://your.oidc-server.com/idp/login
      ?client_id=Ro2hJVtj94oWLw
      &scope=openid
      &response_type=code
      &redirect_uri=https%3A%2F%2Fdemo.portal.com%2FwebPortal%2Foidc
      &state=a8a4ee9e8061a34f93539635ce02e32
      &nonce=1d5c428ffbff3eed95721339e67c56e8c2aa4add6bb493e436249578c81f88
      The user will see a login screen for authentication.
      (3) After successful login the user will be redirected back to the provided redirect_uri with an authorization code:
      HTTP/1.1 303 See Other
      Set-Cookie: JSESSIONID=55638DC5FF717DD730B72978B70F088E; Path=/idp/; Secure; HttpOnly
      Cache-Control: private
      Expires: Thu, 01 Jan 1970 01:00:00 CET
      Date: Thu, 19 Nov 2015 13:39:08 GMT
      Location: https://demo.portal.com/webPortal/oidc
      ?state=a8a4ee9e8061a34f93539635ce02e32
      &code=a1a822cd99da976f47c8a7218ae212
      (4) This code can now be used by the service provider (web portal) to get a JWT token. For this reason the service provider needs to authenticate against the token endpoint with its client_id & client_secret within the Authorization header, as well as the code value and redirect_uri.
      POST https://your.oidc-server.com/idp/oauth2/token
      Host: your.oidc-server.com
      Authorization: Basic Um8yaEpWdGo5NG9XTHc6RUFpemlWUU5PWVpmaHN5WGlSNmpxUQ==

      grant_type=authorization_code
      &code=a1a822cd99da976f47c8a7218ae212
      &redirect_uri=https%3A%2F%2Fdemo.portal.com%2FwebPortal%2Foidc
      As a result the token endpoint of the OIDC IDP will return the JWT Token as well as an access code. The access code will not be needed in this first scenario but is just part of the usual Oauth2 flow.
      HTTP/1.1 200 OK
      Content-Type: application/json

      {
      "access_token": "a8de6a544738c9e9036318ee78dfe7",
      "token_type": "Bearer",
      "expires_in": 3600,
      "scope": "openid",
      "id_token": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJib2IiLCJhdWQiOiJVVUY5RmRzNjdubnA2QSIsImlhdCI6MTQ0OTY3NDMyNywiZXhwIjoxNDQ5NzM0MzI3LCJnaXZlbl9uYW1lIjoiQm9iIiwiZW1haWwiOiJib2J3aW5kc29yQHJlYWxtYS5vcmciLCJmYW1pbHlfbmFtZSI6IldpbmRzb3IiLCJuYW1lIjoiQm9iIFdpbmRzb3IiLCJub25jZSI6IjFkNWM0MjhmZmJmZjNlZWQ5NTcyMTMzOWU2N2M1NmU4YzJhYTRhZGQ2YmI0OTNlNDM2MjQ5NTc4YzgxZjg4IiwiaXNzIjoiYWNjb3VudHMuZmVkaXouY29tIiwiYXRfaGFzaCI6InZvc1h4a1lhcW1aYmJiTWlVdWdYRUEifQ.VQB4QfI1nrka5cfgq8aOGyT0iv3EEE7BBETIzQHbee1t9BDr8wPNV55pGY1YqU6F5C9KIkWiIGLz8MBlKwQVfU7FfzXqm4gEF2zbq4i_LyL-f_RW38-id4VmfF3n6ybWDqdmLLXagFL2UurTdIuGWxbZVW_bgnuIc5dJfnQ_b2wi9hxoL-1u4PPufDLfTLBYRtP5zaedR01me9T5i6PCn2jZSdvfd9304Jf6MOf98Cvv-faeZ87ilUrd5k3q4tQl6dIL3IefPBTfbR8Ni69viD29A-PvtKLquQcGp6p3NEXnwOYF7c72ZsjuZI2pcItoeBeo2Whz9Ni21vfWsd3Iaw"
      }
      The id_token contains three values of interest base64 encoded and separated by a dot '.':
      eyJhbGciOiJSUzI1NiJ9

      eyJzdWIiOiJib2IiLCJhdWQiOiJVVUY5RmRzNjdubnA2QSIsImlhdCI6MTQ0OTY3NDMyNywiZXhwIjoxNDQ5NzM0MzI3LCJnaXZlbl9uYW1lIjoiQm9iIiwiZW1haWwiOiJib2J3aW5kc29yQHJlYWxtYS5vcmciLCJmYW1pbHlfbmFtZSI6IldpbmRzb3IiLCJuYW1lIjoiQm9iIFdpbmRzb3IiLCJub25jZSI6IjFkNWM0MjhmZmJmZjNlZWQ5NTcyMTMzOWU2N2M1NmU4YzJhYTRhZGQ2YmI0OTNlNDM2MjQ5NTc4YzgxZjg4IiwiaXNzIjoiYWNjb3VudHMuZmVkaXouY29tIiwiYXRfaGFzaCI6InZvc1h4a1lhcW1aYmJiTWlVdWdYRUEifQ

      VQB4QfI1nrka5cfgq8aOGyT0iv3EEE7BBETIzQHbee1t9BDr8wPNV55pGY1YqU6F5C9KIkWiIGLz8MBlKwQVfU7FfzXqm4gEF2zbq4i_LyL-f_RW38-id4VmfF3n6ybWDqdmLLXagFL2UurTdIuGWxbZVW_bgnuIc5dJfnQ_b2wi9hxoL-1u4PPufDLfTLBYRtP5zaedR01me9T5i6PCn2jZSdvfd9304Jf6MOf98Cvv-faeZ87ilUrd5k3q4tQl6dIL3IefPBTfbR8Ni69viD29A-PvtKLquQcGp6p3NEXnwOYF7c72ZsjuZI2pcItoeBeo2Whz9Ni21vfWsd3Iaw
      After base64 decoding the first value will tell us which algorithm has been used to secure the ID token:
      {"alg":"RS256"}
      And the second value will contain the actual JWT token:
      {
      "sub": "bob",
      "aud": "UUF9Fds67nnp6A",
      "iat": 1449674327,
      "exp": 1449734327,
      "given_name": "Bob",
      "email": "bobwindsor@realma.org",
      "family_name": "Windsor",
      "name": "Bob Windsor",
      "nonce": "1d5c428ffbff3eed95721339e67c56e8c2aa4add6bb493e436249578c81f88",
      "iss": "accounts.fediz.com",
      "at_hash": "vosXxkYaqmZbbbMiUugXEA"
      }
      The third parameter will contain the digital signature (binary value) thus I cannot display it here.

      After consuming the authorization code the user will be redirected back to the initially requested resource
      HTTP/1.1 303 See Other
      Location: https://demo.portal.com/webPortal/index.html

      OpenID Connect with WS-Federation LoginNow after understanding the basic OpenID Connect flow lets extend this scenario by performing the login not directly at the OIDC service but instead using the Fediz-Plugin at the OIDC Service to redirect the user according to WS-Federation to the Fediz-IDP for user authentication. After successful login at the Fediz-IDP the user will be send back to the OIDC with a SAML token. This token can then be mapped to a JWT token for the OpenID connect login process.


      (1) In the first step the user invokes the web portal.
      (2) Since now existing session exists the user will be redirected to the OIDC Service according to OpenID Connect.
      (3) An Apache Fediz Plugin is active on the OIDC Server. Since now previous session exists the Fediz Plugin will redirect the user to the RP-IDP according to WS-Federation.
      (4) The user will see a login screen and will be redirected back to the OIDC server after successful authentication with a SAML token.
      (5) The Fediz Plugin will store the SAML Token. OIDC will provide the requested code from step (2).
      (6) The Web Portal can now exchange this code according to OAuth2 to get the JWT Token (transformation of the SAML token), as well as an access token to invoke another backend service.
      (7) The REST Service will be invoked with the access token received in the previous step
      GET https://my.services.com/backend/service
      Host: my.services.com
      Authorization: Bearer a8de6a544738c9e9036318ee78dfe7
      (8) The REST Service will send the access token to the OIDC to ensure that this code is valid. (Unfortunately it is not defined within the standard how this validation should happen)

      In my second part of this post, I'll explain how to setup a demonstrator for the above described use case.

      Federated SSO with WS-Federation & OpenID ConnectAfter understanding OpenID Connect and WS-Federation Login I'll extend this scenario by one more degree of complexity thus providing us the flexibility which we discusses at the beginning to provide a SSO solution not just within one company but for multiple partners at the same time.

      (1) A user from a partner company invokes the web portal.
      (2) Since now existing session exists the user will be redirected to the OIDC Service according to OpenID Connect.
      (3) An Apache Fediz Plugin is active on the OIDC Server. Since now previous session exists the Fediz Plugin will redirect the user to the RP-IDP according to WS-Federation.
      (4) Since no existing session exists at the RP-IDP, the RP-IDP will perform a Home-Realm discovery and after that redirects the user to its own Requestor IDP (more details about this in the next section).
      (5) After successful login the Requestor IDP will issue a SAML Token for the user and redirect the user back to the RP-IDP.
      (6) The RP-IDP will to do a Claims or Identity Mapping and after that issue a new SAML token applicable for the OIDC Service and redirects the user back to the OIDC service.
      (7) The Fediz Plugin at the OIDC service will store the SAML Token and OIDC will provide the requested code from step (2).
      (8) The Web Portal can now exchange this code according to OAuth2 to get the JWT Token (transformation of the SAML token), as well as an access token to invoke a backend service.
      (9) The REST Service will be invoked with the access token received in the step (8).
      (10) The REST Service will send the access token to the OIDC to ensure that this code is valid. (Unfortunately it is not defined within the standard how this validation should happen)
      Home Realm DiscoveryIf your web portal shall be used by users from multiple realms (e.g. partner networks), you need to find a way to discover the home realm of each user. The OpenID Connect Standard provides a parameter login_hint for this purpose whereas WS-Federation Standard offers a whr parameter for this purpose.

      There are several ways to do a home realm discovery:
      1. Dedicated URLs
        You can provide different URLs for each user group. This can be done by adding the login_hint parameter directly to the URL which the user is invoking (e.g. link within a different portal which the user usually access first). Also very famous is the usage of different domain names with a reverse proxy in place which adds the login_hint / whr parameter.
      2. E-Mail Address
        If there is just a single page which will be used by all user groups it is also a good choice to ask the user for his/her E-Mail address and then use this as the  the login_hint.The OIDC service could use the domain name of this E-Mail Address to set it as the whr parameter for home realm discovery at the RP-IDP. The RP-IDP would need a mapping table of domain names and Requestor IDP URLs to send the user to its home IDP.
        One disadvantage of this solution is that the user will most likely be required to enter his/her E-Mail address twice. First at the portal to initiate the home realm discovery and later for the login at the Requestor IDP. This is because WS-Federation does not provide a felxible login_hint but instead only a more static whr parameter which will be the same value for all users within one group. You can improve the user experience if you use cookies to remember the selected home realm of a user until a user initiated logout takes place.
      3. IP Range
        IP ranges can also be used for home realm discovery but are usually not a good choice because users will not just login from a company network with a static IP but also from mobile devices with a dynamic IP range.
      4. Miscellaneous
        Any other way to securely identify the home realm of a user would also be acceptable, but will usually require custom development efforts to the services.
      Categories: Jan Bernhardt

      Single Logout with Fediz - WS-Federation

      Fri, 01/30/2015 - 15:25
      WS-Federation is primarily used to achieve Single Sing On (SSO). This raises the challenge how to securely logout from multiple applications once the user is done with his work. To navigate to each application previously used to hit the logout button would be quite inconvenient. Fortunately the WS-Federation standard does not only define how to do single sign on, but also how to do single logout.

      In this blog I'll explain how to setup a demonstrator to show single sing-on as well as single sing-off. Since single sing-off is implemented in CXF Fediz version 1.2, I'm going to use a snapshot build since 1.2 is not yet released.
      First of all we need to download Tomcat 7 since we will deploy our IDP/STS as well as our two demo applications to a tomcat container each. I renamed the tomcat folder of my extracted tomcat zip to:
      • Fediz-IDP
      • Fediz-RP1
      • Fediz-RP2
      Next I opened a terminal within the cxf-fediz source code which I downloaded from github and run maven to build fediz:
      mvn clean installSetup IDPAfter my build was successfull I copied the fediz-idp-sts.war file from cxf-fediz/services/sts/target/ into my Fediz-IDP/webapps/ deployment folder. I also did the same with the fediz-idp.war file from cxf-fediz/services/idp/target/.
      Since the default https fediz port for the IDP and STS is 9443 and also to avoid port collisions with my two other tomcat instances, I need to update the port configuration in my tomcat Fediz-IDP/conf/server.xml. Here I update all ports starting with '8' to start with a '9'.
      <Connector port="9443" protocol="org.apache.coyote.http11.Http11Protocol"
      maxHttpHeaderSize="65536"
      maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
      keystoreFile="idp-ssl-key.jks"
      keystorePass="tompass"
      truststoreFile="idp-ssl-trust.jks"
      truststorePass="ispass"
      truststoreType="JKS"
      clientAuth="want"
      sslProtocol="TLS" />
      To enable SSL for my RP-IDP tomcat I need to provide a keystore as well as a truststore. For demo purposes I will simply copy the java key stores from my fediz build cxf-fediz/services/idp/target/classes/ here I find the file idp-ssl-key.jks as well as idp-ssl-trust.jks which I'll copy to my Fediz-IDP root folder.
      Before you can start Fediz-IDP you also need to get the expected JDBC driver which is by default HyperSQL JDBC driver. You need to download the zip file and then extract all jar files from /hsqldb-2.3.2/hsqldb/lib/ to Fediz-IDP/lib/.
      Now you can start the Fediz-IDP tomcat server via Fediz-IDP/bin/startup.sh.
      To avoid OutOfMemory erros you should add the following settings to your CATALINA_OPTS system environment variable: -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -XX:MaxPermSize=128M
      By default the Fediz IDP has only basic authentication activated for user login. This is done to make it easier to run some system tests. However for single logout HTTP Basic authentication is not recommended, because the browser will cache your user credentials and will automatically sent your credentials to the IDP. So you would have to close all your current browser windows to actually see the login popup again after logout. If you also enable form based authentication in your webapps/fediz-idp/WEB-INF/security-config.xml you will actually see a login form again after your logout action. Here is the sample configuration how to enable form based authentication:
      <security:http use-expressions="true">
      <security:custom-filter after="CHANNEL_FILTER" ref="stsPortFilter" />
      <security:custom-filter after="SERVLET_API_SUPPORT_FILTER" ref="entitlementsEnricher" />
      <security:intercept-url pattern="/FederationMetadata/2007-06/FederationMetadata.xml" access="isAnonymous() or isAuthenticated()" />

      <!-- MUST be http-basic thus systests run fine -->
      <security:form-login />
      <security:http-basic />
      <security:logout delete-cookies="FEDIZ_HOME_REALM,JSESSIONID" invalidate-session="true" />
      </security:http>You can also disable http basic authentication if you want to. But you can also just leave it enabled. In that case you can use both authentication styles. You will see an HTML authentication form if you are requested to login, but you could also provide HTTP-Basic authentication header to login.
      After you updated the IDP configuration you need to restart the IDP tomcat server to apply your changes.
      Setup 1. Demo AppFirst of all we must provide the Fediz plugin dependencies to our RP tomcat container. For this purpose we need to create a fediz subfolder in Fediz-RP1/lib/. Next we extract the content of the tomcat plugin dependencies zip file (cxf-fediz\plugins\tomcat\target\fediz-tomcat-1.2.0-SNAPSHOT-zip-with-dependencies.zip) to the fediz subfolder.
      To make sure that tomcat loads these additional dependencies we must also update the calatina.properties in Fediz-RP1/conf.
      common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar,${catalina.home}/lib/fediz/*.jarFor Fediz-RP1 we will keep all port settings as they are. To keep things simple with the SSL connection we will reuse the idp-ssl-key.jks keystore from the Fediz-IDP and copy this keystore also to Fediz-RP1 root folder. The server.xml file needs to have the following SSL connector to be configured for Fediz-RP1:
      <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
      maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
      keystoreFile="idp-ssl-key.jks"
      keystorePass="tompass"
      clientAuth="false"
      sslProtocol="TLS" />
      Before we start the demo app container, we need to copy the demo app to the webapps folders, which can be found at cxf-fediz/examples/simpleWebapp/target/fedizhelloworld.war.
      Finally we must provide a correct fediz configuration file to the config folder of the demo app container. For this purpose we can copy the demo config file from cxf-fediz/examples/simpleWebapp/src/main/config/fediz_config.xml to Fediz-RP1/conf/.
      To make sure that the SAML tokens issued by the STS can be validated at the RP we must also install the correct STS truststore. This we can do by copying cxf-fediz/services/sts/target/classes/ststrust.jks to Fediz-RP1 root folder.
      Now everything should be in place so that we can start Fediz-RP1.

      We should see no exceptions in the logfiles and we should see the metadata document from the RP at the following URL: https://localhost:8443/fedizhelloworld/FederationMetadata/2007-06/FederationMetadata.xml
      Setup 2. Demo AppThe second demo app will be quite similar to the first. Therefore we can simply copy the Fediz-RP1 folder and rename it to Fediz-RP2. To avoid port collisions, we also need to update some server ports.
      Therefore we will update all ports beginning with a leading '8' and replace it with a leading '7' in the Fediz-RP2/conf/server.xml file.

      Since we are going to start both tomcat container on the same machine (localhost), we must also change the context path of the second demo app. Otherwise both apps would use the same cookies. Thus we need to rename the fedizhelloworld.war file within the Fediz-RP2/webapps/ folder to fedizhelloworld2.war.

      To also make this application known at the IDP, you need to register this application via the IDP REST Interface. You can use SoapUI for example or simply curl from your command line.

      POST https://localhost:9443/fediz-idp/services/rs/applications
      <ns2:application xmlns:ns2="http://org.apache.cxf.fediz/">
           <realm>urn:org:apache:cxf:fediz:fedizhelloworld2</realm>
           <role>ApplicationServiceType</role>
           <serviceDisplayName>Fedizhelloworld</serviceDisplayName>
           <serviceDescription>Web Application to illustrate WS-Federation</serviceDescription>
           <protocol>http://docs.oasis-open.org/wsfed/federation/200706</protocol>
           <tokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</tokenType>
           <lifeTime>3600</lifeTime>
      </ns2:application>
      Next you need to add all claims required for the helloworld application. Since the claim types are already known by the default fedizhelloworld application you only need to add a link between application and claims:

      POST https://localhost:9443/fediz-idp/services/rs/applications/urn%3Aorg%3Aapache%3Acxf%3Afediz%3Afedizhelloworld2/claims 
      <ns2:requestClaim xmlns:ns2="http://org.apache.cxf.fediz/">
      <claimType>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role</claimType>
      <optional>false</optional>
      </ns2:requestClaim>
      <ns2:requestClaim xmlns:ns2="http://org.apache.cxf.fediz/">
      <claimType>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname</claimType>
      <optional>true</optional>
      </ns2:requestClaim>
      <ns2:requestClaim xmlns:ns2="http://org.apache.cxf.fediz/">
      <claimType>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname</claimType>
      <optional>true</optional>
      </ns2:requestClaim>
      <ns2:requestClaim xmlns:ns2="http://org.apache.cxf.fediz/">
      <claimType>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress</claimType>
      <optional>true</optional>
      </ns2:requestClaim>
      Next you need to register this application to a given IDP realm.
      POST https://localhost:9443/fediz-idp/services/rs/idps/urn%3Aorg%3Aapache%3Acxf%3Afediz%3Aidp%3Arealm-A/applications
      <ns2:application xmlns:ns2="http://org.apache.cxf.fediz/">
      <realm>urn:org:apache:cxf:fediz:fedizhelloworld2</realm>
      </ns2:application>
      You can check if your application was registered correctly via GET https://localhost:9443/fediz-idp/services/rs/idps.
       Now the IDP will be able to provide SAML token for the second demo application.
      Test Single Sign-OnTo test if single sign-on is working as expected you can open the following URL in your browser: https://localhost:8443/fedizhelloworld/secure/fedservlet. You should get redirected to the IDP and need to choose Realm-A as your home realm. Next you need to enter your credentials bob:bob.
      You should be redirected back to the fedservlet URL and should see your username, assigned roles as well as  other claims.

      If you now enter https://localhost:7443/fedizhelloworld/secure/fedservlet in your browser you should get redirected to the IDP and then without the need to enter your credentials again the IDP should redirect you back to the demo application.

      Congratulation. Single Sing-on is working!
      Test Single Sign-OffGoal of this blog post was not to achieve single sign-on but rather single sign-off. For this you have two options to trigger single logout:
      1. You can invoke a logout request starting at the demo application:
        https://localhost:8443/fedizhelloworld/secure/logout
      2. You can invoke a logout request directly at the IDP:
        https://localhost:9443/fediz-idp/federation?wa=wsignout1.0
      After you triggered the logout process you will be redirected to a page listing all application which the IDP had previously issued security tokens for. You will also be asked if you really want to logout from all these applications. After you confirmed the logout request, you should see a confirmation page. This page contains the same list of applications as before but this time with a green check maker at the end of each line.

      This image is the key to preform the actual logout for all the remote applications. The image resource URL will point to the logout URL of these applications, and by resolving the image resource in your browser you will also invoke the logout URL off all these applications.

      If you invoke now any of the two applications you should now again be redirected to the login page of the IDP.
      Congratulation. Single Logout is working!
       LimitationsThe WS-Federation standard does not require from any application to provide a "logout image" at the logout URL. This has just shown to be best practice. However if the logout URL of an application does not provide an image, the confirmation page will show a broken image, even thou the logout was most likely successful.

      The Single Logout implementation for Fediz is currently not able to delegate a logout request to the requestors IDP. So for example if the user is not authenticated at realm-a but at realm-b instead, the IDP does not forward the wsingout action to realm-b. Thus the user will only be logged of at applications in realm-a but the user still remains an active session in realm-b.

      Hopefully a global logout will be supported by Fediz in the future as well.
      Categories: Jan Bernhardt

      Understanding WS-Federation - Passive Requestor Profile

      Thu, 12/11/2014 - 10:45
      WS-Federation  is an identity federation specification which makes it possible to setup a SSO federation including multiple security realms. A realm (sometimes also called domain) represents a single unit under security administration or a part in a trust relationship.
      EntitiesWithin the WS-Federation standard the following entities are defined:
      • Relying Party (RP)
        The relying party is a resource (web application or service) which consumes security tokens issued by the Security Token Service.
      • Requestor
        A requestor is a user who wants to access a resource (relying party).
      • Identity Provider (IDP)
        An Identity Provider can act as an authentication service to a requestor (in this case it is also called “Requestor IDP” or “Home-Realm IDP”) as well as an authentication service to a service provider (also called “Relying Party IDP”). If a user tries to access a relying party within his own security domain, the “Requestor IDP” and the “RP-IDP” can be the same IDP instance. An IDP can also be seen as an Web-Frontend (Extension) of an STS.
      • Security Token Service (STS)
        A Security Token Service is a web service that validates user credentials and issues security tokens which can include user attributes (also called claims). The security token can be used at the Relying Party to authenticate the requestor’s identity.
      Passive Requestor ProfileThe “Passive Requestor Protocol”  of the WS-Federation standard deals with web-browser based access of a resource like a web portal or a web application.

      The following figure shows a standard scenario of a web application (Relying Party) which delegates the user authentication to an Identity Provider (IDP) according to the WS Federation standard. This way the web application does not need to implement multiple authentication styles for a user, as well as it allows interacting with users not known within the local security domain. Another benefit of delegating the authentication process is that the IDP can retain a session with the user, so that when a user accesses another web application and is redirected to the IDP again, the IDP does not need to request user credentials again und thus providing a SSO experience for the user.


      The above figure shows a sequence diagram of a user (requestor) accessing a web application with his browser. Since the user was not authentication due to a recent session, the application redirects the user to the IDP for a user login (1). The IDP collects the credentials from the user and uses a Security Token Service (STS) to validate the credentials and also to get a SAML token from the STS (2). The STS itself is connected to a LDAP data store to validate the user credentials and also to retrieve additional information (claims) about the user, e.g. roles. On successful authentication (3) the IDP returns the SAML token issued by the STS (4) to the user and advices (auto-submitting form) the user to send this SAML token to the originally requested web application (5). The IDP takes care of providing a web user interface and handling URL redirects, whereas the STS is responsible for generating SAML Token and validating of user credentials. The web application validates the SAML token (6) and on success returns the desired web page (7).

      The above sample was designed to show a simple use case scenario where the Requestor IDP is equal to the Relying Part IDP. In a more sophisticated scenario the Requestor IDP will not be equal to the Relying Party IDP. In addition to that there is also a Reverse Proxy added to the web application ensuring that the home realm discovery (also see section 2.3.3) is going to work correctly. The resulting access sequence can be seen in the following figure.


      The user enters the public WebApp URL in his browser which leads him to the Reverse Proxy (0). The WebApp has no recent session with the user and therefore does not know the identity of the user. Thus the WebApp redirects the user to its Relying Party IDP (1). The Reverse Proxy detects the redirect to the RP-IDP and adds a home realm parameter for the user (1). This IDP uses this home realm parameter to perform the home realm discovery (3) and thus knowing at which IDP can be redirected to for being authenticated (4). The WS-Federation standard does not define how the home realm discovery should be performed. Multiple options are usually available:
      • User Selection
        A list of known and trusted IDPs is shown to the user. The user selects the IDP at which he wants to be authenticated and is then redirected to that IDP.
      • IP Discovery
        The user will be redirected automatically to another IDP based on his IP address.
      • whr Parameter
        The URL to invoke the RP-IDP contains an additional ‘whr’ parameter to define the IDP name on which the user wants to be redirected to for authentication. The ‘whr’ parameter must be known at the RP-IDP and must either be mapped to an URL or can also already be the URL of another IDP). The ‘whr’ parameter is usually set by a Reverse Proxy or was added (by the user or a provided link) in the URL when initially calling the web application.
      • Custom Discovery
        Any custom logic can be added to the IDP to perform the home realm discovery. The standard is not limited to any predefined behaviour.
      After being redirected to the users home IDP (5) the IDP also has no recent session with the user and thus shows a login form to the user to enter his credentials (6). The user sends his username/password to the IDP, which itself creates an issue request to the STS with the received unsername/password embedded (7). The STS validates the user credentials by using the LDAP. Upon successful authentication the STS retrieves the requested user claims (e.g. roles) from the LDAP (8) and creates a SAML token (9) targeted for the RP-IDP. The Requestor IDP embeds this SAML token inside an auto-submitting (Java Script) web form (10) which is then posted to the RP-IDP (11a). The RP-IDP is now able to use this SAML token to authenticate on behalf of the user against the RP STS (11b) to request a SAML token for the previously requested web application. The RP-STS needs to perform an identity or claim mapping (12) to issue a second SAML token this time applicable for the requested web application (13). The RP-IDP puts this application specific SAML token again in a self-executing HTTP form (14) which is then automatically submitted to the web application via the reverse proxy (15). The Relying Party (the web application) validates the received SAML token by verifying that the issuer certificate of the SAML token is trusted. This should be the case, since the SAML token was issued by its own Relying Party IDP. Additional claims like the user roles can be added to the security context of the web application and thus allowing authorization above authentication.
      Categories: Jan Bernhardt

      Integration Testing for STS Extensions with Jetty

      Thu, 10/16/2014 - 12:04
      Recently I had to develop some extensions (ClaimHandler, Validator) to the CXF STS. My problem at first was, how to write an integration test that proves the correct implementation of my extensions.

      At first I placed my Mockup classes and web config in the src/main folder from my maven project and added the jetty plugin to my pom.xml file. This way I was able to start my REST MockupService simply by typing mvn jetty:run on the console. After starting the service I was able to execute my test classes directly from Eclipse. But this approach did not satisfy me at all, because now I had lots of files in my main project folder, which would not be needed once I build and deploy my STS extensions to another STS installation. Somehow I needed to move all files (Mockup Service, Spring beans.xml, web.xml, etc.) to the test project folder.

      In this post I'll explain how to setup you maven pom file so that you can use Jetty in your integration test phase if your packaging goal is not a war file but a simple jar file instead and all your web configuration and classes are located in your test folder.

      There are two Blogs which I found very helpful to get my use case up and running:
      1. Run Jetty in the Maven life-cycle
      2. End-to-End Client-Server Integration Testing with Maven – Project Setup
      First of all I moved my complete src/main/webapp folder to src/test/webapp as well as src/main/resources to src/test/resources since I needed these files only for testing. The same was true for some of my classes in src/main/java which I moved to src/test/java. Now my src/main/ folder only contained my Java classes which I needed to provide my new STS extension.

      Next I modified my pom.xml file and changed the packaging from war to jar. My goal was to add this maven project later as a dependency to another project which would result as a lib in the STS war archive.
      Allocating dynamic network ports to avoid test failures caused by port collisionsTo get a dynamic port binding for my test services, I added a nice maven helper to my build allocating free ports for me:
      <project>
          . . .
          <build>
              . . .
              <plugins>
                  . . .
                  <plugin>
                      <groupId>org.codehaus.mojo</groupId>
                      <artifactId>build-helper-maven-plugin</artifactId>
                      <version>1.5</version>
                      <executions>
                          <execution>
                              <id>reserve-network-port</id>
                              <goals>
                                  <goal>reserve-network-port</goal>
                              </goals>
                              <phase>process-test-resources</phase>
                              <configuration>
                                  <portNames>
                                      <portName>jettyServerPort</portName>
                                      <portName>jettyServerStopPort</portName>
                                  </portNames>
                              </configuration>
                          </execution>
                      </executions>
                  </plugin>
              <plugins>
          <build>
      <project>
      To ensure that my test classes can know the correct location of my Mock Services I simply set these dynamic values as a system property, which can be picked up in my test class:
      <project>
          . . .
          <build>
              . . .
              <plugins>
                  . . .
                  <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-failsafe-plugin</artifactId>
      <version>2.8.1</version>
      <executions>
      <execution>
      <id>integration-test</id>
      <goals>
      <goal>integration-test</goal>
      </goals>
      <configuration>
      <systemPropertyVariables>
      <service.url>http://localhost:${jettyServerPort}/myRestService</service.url>
      <sts.url>http://localhost:${jettyServerPort}/SecurityTokenService/UT</sts.url>
      </systemPropertyVariables>
      </configuration>
      </execution>
      <execution>
      <id>verify</id>
      <goals>
      <goal>verify</goal>
      </goals>
      </execution>
      </executions>
      </plugin>
              <plugins>
          <build>
      <project>Jetty Plugin ConfigurationAnd here comes the really interesting part of telling Jetty to run with classes and configuration files from the src/test project folder:
      <project>
          . . .
          <build>
              . . .
              <plugins>
                  . . .
                  <plugin>
      <groupId>org.mortbay.jetty</groupId>
      <artifactId>jetty-maven-plugin</artifactId>
      <version>8.1.5.v20120716</version>
      <configuration>
      <systemProperties>
      <systemProperty>
      <name>java.security.auth.login.config</name>
      <value>login.jaas</value>
      </systemProperty>
      <systemProperty>
      <name>service.url</name>
      <value>http://localhost:8080/myRestService</value>
      </systemProperty>
      </systemProperties>
      <scanIntervalSeconds>5</scanIntervalSeconds>
      <webAppConfig>
      <resourceBases>
      <resourceBase>${project.basedir}/src/test/webapp</resourceBase>
      </resourceBases>
      </webAppConfig>
      <useTestScope>true</useTestScope>
      <stopKey>STOP</stopKey>
      <stopPort>${jettyServerStopPort}</stopPort>
      </configuration>
      <executions>
      <execution>
      <id>start-jetty</id>
      <phase>pre-integration-test</phase>
      <goals>
      <goal>start</goal>
      </goals>
      <configuration>
      <scanIntervalSeconds>0</scanIntervalSeconds>
      <daemon>true</daemon>
      <connectors>
      <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
      <port>${jettyServerPort}</port>
      <maxIdleTime>60000</maxIdleTime>
      </connector>
      </connectors>
      <systemProperties>
      <systemProperty>
      <name>java.security.auth.login.config</name>
      <value>login.jaas</value>
      </systemProperty>
      <systemProperty>
      <name>service.url</name>
      <value>http://localhost:${jettyServerPort}/myRestService</value>
      </systemProperty>
      </systemProperties>
      </configuration>
      </execution>
      <execution>
      <id>stop-jetty</id>
      <phase>post-integration-test</phase>
      <goals>
      <goal>stop</goal>
      </goals>
      </execution>
      </executions>
      </plugin>
              <plugins>
          <build>
      <project>
      I tried testing first with version 9.2.3.v20140905 of the Jetty plugin, but for some reasons I could not discover so quickly the SelectChannelConnector would not work as expected. My test would always run at port 8080 instead of a dynamically picked port. Therefore I switched back to version 8.1.5.v20120716 and all was running as desired.
      The most important parts to mention here is to set the resourceBase in the configuration to ${project.basedir}/src/test/webapp as well as setting useTestScope to true.

      Since I still wanted to use the port 8080 when running mvn jetty:run but a dynamic port when running mvn verify, I had to override the port settings accordingly.

      After these changes I was able to build my extensions including the automated integration test, but without any test related code in my src/main/ folder.
      Categories: Jan Bernhardt

      Using the Talend PDP ouside of an OSGi Container

      Tue, 10/14/2014 - 10:05
      In a previous post I've explained how to setup a demo application using SAML token for authentication and XACML for authorization in context of REST services.
      In this blog I'm going to explain how to setup the Talend PDP ouside of the OSGi container in which it is usually located, so that you are able to use the PDP co-located to your demo application in any JavaEE container. This is especially helpful if your application cannot easily being deployed into the Talend runtime, but requires lots of authorization requests. In these cases it will be best to have the PDP co-located with your app, completely avoiding expensive network calls.

      This post continues with the demo application described in my previous post. To follow this post you should first read my other post.

      All you need to do to get the PDP dependencies into your demo application war file is to add the following dependency to your to your pom.xml file:
      <dependency>
          <groupId>org.talend.esb.authorization</groupId>
          <artifactId>tesb-xacml-pdp-rt</artifactId>
          <version>5.4.1</version>
      </dependency>
      This dependency is available to Talend Enterprise Edition only!
      If you do not want to use Talend EE you could also use the HERAS-AF implementation directly. But in this case you will be missing some of the extensions which are only provided by Talend. Standalone PDP Next you can setup the PDP as a REST service, taking XACML requests in and returning XACML responses in your /src/main/webapp/WEB-INF/beans.xml file. In this case the demo service will use the localhost network interface to comunicate with the PDP.
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:cxf="http://cxf.apache.org/core" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="
              http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
              http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
              http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

          <import resource="classpath:META-INF/cxf/cxf.xml" />

          <context:property-placeholder />
          <context:annotation-config />
          <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer" />
          <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer" />

          <jaxrs:server id="services" address="/">
              <jaxrs:serviceBeans>
                  <bean id="helloWorldService" class="org.talend.example.rest.HelloWorld" />
              </jaxrs:serviceBeans>
              <jaxrs:providers>
                  <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
                  <bean id="samlHandler" class="org.apache.cxf.rs.security.saml.SamlHeaderInHandler" />
              </jaxrs:providers>
              <jaxrs:properties>
                  <entry key="ws-security.signature.properties" value="alice.properties" />
              </jaxrs:properties>
              <jaxrs:inInterceptors>
                  <bean class="org.talend.esb.authorization.xacml.rt.pep.CXFXACMLAuthorizingInterceptor" id="XACMLInterceptor">
                      <property name="pdpAddress" value="http://localhost:8080/pdp/authorize" />
                      <property name="requireRoles" value="false" />
                  </bean>
              </jaxrs:inInterceptors>
          </jaxrs:server>

          <bean id="prpBean" class="org.talend.esb.authorization.xacml.pdp.herasaf.FilePolicyRetrievalPoint">
              <property name="policyURL" value="WEB-INF/pdp-policy.xml" />
          </bean>
         
          <bean id="pdpBean" class="org.talend.esb.authorization.xacml.pdp.herasaf.HerasAFPolicyDecisionPoint">
              <property name="policyRetrievalPoint" ref="prpBean" />
          </bean>

          <bean id="pdpServiceBean" class="org.talend.esb.authorization.xacml.pdp.service.PolicyDecisionPointService">
              <property name="policyDecisionPoint" ref="pdpBean" />
          </bean>

          <jaxrs:server address="/pdp/">
              <jaxrs:serviceBeans>
                  <ref bean="pdpServiceBean" />
              </jaxrs:serviceBeans>
          </jaxrs:server>
        
      </beans> 
      In the above sample the PDP uses a file policy retrieval point to load its authorization policies. The policy file could look like this:
      <?xml version="1.0" encoding="UTF-8"?>
      <Policy PolicyId="ExamplePolicy"
                RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:permit-overrides"
                xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os" >
          <Target>
            <Resources>
              <Resource>
                <ResourceMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
                  <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">/hello/echo/Sierra.*</AttributeValue>
                  <ResourceAttributeDesignator DataType="http://www.w3.org/2001/XMLSchema#string"
                                               AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id"/>
                </ResourceMatch>
              </Resource>
            </Resources>
          </Target>
          <Rule RuleId="ExecuteRule" Effect="Permit">
            <Target>
              <Actions>
                <Action>
                  <ActionMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-equal">
                    <AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">GET</AttributeValue>
                    <ActionAttributeDesignator DataType="http://www.w3.org/2001/XMLSchema#string"
                                               AttributeId="urn:oasis:names:tc:xacml:1.0:action:action-id"/>
                  </ActionMatch>
                </Action>
              </Actions>
            </Target>
          </Rule>
        </Policy>
      The above policies only allows GET request to the echo service which are starting with Sierra. This makes it easy to test your PDP endpoint. Calling the first URL should be successfull whereas the second should fail:
      1.  http://localhost:8080/hello/echo/SierraTangoNevada
      2. http://localhost:8080/hello/echo/TangoNevada
      Co-Located SetupTo switch from network connection to direct PDP invocation you need to modify your beans.xml as follows:
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:cxf="http://cxf.apache.org/core" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="
              http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
              http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
              http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

          <import resource="classpath:META-INF/cxf/cxf.xml" />

          <context:property-placeholder />
          <context:annotation-config />
          <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer" />
          <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer" />

          <jaxrs:server id="services" address="/">
              <jaxrs:serviceBeans>
                  <bean id="helloWorldService" class="org.talend.example.rest.HelloWorld" />
              </jaxrs:serviceBeans>
              <jaxrs:providers>
                  <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
                  <bean id="samlHandler" class="org.apache.cxf.rs.security.saml.SamlHeaderInHandler" />
              </jaxrs:providers>
              <jaxrs:properties>
                  <entry key="ws-security.signature.properties" value="alice.properties" />
              </jaxrs:properties>
              <jaxrs:inInterceptors>
                  <bean class="org.talend.esb.authorization.xacml.rt.pep.CXFXACMLAuthorizingInterceptor" id="XACMLInterceptor">
                      <property name="policyDecisionPoint" ref="pdpBean"/>
                      <property name="requireRoles" value="false" />
                  </bean>
              </jaxrs:inInterceptors>
          </jaxrs:server>
         
          <bean id="prpBean" class="org.talend.esb.authorization.xacml.pdp.herasaf.RBACRegistryAtomPolicyRetrievalPoint">
             <property name="registryAtomUrl" value = "http://localhost:8040/services/XacmlRegistryAtom"/>
             <property name="policyCachingStrategy" value ="InMemory"/>
             <property name="cacheConfiguration" value="pdp-ehcache.xml"/>
             <property name="loadPermissionPoliciesOnInit" value="true"/>
             <property name="policyReloadInterval" value="10"/>
             <property name="validatePolicies" value="false"/>
             <property name="authentication" value="NO"/>
             <!--property name="username" value="tesb"/>
             <property name="password" value="tesb"/-->
          </bean>

          <bean id="pdpBean" class="org.talend.esb.authorization.xacml.pdp.herasaf.HerasAFPolicyDecisionPoint">
              <property name="policyRetrievalPoint" ref="prpBean" />
          </bean>

      </beans>
      In the above sample I replaced the file policy retrieval point with the Talend policy store retrieval point, thus being able to fetch authorization policies from a central server location. Since these policies will be cached in the PDP the network overhead will be quite low.I also removed the code to setup a remote accessible PDP endpoint but instead I injected the PDP instance directly to the XACML PEP interceptor of the demo application.
      Categories: Jan Bernhardt

      Identity Federation - Identity Mapping vs. Claim Mapping

      Mon, 10/13/2014 - 16:38
      Identity and claim mapping become important, when the target realm (A) of a web application is not equal to the home realm (B) of the user. In this case a SAML token from the user (issuer B) cannot directly be validated at the web application (trusting issuer A only). The relaying party IDP of the web application (in realm A) is able to exchange the SAML token from the (trusted) home realm (B) into a SAML token applicable for the target realm (A). This exchange can be based either on identity mapping or on claim mapping. In this post I'm going to explain what's the difference between these two mapping styles and when to use which alternative.

      <!-- TODO: add picture here -->
      When the relaying party IDP (A) requests a SAML token on behalf of the user (B) at its STS (A) for a specific application (A), then the relaying party IDP send the SAML token previously received from the users home IDP (B) to its STS (A). The STS validates the SAML token and detects that it was issued from another (trusted) security realm (B). Depending on the STS configuration the new SAML token can be created based on an identity or claim mapping.
      Identity MappingIdentity mapping is usually required if a user is registered in both (target and home realm) security domains with an individual user each. These users can be managed in both security domains independently. They do not need to share the same username, password or other kind of user attributes like assigned roles.

      A good example of this use case can be illustrated by Google and YouTube. Before Google acquired YouTube most users had a normal Google account as well as an independent YouTube account. After the acquisition, Google implemented an identity mapping so that a user with an active Google session (due to previous login) could login to YouTube and Google would ask the user if he/she wants to link both accounts. If the user agreed, he/she was now able to login to Google only, but at the same time he/she could use YouTube without re-authentication and with all settings, playlists, etc. which have been related to their former YouTube account.

      For identity mapping the following constrains apply:
      • Independent user account (including attributes) per security domain
      • Each account is under local administration
      • A mapping needs to be defined between account names
      • No attributes are mapped from one identity to the other
      Claim MappingIn case of claim mapping a user is completely managed and under the control of only one domain. Users do not need to be known and managed in other security domains. This reduces the need for identity synchronization and confidential information like user credentials do not need to be propagated to other security domains.

      When the STS receives a SAML token from another (trusted) security realm it does not need to look up a matching identity in his own security domain, but instead creates a new SAML token valid for the target security realm but with the same username as in the provided SAML token from the other domain. If user specific attributes (claims) are of interest for the target domain, the STS can simply copy these attributes into the newly created SAML token or execute a more complex mapping where original values do not need to match new claims neither in number, name nor value. If such a mapping is required, the STS needs to be configured accordingly. See my other post about how this can be done.

      A simple claim mapping could be implemented in a way that role claims from a SAML token are also included (one to one) in the newly created SAML token. Alternatively a mapping could be defined to map role claims from one domain into different role claims for the target realm.

      For claim mapping the following constrains apply:
      • User account is only available and managed within its home realm
      • No user account information is stored in the target realm
      • If claims are needed in target realm, they need to be mapped accordingly
      Categories: Jan Bernhardt

      STS Claim Mappings using JEXL Scripts

      Wed, 10/01/2014 - 10:17
      Before CXF version 2.7.13 it was quite difficult to use claim mappings in the STS, because CXF did not provide any generic claim mapping solution but instead required custom Java code for each claim mapping. Beginning of version 2.7.13 (not yet released) CXF comes with a JexlClaimsMapper which allows to define claim mappings at configuration time with Java Expression Language (JEXL).
      Also a new feature in CXF which goes hand in hand with the JexlCaimsMapper is a special ClaimUtils class providing methods for common claim handling tasks.

      In this blog I'll write about:
      • How to setup claim mappings in the STS
      • Basic JEXL Claim Handling
      • Several JEXL Claim Mapping Samples
      STS Claim Mapping SetupThe best and fastest way to setup a claim mapping scenario in my opinion is to use the CXF Fediz HelloWorld demonstrator. You can also take a look at my previous blog where I described how to setup the demo app, as well as the IDP and STS. Of course you can omit the Kerberos related steps.

      Once you have the basic HelloWorld Sample up and running, I'll explain how to switch from identity mapping to a claim mapping next.
      IDP Setup First of all you need to tell the IDP in realm B that you need claims for realm A. To do so you must set the requestedClaims property in your idp-realmA bean in idp-config-realmb.xml. Your result should look like the following:
      <beans >
          . . .
          <bean id="idp-realmA" class="org.apache.cxf.fediz.service.idp.model.ServiceConfig">
              <property name="realm" value="urn:org:apache:cxf:fediz:idp:realm-A" />
              <property name="protocol" value="http://docs.oasis-open.org/wsfed/federation/200706" />
              <property name="serviceDisplayName" value="Resource IDP Realm A" />
              <property name="serviceDescription" value="Resource IDP Realm A" />
              <property name="role" value="SecurityTokenServiceType" />
              <property name="tokenType" value="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" />
              <property name="lifeTime" value="3600" />
              <property name="requestedClaims">
                  <util:list>
                      <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
                          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" />
                          <property name="optional" value="false" />
                      </bean>
                      <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
                          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" />
                          <property name="optional" value="false" />
                      </bean>
                      <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
                          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" />
                          <property name="optional" value="false" />
                      </bean>
                      <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
                          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role" />
                          <property name="optional" value="false" />
                      </bean>                                               
                  </util:list>
              </property>
          </bean>
      </beans>

      In your idp-config-realma.xml you need to change the federationType of your trusted-idp-realmB bean from FederateIdentity to FederateClaims:

      Since version 1.2 of Fediz the federationType value has changed to FEDERATE_CLAIMS respectively FEDERATE_IDENTITY<beans >
          . . .
      <bean id="trusted-idp-realmB" class="org.apache.cxf.fediz.service.idp.model.TrustedIDPConfig">
      <property name="realm" value="urn:org:apache:cxf:fediz:idp:realm-B" />
      <property name="cacheTokens" value="true" />
      <property name="url" value="https://localhost:${realmB.port}/fediz-idp-remote/federation" />
      <property name="certificate" value="realmb.cert" />
      <property name="trustType" value="PEER_TRUST" />
      <property name="protocol" value="http://docs.oasis-open.org/wsfed/federation/200706" />
      <property name="federationType" value="FederateClaims" />
      <property name="name" value="REALM B" />
      <property name="description" value="IDP of Realm B" />
      </bean>
      </beans>
      STS Setup In case that your STS pom.xml file does not include the JEXL dependency you can either add this dependency to your STS or alternatively you can just add this library to your Tomcat lib folder.
      <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-jexl</artifactId>
      <version>2.1.1</version>
      </dependency>Next you need to update the STS of your relaying party IDP (Realm-A). Here you need to modify the relationship between REALMA and REALMB. For this scenario it would be sufficient to update only the second releationship in the cxf-transport.xml file, but to make the relationship homogeneous I'll set both relationships type from FederateIdentity to FederateClaims. In addition to that I need to add my claimsMapper bean with the JexlClaimsMapper implementation and set the claimsMapper instead of the identityMapper in my relationships.
      <beans >
          . . .
      <bean id="claimsMapper" class="org.apache.cxf.sts.claims.mapper.JexlClaimsMapper">
      <constructor-arg value="claimMapping.script" />
      </bean>

      <util:list id="relationships">
      <bean class="org.apache.cxf.sts.token.realm.Relationship">
      <property name="sourceRealm" value="REALMA" />
      <property name="targetRealm" value="REALMB" />
      <property name="claimsMapper" ref="claimsMapper" />
      <property name="type" value="FederatedClaims" />
      </bean>
      <bean class="org.apache.cxf.sts.token.realm.Relationship">
      <property name="sourceRealm" value="REALMB" />
      <property name="targetRealm" value="REALMA" />
      <property name="claimsMapper" ref="claimsMapper" />
      <property name="type" value="FederatedClaims" />
      </bean>
      </util:list>
      </beans>
      At last you need to add your claimMapping.script (could be any name you want) to your WEB-INF folder of your STS (or any other location that you chose for your JexlClaimsMapper). The content of the claimMapping.script will be discussed in the following sections.
      Basic JEXL Claim HandlingThe following variables will be available within your custom JEXL script and can be used to determine the outcome of the claim mapping process:
      • sourceClaims
        is a ClaimCollection containing all claims provided from the requester IDP/STS (e.g. REALMB) at which the user was authenticated first (e.g. via username/password).
      • targetClaims
        is a ClaimCollection in which you need to add all claims that you want to be available after the claim mapping for your target application.
      • sourceRealm
        Realm ID of the requester IDP/STS (e.g. REALMB)
      • targetRealm
        Realm ID of the relaying party IDP/STS (e.g. REALMA)
      • claimsParameters
        ClaimsParameter containing additional context information like the STS issuer name and applies to address.
      JEXL also supports registration of custom classes to be used within your script. This makes it possible to transfer complex code to a Java class and thus simplifying your script code. By default the ClaimUtils class will be available via the following syntax: claims:<methodname>(<parameter>...). This util class provides several convenience methods to simplify claim handling like getting claims from a specific type out of a ClaimCollection, mapping claim values, etc. You will find several samples in the following section.

      The last statement in your script should always return the targetClaims!
      Each claim can contain an issuer and an originalIssuer attribute. By calling the

      claims:updateIssuer(targetClaims, "new issuer")

      method you can set your current STS as the issuer of the targetClaims as well as setting the originalIssuer (in your targetClaims) which was the issuer in your sourceClaims. Since the claimsParameters are also available within your JEXL script you can set the current STS issuer name in your claims via claimsParameters.stsProperties.issuer instead of using a static String like "new issuer".
      Claim Mapping SamplesIn this section I'll show you some common scenarios that you might need, when you want to operate your IDP/STS in claim mapping mode.
      Copy AllIf all claims regardless of the type and value shall be copied from the original SAML token into the newly generated token the following expression would be sufficient:
      {
      // Update claim issuer
      targetClaims = claims:updateIssuer(sourceClaims, claimsParameters.stsProperties.issuer);

      // Return all claims
      return targetClaims;
      }
      Copy Roles OnlyA simple JEXL script to copy all roles from the source SAML token which was contained in the onBehalfOf token request to the requested target SAML token could look like the following:
      {
      // Get all role claims
      var roleClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role';
      var roleClaim = claims:get(sourceClaims, roleClaimType);

      // Copy role claims for new token
      targetClaims = claims:add(targetClaims, roleClaim);

      // Update claim issuer
      targetClaims = claims:updateIssuer(targetClaims, claimsParameters.stsProperties.issuer);

      // Return new claims
      return targetClaims;
      }Role Value FilterThe following sample can be used in a scenario when your source role claim contains all role values comma separated in a single claim value and you want the target claim to contain distinct role values which match a specific regular expression (e.g. start with 'ROLE_'):
      {
      // Get role claim
      var roleClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role';
      var roleClaim = claims:get(sourceClaims, roleClaimType);

      // Split multi role values
      roleClaim = claims:multiToSingleValue(roleClaim, ",");

      // Filter role values
      roleClaim = claims:filterValues(roleClaim, "ROLE_.*");

      // Add mapped role claims for new token
      targetClaims = claims:add(targetClaims, roleClaim);

      // Update claim issuer
      targetClaims = claims:updateIssuer(targetClaims, claimsParameters.stsProperties.issuer);

      // Return new claims
      return targetClaims;
      }
      Map Role ClaimsThe following sample shows a role mapping with a custom map (originalRoleName => newRoleName):
      {
      // Role value mapping
      var roleClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role';
      var roleClaim = claims:get(sourceClaims, roleClaimType);
      var roleMappings = { "admin" : "administrator", "manager" : "manager" };
      var mappedRoles = claims:mapValues(roleClaim, roleMappings, false);

      // Add mapped role claims for new token
      targetClaims = claims:add(targetClaims, mappedRoles);

      // Update claim issuer
      targetClaims = claims:updateIssuer(targetClaims, claimsParameters.stsProperties.issuer);

      // Return new claims
      return targetClaims;
      }
      Claim MergesIt is also possible to define script variables to improve readability. The following sample shows a many-too-one claim mapping whereby the first and last name of a person is transformed into a fullname claim, as well as copying all email addresses from the source SAML token:
      {
      // Merge firstname and lastname to fullname claim
      var delimiter = ' ';
      var firstNameClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname';
      var lastNameClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname';
      var fullNameClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name';
      var fullNameClaim = claims:merge(sourceClaims, fullNameClaimType, delimiter, firstNameClaimType, lastNameClaimType);

      // Simple claim copy
      var emailClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mail';
      var emailClaim = claims:get(sourceClaims, emailClaimType);

      // Add fullname claim and email claim for new token
      targetClaims = claims:add(targetClaims, fullNameClaim, emailClaim);

      // Update claim issuer
      targetClaims = claims:updateIssuer(targetClaims, claimsParameters.stsProperties.issuer);

      // Return new claims
      return targetClaims;
      }
      More complex SampleThe following sample shows a more "real life" sample of how roles claims are transferred from one domain to another by changing the role claim type, filtering for app specific roles, mapping one to many and normalizing the result:
      {
          // Get roles
          var sourceRoleClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role';
          var roleClaims = claims:get(sourceClaims, sourceRoleClaimType);

          // Update role claim type
          var targetRoleClaimType = 'http://schemas.mycompany.com/security/authorization/claims/role';
          roleClaims = claims:setType(roleClaims, targetRoleClaimType);
         
          // Normalize role claim values
          roleClaims = claims:singleToMultiValue(roleClaims, ",");

          // Application role filter
          roleClaims = claims:filterValues(roleClaims, 'AppName_[a-zA-Z_]+');

          // Map role claims
          var roleMappings = {
              "AppName_Agent"            : "AppName_User, Agent",
              "AppName_Broker_DE"         : "AppName_User, AppName_Broker",
              "AppName_Partner_Agents"   : "AppName_User, AppName_Partner, External"
              };
          roleClaims = claims:mapValues(roleClaims, roleMappings, false);
          roleClaims = claims:singleToMultiValue(roleClaims, ", ");

          // Remove duplicates
          if (roleClaims != null) {
              var distinctValues = new("java.util.LinkedHashSet", roleClaims.values);
              roleClaims.values.clear();
              roleClaims.values.addAll(distinctValues);
          }

          // Collect claims for new token
          if (roleClaims != null && roleClaims.values.size() > 0) {
              targetClaims = claims:add(targetClaims, roleClaims);
          }

          // Set correct issuer
          targetClaims = claims:updateIssuer(targetClaims, claimsParameters.stsProperties.issuer);

          // Return new claims
          return targetClaims;
      }

      Plain JEXL OnlyIf you do not want to use any util classes in your JEXL script you can also script more complex cases directly within your script. The following sample shall give you just an idea on who this could look like:
      {
      // Role value mapping
      var roleClaimType = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role';
      var roleMappings = { "admin" : "administrator", "manager" : "manager" };

      for (c : sourceClaims) {
      if(c.claimType == roleClaimType) {
      var mappedValues = new("java.util.ArrayList");
      for (v : c.values) {
      v = v.toUpperCase();
      var newValue = roleMappings.get(v);
      if (newValue != null) {
      mappedValues.add(newValue);
      }
      }
      c.values = mappedValues;
      targetClaims.add(c);
      }
      }

      // Set correct issuer
      for (c : targetClaims) {
      if(c.originalIssuer == null) {
      c.originalIssuer = c.issuer;
      }
      c.issuer = claimsParameters.stsProperties.issuer;
      }

      // Return new claims
      return targetClaims;
      }
      Categories: Jan Bernhardt

      Pages