ESOE Single Sign-on and Logout Design

Enterprise Sign On Engine Technical Architecture
Written by Bradley Beddoes
September 2006

Architecture design by Bradley Beddoes
Incorporates SAML 2.0, and (L)XACML 2.0 OASIS standards

Contributions by:
Shaun Mangelsdorf
Andre Zitelli

Edited by:
Bradley Beddoes
Shaun Mangelsdorf
Andre Zitelli

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY",
and "OPTIONAL" in this document are to be interpreted as
described in RFC 2119

SSO Servlet

Component Lead Bradley Beddoes
Package com.qut.middleware.esoe.sso.servlet.\*
Type SSOServlet
Implemented Interfaces N/A

The SSO Servlet is a control point for SSO requests. The servlet must conform to the Servlet 2.4 specification http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html, this servlet currently only supports standards compliant web browser user-agents.

The servlet will be used for all requests made to https://esoe.url/sso

The SSO Servlet SHOULD implement the saml-bindings-2.0 HTTP POST Binding as its primary method of communication with the SPEP.

Messages are encoded for use with this binding by encoding XML into an HTML form control and are transmitted using the HTTP POST method. The SPEP generated SAML <AuthnRequest> is form-encoded by applying the base-64 encoding rules to the XML representation of the message and placing the result in a hidden form control within a standard html form. The form control MUST be named SAMLRequest. The SSO Servlet MUST decode this value.

A bean which implements the interface com.qut.middleware.esoe.sso.bean.SSOProcessorData must be passed by the servlet to the SSO Processor, it MUST be initalised by the servlet. This bean MAY contain value of the users session identifier. Incoming requests from the user-agent will provide session identifiers, in this case in the form of cookies. The identity of the cookie to be utilised is configurable, if present its value MUST be stored in com.qut.middleware.esoe.sso.bean.SSOProcessorData.sessionID. Additionally any SAML request which has been recieved MUST be stored in com.qut.middleware.esoe.sso.bean.SSOProcessorData.samlRequestDocument. For each request that enters the servlet an object of type javax.servlet.http.HttpServletRequest is supplied by the container as is an object of type javax.servlet.http.HttpServletResponse. These are to be stored as the value of httpRequest and httpResponse respectively in com.qut.middleware.esoe.sso.bean.SSOProcessorData

Once the servlet now MUST call the SSO Processor execute function.

Responding to the SPEP

The SSO Servlet should determine a valid binding for the SPEP favoring the SAML POST binding.

Messages are encoded for use with the POST binding by encoding com.qut.middleware.esoe.sso.bean.SSOProcessorData.samlResponseDocument into an HTML form control and are transmitted using the HTTP POST method. The generated SAML <Response> is form-encoded by applying the base-64 encoding rules to the XML representation of the message and placing the result in a hidden form control within a standard html form. The form control MUST be named SAMLResponse. A response document should be sent back to the http user-agent. A javascript event should be utilised to POST the document to com.qut.middleware.esoe.sso.bean.SSOProcessorData.responseEndpoint after 2 seconds.

Handled return values and actions

com.qut.middleware.esoe.sso.SSOProcessor.result.SSOGenerationSuccessful

The servlet should respond to the SPEP using its response mechanism.

com.qut.middleware.esoe.sso.SSOProcessor.result.LogoutSuccessful

Should not be handled by this servlet

com.qut.middleware.esoe.sso.SSOProcessor.result.ForcePassiveAuthn

The servlet should now respond to the SPEP using its response mechanism.

com.qut.middleware.esoe.sso.SSOProcessor.result.ForceAuthn

The user-agent MUST be redirected to the configured Authn provider for the user-agent on the ESOE, again currently only supporting standards compliant web browser user-agents, the url (incl post data) being requested at the Authn Filter MUST be attached using a name value pairing of esoeTarget=<url>

Handled exceptions and actions

com.qut.middleware.esoe.sso.exception.InvalidSessionIdentifierException

This exception MUST have all sessionIdentifiers (cookies in this case) stripped from the user-agent.

The servlet should now respond to the SPEP using its response mechanism.

Authentication Authority Processor

Component Lead Bradley Beddoes
Package com.qut.middleware.esoe.sso.impl.\*
Type SSOProcessorImpl
Implemented Interfaces com.qut.middleware.esoe.sso.SSOProcessor
Exceptions InvalidSessionIdentifierException

