Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Current File(s): conf/authn/spnego-authn-config.xml, views/spnego-unavailable.vm, views/user-prefs.vm, conf/authn/authn.properties(V4.1+)
Format: Properties, Native Spring, Properties (V4.1+)

Table of Contents

Overview

...

This login flow differs from the password-based Kerberos authentication provided by the authn/Password login flow. Where the authn/ Password login flow relies on the password submitted to the IdP, the authn/ SPNEGO login flow consumes a Kerberos ticket provided by the client, and the IdP never sees the password.

...

To use the authn/SPNEGO login flow, you need to install and configure Kerberos on your IdP server first. This includes the creation of a service principal in the Kerberos realm for your service, and usually includes obtaining a keytab file for that principal. A service password may also be used. See "Kerberos Infrastructure" below for more information.

General configuration of Kerberos is outside the scope of the IdP, and not described in detail here, but no native Kerberos libraries beyond Oracle's Java Java’s implementation are required or used.

Enabling Module

...

Configuring and using this feature requires that you first enable the "idp.authn.SPNEGO" module if it isn't already enabled. Systems upgraded from older releases generally come pre-enabled due to the prior state of the configuration tree.

...

The clients' browsers need to be configured to support SPNEGO. See Single sign-on Browser configuration for details.

General Configuration

...

titleV4.0

...

A few simple settings are controlled with conf/authn/authn.properties. The others, including the realms to use, are controlled with conf/authn/spnego-authn-config.xml

...

.

The idp.authn.SPNEGO.

...

titleV4.1+

A few simple settings are controlled with conf/authn/authn.properties. The others, including the realms to use, are controlled with conf/authn/spnego-authn-config.xml.

...

enforceRun property controls the opt-in/enforcing mode (defaults to opt-in, false).

You need to configure the Kerberos service principal(s) you want to use in the shibboleth.authn.SPNEGO.Krb5.Realms bean. A usual configuration involves a single realm and service principal. If your environment contains multiple realms, you may need to configure more than one service principal. They will be tried in sequence when attempting to accept a ticket from the client.

...

Code Block
languagexml
<util:list id="shibboleth.authn.SPNEGO.Krb5.Realms">

    <bean parent="shibboleth.KerberosRealmSettings"
        p:servicePrincipal="HTTP/idp.example.org@DOMAIN_A.COM"
        p:keytab="%{idp.home}/credentials/http_domainA.keytab" />

    <bean parent="shibboleth.KerberosRealmSettings"
        p:servicePrincipal="HTTP/idp.example.org@DOMAIN_B.COM"
        p:password="ihavennokeytab" />

</util:list>

The shibboleth.authn.SPNEGO.ClassifiedMessageMap bean is a map of error messages to classified error conditions. The default mapping supplied allows a couple of errors raised by the external SPNEGO implementation to be passed through so that meaningful error messages specific to SPNEGO can be shown (e.g., on the login form of the Password login flow in a fall-through scenario). Usually, you won't need to change this mapping, but if you want other errors to cause the IdP to "fall through" to other login flows, you would need to map them to the "ReselectFlow" key.

Finally, note that a common deployment model is to enable the SPNEGO and the Password login flows together, requiring that both be enabled:

Code Block
idp.authn.flows=SPNEGO|Password

Kerberos System Configuration

The actual Kerberos configuration is managed in a krb5.conf or krb5.ini file that can be placed in a Finally, note that a common deployment model is to enable the SPNEGO and the Password login flows together, requiring that both be enabled:

Code Block
idp.authn.flows=SPNEGO|Password

Kerberos System Configuration

The actual Kerberos configuration is managed in a krb5.conf or krb5.ini file that can be placed in a number of different locations. On non-Windows servers, using a system-wide configuration in /etc/krb5.conf is generally advised. It's possible to have Java-specific configurations and/or provide the path to a configuration using the system property java.security.krb5.conf, as discussed here. If you want to set the system property java.security.krb5.conf, you should set it using the startup configuration of your Servlet container.

...

The IdP provides a flexible mechanism to help avoid some of these situations, the use of an activation condition bean attached to the login flow's descriptor bean in conf/authn/general-authn.xml (V4.0) or identified with the idp.authn.SPNEGO.activationCondition property (V4.1+).

The SPNEGO protocol itself doesn't provide any way for the IdP to reliably decide whether the browser supports a Kerberos login or not. You need to specify the conditions under which a Kerberos login will be available in your environment, typically based on the client's IP address or some text appearing in the user agent's identifier string. It's possible to implement such a condition in Java, but using a script is usually simpler.

Note

Note: If the activation condition disables the SPNEGO login flow, SPNEGO won't be available during the current login conversation (not even as "extended login flow" on the Password login page). Make sure that your activation condition disabled SPNEGO only if you are sure that the current client doesn't support SPNEGO at all.

