A common integration question we hear is how to handle “sign-on” between an external enterprise application and Zimbra? This scenario is when a user has signed-on to an enterprise application (for example, a Customer Relationship Management system) and that application needs to access data stored in the user mailbox hosted on a Zimbra server. To transition from the enterprise application to Zimbra, you could prompt the user to re-enter a username/password but that is not a very seamless experience. To automatically “sign-on” the user as they move between systems requires a trusted third party to “vouch for” or “assert” the user identity.
Zimbra includes a proprietary protocol for achieving this assertion, which is referred to as “Preauth“. Preauth works by having a key that is shared between a third party application/system and Zimbra. The third party specifies the userid, a timestamp, optionally an expiration time, and an SHA-1 HMAC value computed over that data using the shared key. The Zimbra server, after successfully validating the HMAC value received in the request, redirects the user to the target Zimbra service.
There is also an alternative: SAML. SAML (Security Assertion Markup Language) can be thought of as a standard way of achieving what Zimbra Preauth protocol does.
SAML is a standard that defines an XML-based framework for exchange of security information between various business services (see http://saml.xml.org/saml-technical-overview). It is a widely adopted standard for Single Sign-On (SSO) and Federated Identity use cases. So one can envision that in scenarios such as above, a SAML assertion could be issued for the user by a central/trusted Identity Management System within the enterprise, and that assertion could be used to allow access to Zimbra. A SAML assertion typically contains security information about a subject/principal and could, for example, convey information like “This user is John Doe, he has an email address of john.doe@example.com, he was authenticated into this system using a password mechanism, at this time and date, etc”.
Example Zimbra SAML Interactions
SAML interactions/exchanges take place between entities referred to as the SAML asserting party and the SAML relying party. An asserting party is an entity that creates/issues SAML assertions. It is also sometimes called a SAML authority. A relying party is an entity that uses assertions it has received.
The picture below illustrates a setup where a user accesses a Zimbra service using a SAML assertion without any Zimbra-proprietary authentication token/cookie or Preauth. In this configuration, the Zimbra server acts as the SAML relying party.
Here’s a brief description of the interactions happening in this setup:
- User authenticates and requests a SAML assertion from the SAML authority. [Note: This step happens outside Zimbra]
- The SAML authority makes sure that the user has been authenticated (by some means) and then issues a SAML assertion for the user. [Note: This step happens outside Zimbra]
- The user’s client sends a SOAP request containing an assertion identifier to the Zimbra server:
<soap:Envelope xmlns:soap='http://www.w3.org/2003/05/soap-envelope'> <soap:Header> <context xmlns='urn:zimbra'> <authToken type='SAML_AUTH_PROVIDER'>b07b804c-7c29-ea16-7300-4f3d6f7928ac</authToken> </context> </soap:Header> <soap:Body> <SomeRequest xmlns='urn:zimbraMail'>…</SomeRequest> </soap:Body> </soap:Envelope>
[Note: The client could have alternatively sent the SAML assertion itself inside the request. Also, we are designating the auth token type as SAML_AUTH_PROVIDER, which is a custom authentication provider you must create for your environment. Later in this blog, we describe how to use Zimbra Server Extensions to implement a custom SAML Auth Provider.]
- Zimbra server calls the SAML Authority and inquires about the assertion by passing the identifier:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Body> <samlp:AssertionIDRequest ID="id-1268148042731" Version="2.0" ... > <samlp:AssertionIDRef>b07b804c-7c29-ea16-7300-4f3d6f7928ac</samlp:AssertionIDRef> </samlp:AssertionIDRequest> </soap:Body> </soap:Envelope>
- The SAML Authority trusts the Zimbra server (relying party) and looks up its store of issued assertions and responds with the assertion:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Body> <samlp:Response ID="id-1268148042741" ... > <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> </samlp:Status> <saml:Assertion ID="b07b804c-7c29-ea16-7300-4f3d6f7928ac" ... > <saml:Issuer>http://samlAuthority</saml:Issuer> <saml:Subject> <saml:NameID>user1@domain.com</saml:NameID> <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:sender-vouches"/> </saml:Subject> <saml:Conditions NotBefore="2010-03-09T09:22:05Z" NotOnOrAfter="2020-12-05T09:27:05Z"> <saml:AudienceRestriction> <saml:Audience>http://zimbra</saml:Audience> </saml:AudienceRestriction> </saml:Conditions> <saml:AuthnStatement AuthnInstant="2010-03-09T09:22:00Z"> <saml:AuthnContext> <saml:AuthnContextClassRef>...SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef> </saml:AuthnContext> </saml:AuthnStatement> </saml:Assertion> </samlp:Response> </soap:Body> </soap:Envelope>
- Based on its trust on the SAML authority, the Zimbra server validates the SAML assertion and sends back a response to the client:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope"> <soap:Header>…</soap:Header> <soap:Body> <SomeResponse xmlns="urn:zimbraMail">…</SomeResponse> </soap:Body> </soap:Envelope>
Note: The sample messages shown above are very simplistic. In the real world, the message exchanges, especially those between the relying party and the SAML authority, are secured using TLS and/or message-level security mechanisms.
Implementing the SAML Auth Provider
The Zimbra server provides various extension points to allow extending the Zimbra server functionality (see Extending Zimbra with Server Extensions). One such extension point is the com.zimbra.cs.service.AuthProvider
abstract class and an abstract sub-class the ZimbraAuthProvider
. These classes know how to process/validate the Zimbra proprietary authentication tokens (or cookies).
To support SAML assertions (or tokens) within Zimbra, you can write a SamlAuthProvider
class that extends AuthProvider
:
public class SamlAuthProvider extends AuthProvider { protected SamlAuthProvider() { super("SAML_AUTH_PROVIDER"); } protected AuthToken authToken(Element soapCtxt, Map engineCtxt) throws AuthProviderException, AuthTokenException { // Extract SAML assertion identifier from soap context // Call SAML authority and request for the SAML assertion corresponding to the identifier // Validate and return the SAML assertion (token) } ... }
The SamlAuthProvider
can be registered with the Zimbra server by doing the following inside the ZimbraExtension.init()
method:
AuthProvider.register(new SamlAuthProvider()); AuthProvider.refresh();
Additionally, the “zimbra_auth_provider” localconfig property needs to be modified (using the Zimbra Command Line Interface) to add your custom saml auth provider to the list of registered “zimbra auth providers”:
zmlocalconfig -e zimbra_auth_provider=SAML_AUTH_PROVIDER,Zimbra
As you can see, this is a powerful mechanism for extending Zimbra to support SAML and create a seamless Single-Sign-On experience. Although the AuthProvider
class is not officially supported yet in a production environment yet, it is fine to use it in a proof of concept or an experimental project.
Any chance of getting a how-to on getting this to work with a Shibboleth SAML IdP?
The intent of this blog was to communicate the fact that Zimbra has a framework that could be employed to write a SAML server-extension that knows how to process SAML assertions, to enable SSO into Zimbra. If such a server-extension is developed I guess it should work with Shibboleth SAML IdP also.
In continuation to my previous comment..
I am not an expert on shibboleth, but from what I know, to shibboleth-enable an application a shibboleth daemon and a web server module need to be installed on the application side. The daemon talks to the shibboleth IdP, and the web server module acts as an interface between the daemon and the application by passing along user attributes obtained from the SAML assertion (issued by the IdP) or possibly the complete SAML assertion itself. A typical Zimbra install does not include a web server, so one would have to first put (say) an apache proxy in front of Zimbra and then install the shibboleth SP component there and configure it to pass along the SAML assertion obtained from the shibboleth IdP. After that the idea of implementing the SamlAuthProvider Zimbra server-extension would come into the picture to validate/process the assertion.
Vishal,
It appears that you’ve simply reimplemented Zimbra authentication in terms of SAML 2 protocols – that’s “SAML 2 protocols”, mind you, not even “SAML 2 bindings”. This is all well and good, but it’s not the way people want to use SAML 2. They’re interested in using the SAML 2 “Web Browser SSO Profile” (See section 4.1 of the saml-profile document) to SSO into Zimbra from their corporate portal. I’ve looked at your example code – you at least need an assertion consumer service, and then you need to cache security context locally, rather than go ask the SAML authority every time someone hits the zimbra server. I’d recommend incorporating something like spring security 3 saml extensions into the zimbra server to handle building a saml-based security context on the servlet request. Then your SAML security provider could simply use the credentials on the request context to build a zimbra auth token for the request.
John Calcote
Sr. Software Engineer
Novell, Inc.
Agreed. The approach used by example code is for scenarios where one needs to access Zimbra’s SOAP/REST API. A web browser SSO profile implementation would require a different approach, but one should be able to implement it within Zimbra’s server extension framework (e.g. see Custom HTTP Handlers section in http://blog.zimbra.com/blog/archives/2010/04/extending-zimbra-with-server-extensions.html).