The SSOProcessorImpl should firstly validate the Request

Any invalidly generated request must create and throw com.qut.middleware.esoe.sso.InvalidRequest

The value of <Issuer> should be retrieved and stored at com.qut.middleware.esoe.sso.bean.SSOProcessorData.descriptorID. The value of AssertionConsumerServiceIndex should be retrieved and stored at com.qut.middleware.esoe.sso.bean.SSOProcessorData.responseEndpointID. This value along with the descriptorID MUST be sent to the [[SPEP processor]|[#SPEP Processor]] to determine the URL of the SPEP endpoint to respond to. The value of com.qut.middleware.esoe.sso.bean.SSOProcessorData.responseEndpoint should be set as the response value. If this operation causes an exception it should be caught and com.qut.middleware.esoe.sso.InvalidRequest created and thrown.

Once this occured the value of <NameIDPolicy> AllowCreate MUST be determined. Once this is the case one of three actions MUST be taken:
  • com.qut.middleware.esoe.sso.bean.SSOProcessorData.sessionID is not null
    • Fall through to additional logic processing
  • If the value is 'True'
    • Return com.qut.middleware.esoe.sso.SSOProcessor.result.ForceAuthn
  • If the value is 'False'
    • A SAML [[Response]|[SAML Document Descriptors#Generating Responses]] MUST be created, the value of <Status>, <StatusCode> MUST be set to urn:oasis:names:tc:SAML:2.0:status:AuthnFailed, the generated Response MUST be stored at com.qut.middleware.esoe.sso.bean.SSOProcessorData.samlResponseDocument.
    • Return com.qut.middleware.esoe.sso.SSOProcessor.result.ForcePassiveAuthn

The SSOProcessorImpl MUST now validate sessionIdentifier against the [[com.qut.middleware.esoe.sessions.Query.queryAuthnSession]|[#Query]] query component to ensure that it has been issued by the ESOE Authn Processor and is currently valid by requesting a com.qut.middleware.esoe.sessions.Principal. Any request that fails to be validated against the local session cache after having presented what it considered to be a valid session identifer MUST result in an com.qut.middleware.esoe.sso.exception.InvalidSessionIdentifierException exception being thrown.

If the value of ForceAuthn in the request is set to 'true' one of three actions MUST be taken
  • The value of IsPassive is set to true AND the principal details indicate they last authenticated more then the configured minimal time in the past (default of 60 seconds)
    • A SAML [[Response]|[SAML Document Descriptors#Generating Responses]] MUST be created, the value of <Status>, <StatusCode> MUST be set to urn:oasis:names:tc:SAML:2.0:status:AuthnFailed, the generated Response MUST be stored at com.qut.middleware.esoe.sso.bean.SSOProcessorData.samlResponseDocument.
    • Return com.qut.middleware.esoe.sso.SSOProcessor.result.ForcePassiveAuthn
  • The value of IsPassive is set to false AND the principal details indicate they last authenticated more then the configured minimal time in the past (default of 60 seconds)
    • Return com.qut.middleware.esoe.sso.SSOProcessor.result.ForceAuthn

If the principal has not previously had an entry recorded for the SPEP being accessed in com.qut.middleware.esoe.sessions.Principal.activeEntities this indentifier should be added to the principal by calling the [[com.qut.middleware.esoe.sessions.Update.updateEntityList]|[#Update]] update component, supplying the sessionIdentifier and descriptorID as values.

A 10 byte SHA1 hash should be generated by the Identity Generator and added to the principal by calling the [[com.qut.middleware.esoe.sessions.Update.updateEntitySessionIdentifierList]|[#Update]] update component, supplying the sessionIdentifier, descriptorID and generated samlAuthnSessionIndex as values.

Details about the com.qut.middleware.esoe.sessions.Principal should now be refreshed by using the valid sessionIdentifer with the [[com.qut.middleware.esoe.sessions.Query.queryAuthnSession]|[#Query]] query component.

If the request has passed all processing logic and deemed to be valid an AuthnStatement must be created by generating a base SAML Statement additionally
AuthnInstant MUST be set to the UTC value of time the assertion was created with no time zone information.
SessionIndex SHOULD be set to the value generated for the current session
SessionNotOnOAfter SHOULD be set to the UTC value of the time the assertion was generated plus the configured time skew allowance (set at default of 60 seconds)
<Subject> MUST be set and MUST contain a <NameID> element. The value of this element SHOULD be set to com.qut.middleware.esoe.sessions.Principal.samlAuthnIdentifier
<AuthnContext> MUST be set and MUST contain an <AuthenticatingAuthority> its value MUST be set to https://esoe.url/logon

The statement MUST be wrapped in a SAML Assertion

The assertion MUST be wrapped in a SAML Response

The completed SAML response should be stored at com.qut.middleware.esoe.sso.bean.SSOProcessorData.samlResponseDocument

The processor should now return control to the SSO Servlet with a value of com.qut.middleware.esoe.sso.SSOProcessor.result.SSOGenerationSuccessful.

Logout Servlet

Component Lead Andre Zitelli
Package com.qut.middleware.esoe.logout.servlet.\*
Type LogoutServlet
Implemented Interfaces N/A

The Logout Servlet is a control point for Logout requests. The servlet must conform to the Servlet 2.4 specification http://jcp.org/aboutJava/communityprocess/final/jsr154/index.html, this servlet currently only supports standards compliant web browser user-agents.

The servlet will be used for all requests made to https://esoe.url/logout

A bean which implements the interface com.qut.middleware.esoe.logout.bean.LogoutProcessorData must be passed by the servlet to the Logout Processor, it MUST be initalised by the servlet. This bean MAY contain value of the users session identifier. Incoming requests from the user-agent will provide session identifiers, in this case in the form of cookies. The identity of the cookie to be utilised is configurable, if present its value MUST be stored in com.qut.middleware.esoe.sso.bean.SSOProcessorData.sessionID. For each request that enters the servlet an object of type javax.servlet.http.HttpServletRequest is supplied by the container as is an object of type javax.servlet.http.HttpServletResponse. These are to be stored as the value of httpRequest and httpResponse respectively in com.qut.middleware.esoe.sso.bean.SSOProcessorData

Once the servlet now MUST call the Logout Processor execute function.

Handled return values and actions

com.qut.middleware.esoe.logout.LogoutProcessor.result.LogoutSuccessful

Once the logout is deemed to be completed the Logout Servlet should determine if 'disablesso' was presented by the user of the system. If it is present the Logout Servlet SHOULD create a cookie with a scope of https://esoe.url/ and named as configured for ESOE disablesso cookie. Its value should simply be set to 'disablesso'. The cookie SHOULD be stored in the response by calling the methods supplied in com.qut.middleware.esoe.logout.bean.LogoutProcessorData.httpResponse

The Logout Servlet must determine where to direct the principal. If the value of esoelogout_response was supplied the implementation MAY choose to redirect the user to location configured for the supplied identifier by setting the URL as the value of com.qut.middleware.esoe.logout.bean.LogoutProcessorData.responseURL. Should the implementation not wish to honor this value or it is not supplied com.qut.middleware.esoe.logout.bean.LogoutProcessorData.responseURL should be set to the configured default for the ESOE.

The user-agent MUST be redirected to the configured URL for successful logout requests for the ESOE.

Handled exceptions and actions

com.qut.middleware.esoe.logout.exception.InvalidSessionIdentifierException

This exception MUST have all sessionIdentifiers (cookies in this case) stripped from the user-agent.

The user-agent MUST be redirected to the configured URL for failed logout requests for the ESOE.

com.qut.middleware.esoe.logout.exception.InvalidLogoutRequestException

The user-agent MUST be redirected to the configured URL for failed logout requests for the ESOE.

Logout Processor

Component Lead Andre Zitelli
Package com.qut.middleware.esoe.logout.impl.\*
Type LogoutProcessorImpl
Implemented Interfaces com.qut.middleware.esoe.sso.SSOProcessor
Exceptions InvalidSessionIdentifierException

The LogoutProcessorImpl operates on a form post event coming from the principal.

The form SHOULD be created with a method of post, a target of https://esoe.url/logout a name of esoesso_logout and autocomplete set to off. The form MUST expose an input field named disablesso, type checkbox. The form creator MAY choose to implement the hidden field esoelogout_response, where the value of this field is a numeric value representing the prefered logout page to respond to as configured for the LogoutProcessorImpl. This allows the ESOE to be utilised for multiple logout pages within an enterprise should the single logout portal not be sufficent.

If the request form is considered to be invalid the LogoutProcessorImpl MUST populate and throw the exception com.qut.middleware.esoe.sso.exception.InvalidLogoutRequestException

The LogoutProcessorImpl MUST validate the com.qut.middleware.esoe.sso.bean.SSOProcessorData.sessionID value against the [[com.qut.middleware.esoe.sessions.Query.queryAuthnSession]|[#Query]] query component to ensure that it has been issued by the ESOE Authn Processor and is currently valid by requesting com.qut.middleware.esoe.sessions.Principal. Any request that fails to be validated against the local session cache after having presented what it considered to be a valid sessionIdentifer MUST result in a com.qut.middleware.esoe.sso.InvalidSessionIdentifierException exception being thrown.

Each descriptorID for the com.qut.middleware.esoe.sessions.Principal.activeEntities list SHOULD be traversed.

For each descriptorID
A request supplying the descriptorID should be sent to the [[SPEP processor]|[#Endpoint Resolver]] endpoints for single logout.

For each effected descriptorID/endPoint combination a LogoutRequest MUST be created by generating a base [[Request]|[SAML Document Descriptors#Generating Requests]] and addtionally specifying:
NotOnOrAfter SHOULD be set to the UTC value of the current system time plus the configured time skew allowance (set at default of 60 seconds)
Reason SHOULD be set to "https://esoe.url/logout"
<NameID> \- This value MUST be set to the principals samlAuthnIdentifier
<SessionIndex> \- This value MUST be set to the sessionIndex of the session being terminated.

For each request generated the injected IDPSSOWS instance single logout service should be called and the generated SAML message and currently processing endpoint supplied. A SAML response document MUST be returned. The SAML response contained should be validated and a status of urn:oasis:names:tc:SAML:2.0:status:Success assured. Where these conditions are not met or an exception is returned by IDPSSOWS a bean implementing com.qut.middleware.esoe.logout.bean.FailedLogout must be created. The SAML request document must be stored at com.qut.middleware.esoe.logout.bean.FailedLogout.requestDocument, the current endpoint at com.qut.middleware.esoe.logout.bean.FailedLogout.endPoint. This bean should be supplied to the injected failed logout monitor.

The session information should be cleaned up on the ESOE by calling [[com.qut.middleware.esoe.sessions.Terminate.terminateSession]|[#Terminate]] terminate component and supplying the session identifier.

The processor should now return control to the Logout Servlet with a value of com.qut.middleware.esoe.sso.LogoutSuccessful.

Failed logout Monitor

The failed logout monitor consistently looks at a repository of com.qut.middleware.esoe.logout.bean.FailedLogout beans. Once it reaches the end of the repository it MUST immediently start at the beginning and work its way back through again attempting to deliver logouts to SPEPs. On startup the monitor is to be created in its own thread, this thread MUST continuously stay active until the shutdown of the system. Should it fail in some way it MUST be automatically recovered by the system.

For each bean stored in the repository the generated document stored at com.qut.middleware.esoe.logout.bean.FailedLogout.requestDocument should be regenerated with updated timestamps and signatures to avoid time problems and the new document re-stored at this location. The injected IDPSSOWS instance single logout service should now be called with com.qut.middleware.esoe.logout.bean.FailedLogout.requestDocument and com.qut.middleware.esoe.logout.bean.FailedLogout.endPoint supplied. A SAML response document MUST be returned. The SAML response contained should be validated and a status of urn:oasis:names:tc:SAML:2.0:status:Success assured. If this is the case the bean is ejected from the local repository.

If this check fails the value of com.qut.middleware.esoe.sso.bean.FailedLogout.TimeStamp should be evaluated. If it is older then the time configured as a maximum (default of 24hrs) the bean is ejected from the local repository.

Where these conditions are not met or an exception is returned by IDPSSOWS the bean should remain in the local repository.

External components will supply additional failed logout to be added to the repository at random intervals. When they are supplied the repository MUST be locked for both read and write access while the new data is added. For each bean placed into the repository the value of com.qut.middleware.esoe.logout.bean.FailedLogout.TimeStamp must be set to the current system time.

Also available in: HTML TXT