...

The following examples assume that the condition property customObject is set to the bean shibboleth.HttpServletRequest.In 4.3 and later you should use shibboleth.HttpServletRequestSupplier and adjust the javascript as described in this link.

Expand
titleWhether client's IP address contains a certain address prefix
Code Block
languagejs
// Default return value.
var activate = false;

// Check the client's IP address.
if (custom.remoteAddrget().getRemoteAddr().startsWith("192.168.42.")) {
    activate = true;
}

// Return the result.
activate;
Expand
titleWhether user agent's identifier string contains certain text
Code Block
languagejs
// Default return value.
var activate = false;

// Check the user agent's identifier string.
var identifier = custom.get().getHeader("User-Agent");
if (identifier != null && identifier.match(/Kerberos/)) {
    activate = true;
}

// Return the result.
activate;
Expand
titleWhether a cookie has a certain value
Code Block
languagejs
// This example assumes that the deployer uses the cookie "_idp_krb_enabled"
// to control the availability of SPNEGO. Any other cookie name could be
// used. (It has no relation with the cookie used for controlling "auto login".)
// If such a cookie is used, the view `views/user-prefs.vm` should be extended
// to allow the user to manage this setting. (By default, this view only
// supports to manage the auto-login setting.)

// Default return value.
var activate = false;

// Check whether the cookie "_idp_krb_enabled" is set.
var cookies = custom.get().getCookies();
if (cookies != null) {
    for (var i = 0; i < cookies.length; i++) {
        var cookie = cookies[i];
        if (cookie.getName() == "_idp_krb_enabled" && cookie.getValue != null && cookie.getValue() == "true") {
            activate = true;
        }
    }
}

// Return the result.
activate;

...

The following example shows a full-featured script including logging. To see the log lines in the IdP's log file, you need to configure a logger with name "shibboleth.authn.SPNEGO.ActivationCondition" and log level "DEBUG" in your log configuration file conf/logback.xml.

...

Expand
Code Block
languagexml
<bean id="shibboleth.SPNEGO.ActivationCondition" parent="shibboleth.Conditions.Scripted" factory-method="inlineScript"
		p:customObject-ref="shibboleth.HttpServletRequestHttpServletRequestSupplier">
    <constructor-arg>
        <value>
            <![CDATA[
                // This script activates SPNEGO if the client is part
                // of the network 192.168.42.0/24, the user agent's
                // identifier string contains the term "Kerberos",
                // or the cookie "_idp_krb_enabled" is set to "true".

                // Create logger object.
(Syntax for				var logger = Java 1.8/Nashorn.)
				var logger = Java.type.type("org.slf4j.LoggerFactory").getLogger("shibboleth.SPNEGO.ActivationCondition");
				// For Java 1.7 do this instead:
				// importPackage(Packages.org.slf4j);
				// logger = LoggerFactory.getLogger("shibboleth.authn.SPNEGO.ActivationCondition");

                // Default return value.
                var activate = false;

                // Make HTTPServletRequest object known as "request".
                var request = custom.get();

                // Check the client's IP address.
                if (request.remoteAddrgetRemoteAddr().startsWith("192.168.42.")) {
                    logger.debug("Activating SPNEGO for client in network 192.168.42.0/24.");
                    activate = true;
                }

                if (!activate) {
                    // Check the user agent's identifier string.
                    var identifier = request.getHeader("User-Agent");
                    if (identifier != null && identifier.match(/Kerberos/)) {
                        logger.debug("Activating SPNEGO for client with term 'Kerberos' in user agent's identifier string.");
                        activate = true;
                    }
                }

                if (!activate) {
                    // Check whether the cookie "_idp_krb_enabled" is set.
                    var cookies = request.getCookies();
                    if (cookies != null) {
                        for (var i = 0; i < cookies.length; i++) {
                            var cookie = cookies[i];
                            if (cookie.getName() == "_idp_krb_enabled" && cookie.getValue != null && cookie.getValue() == "true") {
                                logger.debug("Activating SPNEGO for client having cookie '_idp_krb_enabled' set to 'true'.");
                                activate = true;
                            }
                        }
                    }
                }

                // Return the result.
                activate;
            ]]>
        </value>
    </constructor-arg>
</bean>

...

Expand
Code Block
languagejs
//
// Test the IP Range
//

var ipToLong = function(ip) {
    var components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
    if (components) {
        var iplong = 0;
        var power  = 1;
        for (var i = 4; i >= 1; i -= 1) {
            iplong += power * parseInt(components[i]);
            power *= 256;
        }
        return iplong;
    } else {
        return -1;
    }
};

ipOkFrom = ipToLong("10.208.0.0");
ipOkTo = ipToLong("10.239.255.255");
remIp = ipToLong("10.208.0.1");
if (remIp >= ipOkFrom && remIp <= ipOkTo) {
  activate = true;
} else {
  activate = false;
}

