View on GitHub

Single Sign-On for the Web

SPNEGO Authentication

SPNEGO is an authentication technology that is primarily used to provide transparent CAS authentication to browsers running on Windows running under Active Directory domain credentials. There are three actors involved: the client, the CAS server, and the Active Directory Domain Controller/KDC.

1. Client sends CAS:               HTTP GET to CAS  for cas protected page
2. CAS responds:                   HTTP 401 - Access Denied WWW-Authenticate: Negotiate
3. Client sends ticket request:    Kerberos(KRB_TGS_REQ) Requesting ticket for HTTP/cas.example.com@REALM
4. Kerberos KDC responds:          Kerberos(KRB_TGS_REP) Granting ticket for HTTP/cas.example.com@REALM
5. Client sends CAS:               HTTP GET Authorization: Negotiate w/SPNEGO Token
6. CAS responds:                   HTTP 200 - OK WWW-Authenticate w/SPNEGO response + requested page.

The above interaction occurs only for the first request, when there is no CAS ticket-granting ticket associated with the user session. Once CAS grants a ticket-granting ticket, the SPNEGO process will not happen again until the CAS ticket expires.

SPNEGO Requirements

SPNEGO Components

SPNEGO support is enabled by including the following dependency in the Maven WAR overlay:

<dependency>
  <groupId>org.jasig.cas</groupId>
  <artifactId>cas-server-support-spnego</artifactId>
  <version>${cas.version}</version>
</dependency>
JCIFSSpnegoAuthenticationHandler

The authentication handler that provides SPNEGO support in both Kerberos and NTLM flavors. NTLM is disabled by default. Configuration properties:

JCIFSConfig

Configuration helper for JCIFS and the Spring framework. Configuration properties:

SpnegoNegociateCredentialsAction

CAS login Webflow action that begins the SPNEGO authenticaiton process. The action checks the Authorization request header for a suitable value (Negotiate for Kerberos or NTLM). If the check is successful, flow continues to the SpnegoCredentialsAction state; otherwise a 401 (not authorized) response is returned.

SpnegoCredentialsAction

Constructs CAS credentials from the encoded GSSAPI data in the Authorization request header. The standard CAS authentication process proceeds as usual after this step: authentication is attempted with a suitable handler, JCIFSSpnegoAuthenticationHandler in this case. The action also sets response headers accordingly based on whether authentication succeeded or failed.

SPNEGO Configuration

Create SPN Account

Create an Active Directory account for the Service Principal Name (SPN) and record the username and password, which will be used subsequently to configure the JCIFSConfig component.

Create Keytab File

The keytab file enables a trust link between the CAS server and the Key Distribution Center (KDC); an Active Directory domain controller serves the role of KDC in this context. The ktpass tool is used to generate the keytab file, which contains a cryptographic key. Be sure to execute the command from an Active Directory domain controller as administrator.

Test SPN Account

Install and configure MIT Kerberos V on the CAS server host(s). The following sample krb5.conf file may be used as a reference.

[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log
 
[libdefaults]
 ticket_lifetime = 24000
 default_realm = YOUR.REALM.HERE
 default_keytab_name = /home/cas/kerberos/myspnaccount.keytab
 dns_lookup_realm = false
 dns_lookup_kdc = false
 default_tkt_enctypes = rc4-hmac
 default_tgs_enctypes = rc4-hmac
 
[realms]
 YOUR.REALM.HERE = {
  kdc = your.kdc.your.realm.here:88
 }
 
[domain_realm]
 .your.realm.here = YOUR.REALM.HERE
 your.realm.here = YOUR.REALM.HERE

Then verify that your are able to read the keytab file:

klist -k

Then verify that your are able to read the keytab file:

kinit a_user_in_the_realm@YOUR.REALM.HERE
klist

Browser Configuration

CAS Component Configuration

Define two new action states in login-webflow.xml before the viewLoginForm state:

<action-state id="startAuthenticate">
  <evaluate expression="negociateSpnego" />
  <transition on="success" to="spnego" />
</action-state>
 
<action-state id="spnego">
  <evaluate expression="spnego" />
  <transition on="success" to="sendTicketGrantingTicket" />
  <transition on="error" to="viewLoginForm" />
</action-state>

Additionally, two existing states need to be modified: 1. gatewayRequestCheck - replace viewLoginForm with startAuthenticate 2. renewRequestCheck - replace viewLoginForm with startAuthenticate

Add two bean definitions in cas-servlet.xml:

<bean id="negociateSpnego" class="org.jasig.cas.support.spnego.web.flow.SpnegoNegociateCredentialsAction" />
 
<bean id="spnego" class="org.jasig.cas.support.spnego.web.flow.SpnegoCredentialsAction"
      p:centralAuthenticationService-ref="centralAuthenticationService" />

Update deployerConfigContext.xml according to the following template:

<bean id="jcifsConfig"
      class="org.jasig.cas.support.spnego.authentication.handler.support.JCIFSConfig"
      p:jcifsServicePrincipal="HTTP/cas.example.com@EXAMPLE.COM"
      p:kerberosDebug="false"
      p:kerberosRealm="EXAMPLE.COM"
      p:kerberosKdc="172.10.1.10"
      p:loginConf="/path/to/login.conf" />

<bean id="spnegoAuthentication" class="jcifs.spnego.Authentication" />

<bean id="spnegoHandler"
      class="org.jasig.cas.support.spnego.authentication.handler.support.JCIFSSpnegoAuthenticationHandler"
      p:authentication-ref="spnegoAuthentication"
      p:principalWithDomainName="false"
      p:NTLMallowed="true" />

<bean id="spnegoPrincipalResolver"
      class="org.jasig.cas.support.spnego.authentication.principal.SpnegoPrincipalResolver" />

<bean id="authenticationManager"
      class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
  <constructor-arg>
    <map>
      <entry key-ref="spnegoHandler" value-ref="spnegoPrincipalResolver"/>
    </map>
  </constructor-arg>
  <property name="authenticationMetaDataPopulators">
    <list>
      <bean class="org.jasig.cas.authentication.SuccessfulHandlerMetaDataPopulator" />
    </list>
  </property>
</bean>

Provide a JAAS login.conf file in a location that agrees with the loginConf property of the JCIFSConfig bean above.

jcifs.spnego.initiate {
   com.sun.security.auth.module.Krb5LoginModule required storeKey=true useKeyTab=true keyTab="/home/cas/kerberos/myspnaccount.keytab";
};
jcifs.spnego.accept {
   com.sun.security.auth.module.Krb5LoginModule required storeKey=true useKeyTab=true keyTab="/home/cas/kerberos/myspnaccount.keytab";
};