Yubico key (https://www.yubico.com/) is a really smart key to get a strong authentication to login into your e-account (e-bank, web mail, etc...).
Several e-companies includes/are including this features to login.
Contrary to Yubico OTP and JBoss EAP 6 or Wildfly 8, this post implements the 2FA feature.
It means user will pass his password and a token (form yubikey) to authenticate.
Goal : how integrate 2FA with Yubico OTP and an other login module inside JBoss EAP/Wildfly.
First step : Materials
For this example you need several materials :
- an Yubico key (you can buy one https://store.yubico.com/)
- tested with YubiKey NEO-n (out of the box)
- should work with YubiKey Nano and YubiKey Neo
- a JBoss EAP or Wildlfy instance (http://www.redhat.com/en or http://www.wildfly.org/)
- tested with EAP 6.3
- should work with all JBoss EAP 6.X
- should work with all JBoss AS 7.X or Wildfly 8+
This example will check and control the OTP with Yubico Cloud Service, the JBoss instance needs an acces to Internet (HTTP/HTTPS)
Second step : Compile Yubico Java Client
Retrieve the yubico-java-client source code and compile it
You must retrieve my PR patch : Add JACC feature for Form-Based Authentication by gautric · Pull Request #10 · Yubico/yubico-java-client · GitHub
greg@a.net> git clone git@github.com:Yubico/yubico-java-client.git greg@a.net> cd yubico-java-client greg@a.net> mvn clean package .... OMIT ..... [INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary: [INFO] [INFO] Yubico OTP validation client ....................... SUCCESS [ 0.003 s] [INFO] Yubico OTP validation client protocol 1 ............ SUCCESS [ 2.254 s] [INFO] Yubico OTP validation client protocol 2 ............ SUCCESS [ 3.798 s] [INFO] Yubico JAAS module ................................. SUCCESS [ 0.710 s] [INFO] yubico-demo-server ................................. SUCCESS [ 2.782 s] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9.704 s [INFO] Fin
You should get several outputs JAR
the first one is the yubico cloud client and the second one is the Yubico login module (Yubico JAAS Impl)
- Yubico cloud client will control and check the OTP provided by the Yubico Key and will return the authorization.
- Yubico OTP validation client protocol 2 : yubico-validation-client2-<Version>.jar
- Yubico login module provides a Login module implementation to plug into an application or an application server using JAAS API.
- Yubico JAAS module : yubico-jaas-module-<Version>.jar
Third step : Create Yubico JBoss Module
Create an module.xml file
<?xml version="1.0" encoding="UTF-8"?> <!-- ~ JBoss, Home of Professional Open Source. ~ Copyright 2014, Red Hat, Inc., and individual contributors ~ as indicated by the @author tags. See the copyright.txt file in the ~ distribution for a full listing of individual contributors. ~ ~ This is free software; you can redistribute it and/or modify it ~ under the terms of the GNU Lesser General Public License as ~ published by the Free Software Foundation; either version 2.1 of ~ the License, or (at your option) any later version. ~ ~ This software is distributed in the hope that it will be useful, ~ but WITHOUT ANY WARRANTY; without even the implied warranty of ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ~ Lesser General Public License for more details. ~ ~ You should have received a copy of the GNU Lesser General Public ~ License along with this software; if not, write to the Free ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> <module xmlns="urn:jboss:module:1.1" name="com.yubico"> <resources> <resource-root path="yubico-jaas-module-3.0.0-SNAPSHOT.jar"/> <resource-root path="yubico-validation-client2-3.0.0-SNAPSHOT.jar"/> <resource-root path="vt-ldap-3.3.3.jar"/> </resources> <dependencies> <module name="org.picketbox"/> <module name="javax.api"/> <module name="org.slf4j"/> <module name="org.apache.commons.codec" /> <module name="javax.security.jacc.api" /> <!-- Mandatory --> <module name="javax.servlet.api"/> <!-- Mandatory --> <module name="org.jboss.common-core"/> <!-- Mandatory --> </dependencies> </module>
Copy it and jars too into your JBoss Module directory.
$JBOSS_HOME/module/com/yubico/main
NB don't forget to copy this jar into the directory, pick up directly from you .m2 repository
edu.vt.middleware:vt-ldap:3.3.3
Tips : to debug quickly, you can create symbolic link
4th step : Get client Id & Secret Key for your JBoss Instance
To use the Yubico Cloud Service you need to get your own Client Id and Secret Key (aka client Key)
https://upgrade.yubico.com/getapikey/
Usefull for 6th step
5th step : Get PublicId of you Yubico Key
For each of your clients have to provide theirs own PublicId to you
https://demo.yubico.com/start/otp/standard
Use the Identity field as <PublicId>
6th step : Create PublicId --> Login User mapping file
Create a mapping file with all previous PublicId and Login user
greg@a.net> cat ${jboss.server.config.dir}/id2name_textfile.conf yk.<PublicId>.user = <login>
We use this Yubico JAAS option to authenticate the Yubico key with the User Login. An user with an unknown Yubikey Id cannot logon.
7th step : Configure your JBoss Instance Security Domain
Create a new security domain
<security-domain name="2fa-auth" cache-type="default"> <authentication> <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule" flag="required"> <module-option name="usersProperties" value="file://${jboss.server.config.dir}/application-users.properties"/> </login-module> <login-module code="com.yubico.jaas.YubikeyLoginModule" flag="required" module="com.yubico"> <module-option name="clientId" value="12123"/> <module-option name="clientKey" value="U873jhsYT629uuh7gban65+p2Io="/> <!-- client Key aka secret Key --> <module-option name="id2name_textfile" value="${jboss.server.config.dir}/id2name_textfile.conf"/> <module-option name="jacc" value="true"/> <!--jacc option in login module at true to retrieve j_otp field (see 9th step) --> </login-module> </authentication> <mapping> <mapping-module code="SimpleRoles" type="role"> <!-- for example only --> <module-option name="<PublicId>" value="admin"/> </mapping-module> </mapping> </security-domain>
we have to create an mapping module to retrieve all user roles, for this example we use SimpleRoles but you can change it to retrieve from an other data source.
8th step : Configure your application to use yubico-auth
to use the previous "yubico-auth" inject it into your jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?> <jboss-web> <security-domain>2fa-auth</security-domain> </jboss-web>
9th step : Use the Form Auth mechanism
Into your login page, you have to add an extra input form to store the otp field
<form method='post' action='j_security_check'> <input type='text' name='j_username'> <input type='password' name='j_password'> <input type='text' name='j_otp'> <!-- the new field form, jacc option in login module at true to retrieve it (see 7th step) --> </form>
into your web.xml file
<web-app> <security-constraint> <web-resource-collection> <web-resource-name>User Auth</web-resource-name> <url-pattern>/user/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> <role-name>manager</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <realm-name>User Auth</realm-name> <form-login-config> <form-login-page>login.jsp</form-login-page> <form-error-page>error.jsp</form-error-page> </form-login-config> </login-config> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>manager</role-name> </security-role> </web-app>
10th step : Enjoy It & Debug authentication log to check the configuration
14:07:50,155 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Security checking request GET /2fa-demo/user/ 14:07:50,155 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Calling hasUserDataPermission() 14:07:50,156 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1) User data constraint has no restrictions 14:07:50,157 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Calling authenticate() 14:07:50,157 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Save request in session 'QIFjgJB8A9nfOVza+4ea5Rs5' 14:07:50,158 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Failed authenticate() test 14:07:50,158 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) End invoke, caller=null 14:07:54,146 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) Begin invoke, caller=null 14:07:54,146 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Security checking request POST /2fa-demo/user/j_security_check 14:07:54,147 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Authenticating username '<LOGIN>' 14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Initializing YubikeyLoginModule 14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Trying to instantiate com.yubico.jaas.impl.YubikeyToUserMapImpl 14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Begin OTP login 14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Skipping token, not a valid YubiKey OTP (too short, 7 < 32) 14:07:54,152 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) OTP from j_otp token : <XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX> 14:07:54,152 TRACE [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Checking OTP <XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX> 14:07:54,807 TRACE [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) OTP <XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX> verify result : OK 14:07:54,807 INFO [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) OTP verified successfully (YubiKey id <YYYYYYYYYYYY>) 14:07:54,807 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Check if YubiKey <YYYYYYYYYYYY> belongs to user <LOGIN> 14:07:54,808 TRACE [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) In commit() 14:07:54,808 DEBUG [com.yubico.jaas.YubikeyLoginModule] (http-localhost/127.0.0.1:8080-1) Committing principal <YubikeyPrincipal><YYYYYYYYYYYY> 14:07:54,808 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) User: <LOGIN> is authenticated 14:07:54,808 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Authentication of '<LOGIN>' was successful 14:07:54,808 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Redirecting to original '/2fa-demo/user/' 14:07:54,808 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Failed authenticate() test ??/2fa-demo/user/j_security_check 14:07:54,808 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) End invoke, caller=null 14:07:54,810 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) Begin invoke, caller=null 14:07:54,810 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) Restoring principal info from cache 14:07:54,810 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Security checking request GET /2fa-demo/user/ 14:07:54,810 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Calling hasUserDataPermission() 14:07:54,810 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1) User data constraint has no restrictions 14:07:54,810 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Calling authenticate() 14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Restore request from session 'QIFjgJB8A9nfOVza+4ea5Rs5' 14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Authenticated '<LOGIN>' with type 'FORM' 14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Proceed to restored request 14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Calling accessControl() 14:07:54,811 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1) Checking roles GenericPrincipal[<LOGIN>(admin,)] 14:07:54,811 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1) JBWEB000017: User [<LOGIN>] has role [admin] 14:07:54,811 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) hasRole:RealmBase says:true::Authz framework says:true:final=true 14:07:54,811 DEBUG [org.apache.catalina.realm] (http-localhost/127.0.0.1:8080-1) Role found: admin 14:07:54,811 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) hasResourcePermission:RealmBase says:true::Authz framework says:true:final=true 14:07:54,811 DEBUG [org.apache.catalina.authenticator] (http-localhost/127.0.0.1:8080-1) Successfully passed all security constraints 14:07:54,811 TRACE [org.jboss.as.web.security] (http-localhost/127.0.0.1:8080-1) End invoke, caller=null
Into the log trace you can notice severals useful informations :
- <LOGIN_USER> : the login user
- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX : the OTP sent to Yubico Cloud Service
- YYYYYYYYYYYY : the Public Id, the Yubico key user
Useful links :
- http://yubico.github.io/yubico-java-client/
- yubico-java-client/jaas at master · Yubico/yubico-java-client · GitHub
- Authentication Modules - JBoss AS 7.0 - Project Documentation Editor
- JBoss AS7: Security : Custom Login Modules
- http://java.dzone.com/articles/creating-custom-login-modules
- Yubico OTP and JBoss EAP 6 or Wildfly 8
Glossary :
- OTP : On Time Password : http://en.wikipedia.org/wiki/One-time_password
- 2FA : 2 factor authentication : http://en.wikipedia.org/wiki/Two_factor_authentication
Comments