The following boilerplate code might be helpful for you to test your scripts outside of the IdP. (The script creates a mock custom object that can be used to test various functionality.)

...

  • The IdP doesn't start because of a syntax error in the JavaScript code. You won't find any error messages in the IdP's log file (i.e. logs/idp-process.log). In this case, consult the log files of your Java container. There, you should find a javax.should find a javax.script.ScriptException exception telling you more about the problem in one of the logs available.

  • The script produces a runtime error while it is executed. In this case, you will find an error message in the IdP's log telling more about the error. The log line will look similar to this:
    2015-11-13 11:44:13,408 - ERROR [net.shibboleth.idp.profile.logic.ScriptedPredicate:119] - Anonymous Scripted Predicate : Error while executing Predicate script
    <Exception>

  • The script runs successfully, but evaluates to false. This means that the authn/SPNEGO flow won't be available.
    If you added logging to your script and adapted your logging configuration accordingly, as described above, you will find appropriate log messages in the IdP's log.

...

Users can enable or disable the auto-login cookie by visiting a URL at the path "/idp/profile/user/prefs". You can customize the page displayed by modifying the view template in views/user-prefs.vm.

An alternative is to include the authn/SPNEGO login flow as an "extended" login flow within the authn/Password login flow and allow the users to enable auto-login on the Password login page. This is described below.

Extending the Password Login Flow

Note

While this approach remains supported, it relies on a feature likely to be removed in a future version. Using the MFA flow is the preferred approach, but no explicit example of this exists.

If you use the SPNEGO flow in "opt-in" mode, you may want to allow users to login with SPNEGO as an alternative to a Password login. You can make the SPNEGO login option available on the Password login page, and optionally allow users to enable auto-login with SPNEGO at the same time.

Note that SPNEGO as an alternative login method to the Password login is available only if the relying party doesn't request a specific authentication context class that is incompatible with the use of SPNEGO (which would typically be a sign of a poor SP configuration as well). If SPNEGO is not compatible with the requested authentication context class, SPNEGO won't be available on the Password login page. This is checked for you by the default login form template as part of the extended flow feature.

The Extended Flow feature of the Password flow is not specific to the use of SPNEGO and is described more generally in the PasswordAuthnConfiguration / Extended Flow Calling topic. SPNEGO-specific examples are included below.

First, you need to configure SPNEGO as an extended login flow in conf/authn/password-authn-config.xml. Add "SPNEGO" to the bean called shibboleth.authn.Password.ExtendedFlows, and add the auto-login signaling parameter to the bean called shibboleth.authn.Password.ExtendedFlowParameters:

Enabling Extended Flow in conf/authn/password-authn-config.xml

...

languagexml

...

SPNEGO User Interface

The SPNEGO authentication process isn't visible to the user. The communication takes place between the IdP and the browser, without requiring user intervention. If an error occurs, an error page is shown to the user explaining the problem and allowing to return control to the IdP to continue. The default implementation of the view rendering this page uses JavaScript to automatically return to the IdP so that the user doesn't need to do anything.

If Internet Explorer is used and Kerberos is not available, and no activation condition (see above) is configured or doesn't prevent this from happening, the browser may show a login dialog box to the user instead of an error page. Users may be confused and may not be able to recover from this situation, so this case should be avoided by configuring an appropriate activation condition if possible.

The view rendering the error page is named views/spnego-unavailable.vm and can be adapted to your needs.

Attribute Resolution Implications

As the SPNEGO authentication doesn't result in a simple username (e.g. "jdoe") but in a Kerberos Principal name that includes the Kerberos realm (e.g. "jdoe@EXAMPLE.ORG"), your current configuration of the attribute resolution might not work, because it expects a simple username to look up the user in the user directory.

You basically have two options to extend your configuration:

  • Configure subject c14n to transform the Kerberos Principal name into a simple username. This method works if the username that is contained in the Kerberos Principal name more or less identical to the username you want to normalize to. Then, you don't need to adapt the existing attribute resolution configuration.

  • Adapt the configuration of the attribute resolution to additionally support Kerberos Principal names besides simple usernames. Choose this approach if the subject canonicalization doesn't lead to unique usernames (e.g. because you have multiple realms and the usernames are not unique over all realms; or because this canonicalization may affect e-mail addresses used as usernames, too).

The following two subsections describe both approaches.

Configuring Subject Canonicalization

This approach requires that each Kerberos Principal name can be transformed to a unique simple username.

You can configure a transformation in the file conf/c14n/simple-subject-c14n-config.xml. See that feature's documentation for further information about the available options.

For example, the following transformation rule removes the realm "@EXAMPLE.ORG":

subject-c14n-config.xml example removing the realm @EXAMPLE.ORG
Code Block
languagexml
...
    <!-- Apply any regular expression replacement pairs after authentication. -->
    <util:list id="shibboleth.authnc14n.Passwordsimple.ExtendedFlowParameters">
	<value>_shib_idp_SPNEGO_enable_autologin</value>Transforms">
        <bean parent="shibboleth.Pair" p:first="^(.+)@EXAMPLE\.ORG$" p:second="$1" />
    </util:list>

Then, you (usually) will want to adapt the view template view/login.vm. The default view template creates a login button for each extended flow, but doesn't include on option to enable "auto-login". You may adapt the default view by replacing the existing "#foreach" loop by the following code, or you may want to replace it more fully to provide a more appropriate presentation model for the use of SPNEGO in your environment.

Velocity markup to add auto-login checkbox to views/login.vm
#foreach ($extFlow in $extendedAuthenticationFlows)

...
Expand
Code Block

Note: In case you allow your users to use the e-mail address for username/password login, this rule may also apply to such an e-mail address in case the user entered the address in upper case. If this may affect you, please have a look at the next approach.

Adapting the Attribute Resolver Configuration

In this approach, the username is kept as is, but the attribute resolver defines additional attributes from the Kerberos Principal name that can be used in the user directory lookup filter template.

The following example defines the attributes krbPrincipalname and krbDomain that are used in the user directory lookup filter template:

Extract the username and realm from the Kerberos Principal name
Expand
Code Block
languagexml
    <!-- The principal name resulting from the authentication. -->
    <AttributeDefinition id="principalName"
           #if ($authenticationContext.isAcceptable($extFlow) and $extFlow.apply(profileRequestContext))           xsi:type="PrincipalName"
     #if ($extFlow.getId() == 'authn/SPNEGO')                 <div classdependencyOnly="form-element-wrappertrue">
    </AttributeDefinition>
 
    <!-- Extract the simple username from  <div class="form-element-wrapper"the Kerberos Principal name. -->
    <AttributeDefinition id="krbPrincipalname"
              <input type="checkbox" name="_shib_idp_SPNEGO_enable_autologin" value="true"> #springMessageText("idp.login.spnego.enable_autologin", "Enable auto-login")     xsi:type="Mapped"
             </div>            dependencyOnly="true">
      <button class="form-element form-button" type="submit" name="_eventId_$extFlow.getId()" <InputAttributeDefinition ref="principalName" />
 
        <DefaultValue passThru="true"/>
        #springMessageText("idp.login.$extFlow.getId().replace('authn/','')", $extFlow.getId().replace('authn/',''))<ValueMap>
            <ReturnValue>$1</ReturnValue>
      </button>      <SourceValue>(.+)@EXAMPLE.ORG</SourceValue>
        </ValueMap>
    </div>AttributeDefinition>
 
    <!-- Map the Kerberos realm to a domain name. -->
 #end   <AttributeDefinition id="krbDomain"
            #end             #end

The sequence of behavior during the login process is normally as follows:

  1. First, the SPNEGO login flow will be tried. If the user hasn't enabled auto-login, SPNEGO authentication won't run. If the user has enabled auto-login, SPNEGO authentication is run. If it succeeds, the authentication step has finished. If it fails, the Password login flow will be run next (if it's configured as available flow).

  2. If SPNEGO didn't successfully run before, the Password login flow will be run and the login page will be shown.

  3. The user can choose to login with SPNEGO. Optionally, the user can enable the "auto-login" option (if enabled in the view template). If the user has chosen SPNEGO, the SPNEGO login flow will run. This time, the flow ignores the auto-login setting and SPNEGO authentication is run. If it succeeds, the authentication process is typically finished. The "auto-login" cookie is set if the user enabled that option on the Password login page. If SPNEGO fails, the user will be returned to the Password login page.

SPNEGO User Interface

The SPNEGO authentication process isn't visible to the user. The communication takes place between the IdP and the browser, without requiring user intervention. If an error occurs, an error page is shown to the user explaining the problem and allowing to return control to the IdP to continue. The default implementation of the view rendering this page uses JavaScript to automatically return to the IdP so that the user doesn't need to do anything.

If Internet Explorer is used and Kerberos is not available, and no activation condition (see above) is configured or doesn't prevent this from happening, the browser may show a login dialog box to the user instead of an error page. Users may be confused and may not be able to recover from this situation, so this case should be avoided by configuring an appropriate activation condition if possible.

The view rendering the error page is named views/spnego-unavailable.vm and can be adapted to your needs.

Attribute Resolution

As the SPNEGO authentication doesn't result in a simple username (e.g. "jdoe") but in a Kerberos Principal name that includes the Kerberos realm (e.g. "jdoe@EXAMPLE.ORG"), your current configuration of the attribute resolution might not work, because it expects a simple username to look up the user in the user directory.

You basically have two options to extend your configuration:

  • Configure subject c14n to transform the Kerberos Principal name into a simple username. This method works if the username that is contained in the Kerberos Principal name more or less identical to the username you want to normalize to. Then, you don't need to adapt the existing attribute resolution configuration.

  • Adapt the configuration of the attribute resolution to additionally support Kerberos Principal names besides simple usernames. Choose this approach if the subject canonicalization doesn't lead to unique usernames (e.g. because you have multiple realms and the usernames are not unique over all realms; or because this canonicalization may affect e-mail addresses used as usernames, too).

The following two subsections describe both approaches.

Configuring Subject Canonicalization

This approach requires that each Kerberos Principal name can be transformed to a unique simple username.

You can configure a transformation in the file conf/c14n/simple-subject-c14n-config.xml. See that feature's documentation for further information about the available options.

For example, the following transformation rule removes the realm "@EXAMPLE.ORG":

simple-subject-c14n-config.xml example removing the realm @EXAMPLE.ORG
Code Block
languagexml
...
    <!-- Apply any regular expression replacement pairs after authentication. -->
    <util:list id="shibboleth.c14n.simple.Transforms">
        <bean parent="shibboleth.Pair" p:first="^(.+)@EXAMPLE\.ORG$" p:second="$1" />
    </util:list>
...

Note: In case you allow your users to use the e-mail address for username/password login, this rule may also apply to such an e-mail address in case the user entered the address in upper case. If this may affect you, please have a look at the next approach.

Adapting the Attribute Resolver Configuration

In this approach, the username is kept as is, but the attribute resolver defines additional attributes from the Kerberos Principal name that can be used in the user directory lookup filter template.

The following example defines the attributes krbPrincipalname and krbDomain that are used in the user directory lookup filter template:

Extract the username and realm from the Kerberos Principal name
Expand
Code Block
languagexml
    <!-- The principal name resulting from the authentication. -->
    <AttributeDefinition id="principalName"xsi:type="Mapped"
                         dependencyOnly="true">
        <InputAttributeDefinition ref="principalName" />

        <DefaultValue passThru="true"/>
        <ValueMap>
            <ReturnValue>domain_a.com</ReturnValue>
            <SourceValue>(.+)@DOMAIN_A.COM</SourceValue>
        </ValueMap>
        <ValueMap>
            <ReturnValue>domain_b.com</ReturnValue>
            <SourceValue>(.+)@DOMAIN_B.COM</SourceValue>
        </ValueMap>
    </AttributeDefinition>

Then, the user directory lookup filter template needs to be adapted to use the attributes defined above:

Example LDAP DataConnector
Code Block
languagexml
    <DataConnector id="myLDAP" xsi:type="LDAPDirectory"
        ...>
        
        <InputAttributeDefinition ref="krbPrincipalname" />
        <InputAttributeDefinition ref="krbDomain" />
        <FilterTemplate>
            <![CDATA[
                (&(|(sAMAccountName=$resolutionContext.principal)(mail=$resolutionContext.principal)(&(sAMAccountName=$krbPrincipalname.get(0))(msSFU30NisDomain=$krbDomain.get(0))))(objectClass=user))
            ]]>
        </FilterTemplate>

        ...
    </DataConnector>

See the documentation for a detailed description of attribute resolution configuration.

Testing the Availability of SPNEGO in the Browser

If you extend the Password login flow to include SPNEGO as an alternative login method, you may further optimize the password login page to show the SPNEGO login only if it's really available in the browser by using AJAX technology. Although this is fully optional, it may lead to a better user experience.

This section doesn't explain in detail how to do this. The real implementation is left to you as part of the customization of the login page template. But we provide an example of a JSP script that checks whether the browser is capable of providing a Kerberos ticket.

You need to install this script to edit-webapp/spnego-test.jsp and rebuild the WAR file and restart the application container to make it available. This example script initiates a SPNEGO login and returns a status in JSON. You can adapt this scripts to your needs.

JSP script to check whether SPNEGO works
Expand
Code Block
languagexml
<%@ page trimDirectiveWhitespaces="true" %>
<%@ page import="org.apache.commons.codec.binary.Base64" %>
<%@ page import="java.util.Arrays" %>
<%@ page contentType="application/json; charset=UTF-8" %>
<%
String status = "";
final String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader == null || !authorizationHeader.startsWith("Negotiate ")) {
    status = "SPNEGONotAvailable";
    response.addHeader("WWW-Authenticate", "Negotiate");
    response.setStatus(401);
} else {
    // Test for NTLM mechanism request.
    final byte[] headerNTLM = {(byte) 0x4E, (byte) 0x54, (byte) 0x4C, (byte) 0x4D, (byte) 0x53, (byte) 0x53, (byte) 0x50};
    final byte[] gssapiData = Base64.decodeBase64(authorizationHeader.substring(10).getBytes());
    if (Arrays.equals(headerNTLM, Arrays.copyOfRange(gssapiData, 0, 7))) {
        // GSS-API data represents an NTLM mechanism request.
        status xsi:type= "PrincipalNameNTLMUnsupported";
    } else {
        // Assume GSS-API data represents an SPNEGO mechanism request.
 dependencyOnly="true">     </AttributeDefinition>  status = "SPNEGOAvailable";
  <!-- Extract the}
simple username from the Kerberos Principal name. --> response.setStatus(200);
}
%>
{
    <AttributeDefinition id="krbPrincipalnamestatus": "<%= status %>"
}

The login page can send an AJAX query to the URL path /idp/spnego-test.jsp and then evaluate the result:

AJAX example to detect SPENGO using JSP above
Expand
Code Block
languagejs
    ## SPNEGO detection, button fixup
    <script>
        xsi:type="Mapped"
  $( document ).ready(function() {
            // query server and test if browser is ready  dependencyOnly="true">
to support SPNEGO
       <InputAttributeDefinition ref="principalName" />   $.ajax({
       <DefaultValue passThru="true"/>         <ValueMap>url: "/idp/spnego-test.jsp",
            <ReturnValue>$1</ReturnValue>    type: "GET",
       <SourceValue>(.+)@EXAMPLE.ORG</SourceValue>         </ValueMap>
dataType: "json",
   </AttributeDefinition>       <!-- Map the})
Kerberos realm to a domain name. -->     <AttributeDefinition id="krbDomain"
   .done(function( json ) {
                var status     xsi:type="Mapped"= json["status"];
                if         dependencyOnly="true">
        <InputAttributeDefinition ref="principalName" />

        <DefaultValue passThru="true"/>
  (status == "SPNEGOAvailable" ) {
     <ValueMap>             <ReturnValue>domain_a.com</ReturnValue>
            <SourceValue>(.+)@DOMAIN_A.COM</SourceValue>
        </ValueMap>
        <ValueMap>
            <ReturnValue>domain_b.com</ReturnValue>
            <SourceValue>(.+)@DOMAIN_B.COM</SourceValue>
        </ValueMap>
    </AttributeDefinition>

Then, the user directory lookup filter template needs to be adapted to use the attributes defined above:

Example LDAP data connector
Code Block
languagexml
    <DataConnector id="myLDAP" xsi:type="LDAPDirectory"
        ...>
        
        <InputAttributeDefinition ref="krbPrincipalname" />
        <InputAttributeDefinition ref="krbDomain" />
        <FilterTemplate>
            <![CDATA[
                (&(|(sAMAccountName=$resolutionContext.principal)(mail=$resolutionContext.principal)(&(sAMAccountName=$krbPrincipalname.get(0))(msSFU30NisDomain=$krbDomain.get(0))))(objectClass=user))
            ]]>
        </FilterTemplate>

        ...
    </DataConnector>

See the documentation for a detailed description of attribute resolution configuration.

Testing the Availability of SPNEGO in the Browser

If you have extended the Password login flow to include SPNEGO as an alternative login method, as described above, you may further optimize the password login page to show the SPNEGO login only if it's really available in the browser by using AJAX technology. Although this is fully optional, it may lead to a better user experience.

This section doesn't explain in detail how to do this. The real implementation is left to you as part of the customization of the login page template. But we provide an example of a JSP script that checks whether the browser is capable of providing a Kerberos ticket.

You need to install this script to edit-webapp/spnego-test.jsp and rebuild the WAR file and restart the application container to make it available. This example script initiates a SPNEGO login and returns a status in JSON. You can adapt this scripts to your needs.

JSP script to check whether SPNEGO works
Expand
Code Block
languagexml
<%@ page trimDirectiveWhitespaces="true" %>
<%@ page import="org.apache.commons.codec.binary.Base64" %>
<%@ page import="java.util.Arrays" %>
<%@ page contentType="application/json; charset=UTF-8" %>
<%
String status = "";
final String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader == null || !authorizationHeader.startsWith("Negotiate ")) {
    status = "SPNEGONotAvailable";
    response.addHeader("WWW-Authenticate", "Negotiate");
    response.setStatus(401);
} else {
    // Test for NTLM mechanism request.
    final byte[] headerNTLM = {(byte) 0x4E, (byte) 0x54, (byte) 0x4C, (byte) 0x4D, (byte) 0x53, (byte) 0x53, (byte) 0x50};
    final byte[] gssapiData = Base64.decodeBase64(authorizationHeader.substring(10).getBytes());
    if (Arrays.equals(headerNTLM, Arrays.copyOfRange(gssapiData, 0, 7))) {
        // GSS-API data represents an NTLM mechanism request.
        status = "NTLMUnsupported";
    } else {
        // Assume GSS-API data represents an SPNEGO mechanism request.
        status = "SPNEGOAvailable";
    }
    response.setStatus(200);
}
%>
{
    "status": "<%= status %>"
}

The login page can send an AJAX query to the URL path /idp/spnego-test.jsp and then evaluate the result:

AJAX example to detect SPENGO using JSP above
Expand
Code Block
languagejs
    ## SPNEGO detection, button fixup
    <script>
        $( document ).ready(function() {
            // query server and test if browser is ready to support SPNEGO
            $.ajax({
                url: "/idp/spnego-test.jsp",
                type: "GET",
                dataType: "json",
            })
            .done(function( json ) {
                var status = json["status"];
                if (status == "SPNEGOAvailable" ) {
                    // display the SPNEGO functionality to the user
                    $('button[name="_eventId_authn/SPNEGO"]').text('Login with SPNEGO');
                    $('button[name="_eventId_authn/SPNEGO"]').css("display", "inline");
                }
            });
        });
    </script>

Note: This example is not part of the main IdP distribution and is provided without warranty.

Reference

The beans defined in conf/authn/spnego-authn-config.xml follow:

Expand
titleBeans (V4.0)

Bean ID / Type

Default

Description

shibboleth.authn.SPNEGO.externalAuthnPath

String

/Auth/SPNEGO

Servlet-relative path to the SPNEGO external authentication implementation

shibboleth.authn.SPNEGO.externalAuthnPathStrategy

Function<ProfileRequestContext,String>

Optional function that returns the redirection expression to use for the protected resource

shibboleth.authn.SPNEGO.EnforceRun

Boolean

false

Whether to always try to run SPNEGO, independent of the user's auto-login setting (i.e., try to run for all users)

shibboleth.authn.SPNEGO.Krb5.RefreshConfig

Boolean

false

Whether to reload the underlying Kerberos configuration (generally in /etc/krb5.conf) on every login attempt

shibboleth.authn.SPNEGO.Krb5.Realms

Collection<KerberosRealmSettings>

List of service principal names and credentials. At least one is required.

shibboleth.KerberosRealmSettings

KerberosRealmSettings

Parent bean used to define beans of this type

shibboleth.authn.SPNEGO.matchExpression

Pattern

Regular expression to match username against

shibboleth.authn.SPNEGO.ClassifiedMessageMap

Map<String,List<String>>

(see file)

A map between defined error/warning conditions and events and implementation-specific message fragments to map to them

Expand
titleBeans (V4.1+)
The beans defined
   // display the SPNEGO functionality to the user
                    $('button[name="_eventId_authn/SPNEGO"]').text('Login with SPNEGO');
                    $('button[name="_eventId_authn/SPNEGO"]').css("display", "inline");
                }
            });
        });
    </script>

Note: This example is not part of the main IdP distribution and is provided without warranty.

Reference

Expand
titleBeans

Beans definable in in conf/authn/spnego-authn-config.xml follow include:

Bean ID / Type

Default

Description

shibboleth.authn.SPNEGO.externalAuthnPathStrategy

Function<ProfileRequestContext,String>

Optional function that returns the redirection expression to use for the protected resource

shibboleth.authn.SPNEGO.Krb5.Realms

Collection<KerberosRealmSettings>

List of service principal names and credentials. At least one is required.

shibboleth.KerberosRealmSettings

KerberosRealmSettings

Parent bean used to define beans of this type

shibboleth.authn.SPNEGO.ClassifiedMessageMap

Map<String,List<String>>

(see file)

A map between defined error/warning conditions and events and implementation-specific message fragments to map to themRoutes various common events to the ReselectFlow event for fall-through

Optional remapping of exception messages or events into specific Spring Web Flow events.

Expand
titleProperties (V4.1+)

The flow-specific properties usable via authn/authn.properties are:

Name

Default

Description

idp.authn.SPNEGO.externalAuthnPath

/Authn/SPNEGO

Servlet-relative path to the SPNEGO external authentication implementation

idp.authn.SPNEGO.enforceRun

false

Whether to always try to run SPNEGO, independent of the user's auto-login setting (i.e., try to run for all users)

idp.authn.SPNEGO.refreshKrbConfig

false

Whether to reload the underlying Kerberos configuration (generally in /etc/krb5.conf) on every login attempt

idp.authn.SPNEGO.matchExpression

Regular expression to match username against

idp.authn.SPNEGO.cookieName 4.2

_idp_spnego_autologin

Name of cookie used to track auto-login state of client

The general properties configuring this flow via authn/authn.properties are:

Name

Default

Description

idp.authn.SPNEGO.order

1000

Flow priority relative to other enabled login flows (lower is "higher" in priority)

idp.authn.SPNEGO.nonBrowserSupported

false

Whether the flow should handle non-browser request profiles (e.g., ECP)

idp.authn.SPNEGO.passiveAuthenticationSupported

false

Whether the flow allows for passive authentication

idp.authn.SPNEGO.forcedAuthenticationSupported

false

Whether the flow supports forced authentication

idp.authn.SPNEGO.proxyRestrictionsEnforced

%{idp.authn.enforceProxyRestrictions:true}

Whether the flow enforces upstream IdP-imposed restrictions on proxying

idp.authn.SPNEGO.proxyScopingEnforced

false

Whether the flow considers itself to be proxying, and therefore enforces SP-signaled restrictions on proxying

idp.authn.SPNEGO.discoveryRequired

false

Whether to invoke IdP-discovery prior to running flow

idp.authn.SPNEGO.lifetime

%{idp.authn.defaultLifetime:PT1H}

Lifetime of results produced by this flow

idp.authn.SPNEGO.inactivityTimeout

%{idp.authn.defaultTimeout:PT30M}

Inactivity timeout of results produced by this flow

idp.authn.SPNEGO.reuseCondition

shibboleth.Conditions.TRUE

Bean ID of Predicate<ProfileRequestContext> controlling result reuse for SSO

idp.authn.SPNEGO.activationCondition

shibboleth.Conditions.TRUE

Bean ID of Predicate<ProfileRequestContext> determining whether flow is usable for request

idp.authn.SPNEGO.subjectDecorator

Bean ID of BiConsumer<ProfileRequestContext,Subject> for subject customization

idp.authn.SPNEGO.supportedPrincipals

(see below)

Comma-delimited list of protocol-specific Principal strings associated with flow

idp.authn.SPNEGO.addDefaultPrincipals

true

Whether to auto-attach the preceding set of Principal objects to each Subject produced by this flow

As a non-password based flow, the supportedPrincipals property defaults to the following XML:

Code Block
languagexml
<list>
    <bean parent="shibboleth.SAML2AuthnContextClassRef"
        c:classRef="urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos" />
    <bean parent="shibboleth.SAML1AuthenticationMethod"
        c:method="urn:ietf:rfc:1510" />
</list>

In property form, this is expressed as (note the trailing commas):

Code Block
idp.authn.SPNEGO.supportedPrincipals = \
    saml2/urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos, \
    saml1/urn:ietf:rfc:1510
Expand
titleFlow Descriptor XML (V4.1+)

To replace the internally defined flow descriptor bean, the following XML is required:

Code Block
languagexml
<util:list id="shibboleth.AvailableAuthenticationFlows">
 
    <bean p:id="authn/SPNEGO" parent="shibboleth.AuthenticationFlow"
            p:order="%{idp.authn.SPNEGO.order:1000}"
            p:nonBrowserSupported="%{idp.authn.SPNEGO.nonBrowserSupported:false}"
            p:passiveAuthenticationSupported="%{idp.authn.SPNEGO.passiveAuthenticationSupported:false}"
            p:forcedAuthenticationSupported="%{idp.authn.SPNEGO.forcedAuthenticationSupported:false}"
            p:proxyRestrictionsEnforced="%{idp.authn.SPNEGO.proxyRestrictionsEnforced:%{idp.authn.enforceProxyRestrictions:true}}"
            p:proxyScopingEnforced="%{idp.authn.SPNEGO.proxyScopingEnforced:false}"
            p:discoveryRequired="%{idp.authn.SPNEGO.discoveryRequired:false}"
            p:lifetime="%{idp.authn.SPNEGO.lifetime:%{idp.authn.defaultLifetime:PT1H}}"
            p:inactivityTimeout="%{idp.authn.SPNEGO.inactivityTimeout:%{idp.authn.defaultTimeout:PT30M}}"
            p:reuseCondition-ref="#{'%{idp.authn.SPNEGO.reuseCondition:shibboleth.Conditions.TRUE}'.trim()}"
            p:activationCondition-ref="#{'%{idp.authn.SPNEGO.activationCondition:shibboleth.Conditions.TRUE}'.trim()}"
            p:subjectDecorator-ref="#{getObject('%{idp.authn.SPNEGO.subjectDecorator:}'.trim())}">
        <property name="supportedPrincipalsByString">
            <bean parent="shibboleth.CommaDelimStringArray"
                c:_0="#{'%{idp.authn.SPNEGO.supportedPrincipals:}'.trim()}" />
        </property>
    </bean>
 
</util:list>

In older versions and upgraded systems, this list is defined in conf/authn/general-authn.xml. In V4.1+V5, no default version of the list is provided and it may simply be placed in conf/global.xml if needed.

...