Setting up single sign-on with JWT (JSON Web Token)

Single sign-on is a mechanism that allows you to authenticate users in your systems and subsequently tell Zendesk that the user has been authenticated. The user is then allowed to access Zendesk without being prompted to enter separate login credentials.

At the core of single sign-on is a security mechanism that allows Zendesk to trust the login requests it gets from your systems. Zendesk only grants access to the users that have been authenticated by you. Zendesk SSO relies on a technology called JSON Web Token (JWT) for securing the exchange of user authentication data.

Note: Before April 29, 2013, single sign-on was provided by Zendesk Remote Authentication. This has been deprecated and JWT is the new solution for single sign-on (read the announcement here). Plus and Enterprise accounts may also choose to implement single sign-on using SAML (see Introducing SAML for Zendesk). The Zendesk Remote Authentication documentation is archived and is now available here: Setting up single sign-on with Zendesk Remote Authentication (Deprecated) [PDF].

JWT implementation code examples

The actual JWT implementation is straight forward and most modern languages have libraries available. Zendesk provides a series of examples for various stacks in the following JWT SSO GitHub repository:

If you implement JWT in any other stack, we would love to feature an example of that there as well. Add a comment to this article to share what you've implemented.

In case you run IIS/AD and don't want to build your own .NET solution, we provide a full implementation in classic ASP, which requires you to adjust only a couple of variables. Download the ASP authentication script from this page on Github: https://github.com/zendesk/zendesk_jwt_sso_examples/tree/master/bundles.

The single sign-on authentication process

Once you enable single sign-on, login requests are routed to a remote login URL (a login page that is external to your Zendesk).

Here are the steps of the single sign-on authentication process:

  1. An unauthenticated user (not already logged in) navigates to your Zendesk URL (for example, https://mycompany.zendesk.com/).
  2. The Zendesk SSO mechanism recognizes that SSO is enabled and that the user is not authenticated.
  3. The user is redirected to the remote login URL configured for the SSO settings (for example, https://mycompany.com/zendesk/sso).
  4. A script on your side authenticates the user using your proprietary login process.
  5. Your script builds a JWT request that contains the relevant user data.
  6. You redirect the customer to the Zendesk endpoint at https://mycompany.zendesk.com/access/jwt with the JWT payload.
  7. Zendesk parses the user detail from the JWT payload and then grants the user a session.

As you can see, this process relies on browser redirects and passing signed messages using JWT. The redirects happen entirely in the browser and there is no direct connection between Zendesk and your systems, so you can keep your authentication scripts safely behind your corporate firewall.

Configuring your JWT implementation

If you're upgrading from an earlier version of Zendesk SSO (referred to as Zendesk Remote Authentication) to JWT, it's okay to have multiple SSO implementations enabled at the same time. Zendesk recognizes if a request is meant for JWT or another type of SSO and will handle the request accordingly. This means that you can enable and test JWT before deactivating your previous SSO implementation.

To perform SSO for a user, you need to send several required user attributes to Zendesk as a hash (hash table, dictionary). Most importantly, Zendesk requires an email address to uniquely identify the user. Beyond the required attributes, which are shown in the table below, you may optionally send additional user profile data. This data is synced between your user management system and your Zendesk.

Redirect the user along with the JWT payload to the following Zendesk endpoint: https://mycompany.zendesk.com/access/jwt. See Anatomy of a JWT request.

The JWT payload must be sent to your Zendesk subdomain using the https protocol. Example: https://example.zendesk.com/access/jwt?jwt={payload}. Host-mapped subdomains are not supported.

Table 1. Supported attributes
AttributeMandatoryDescription
iat Yes Issued At. The time the token was generated, this is used to help ensure that a given token gets used shortly after it's generated. The value must be the number of seconds since UNIX epoch. Zendesk allows up to two minutes clock skew, so make sure to configure NNTP or similar on your servers.
jti Yes JSON Web Token ID. A unique id for the token, used by Zendesk to prevent token replay attacks.
email Yes Email of the user being signed in, used to uniquely identify the user record in Zendesk.
name Yes The name of this user. The user in Zendesk will be created or updated in accordance with this.
external_id No If your users are uniquely identified by something other than an email address, and their email addresses are subject to change, send the unique id from your system. Specify the id as a string.
locale (for end-users)

locale_id (for agents)

No The locale in Zendesk, specified as a number.
organization No The name of an organization to add the user to.
organization_id No The organization's external ID in the Zendesk API. If both organization and organization_id are supplied, organization is ignored.
phone No A phone number, specified as a string.
tags No This is a JSON array of tags to set on the user. These tags will replace any other tags that may exist in the user's profile.
remote_photo_url No URL for a photo to set on the user profile.
role No The user's role. Can be set to "user", "agent", or "admin". Default is "user". If the user already exists in Zendesk, the existing role is not changed.
custom_role_id No Applicable only if the role of the user is agent.
user_fields No

A JSON hash of user field key and values to set on the user. The user field must exist in order to set the field value. Each user field is identified by its field key found in the user fields admin settings. The format of date values is yyyy-mm-dd.

If a user field key or value is invalid, updating the field will fail silently and the user will still login successfully. For more information about custom user fields, see Adding custom fields to users.
Note: Sending null values in the the user_fields attribute will remove any existing values in the corresponding fields.

Most JWT implementations take a hash and a secret, and return a plain string payload to send to the other side. For context, here's an example in Ruby:

payload = JWT.encode({
   :email => "bob@example.com", :name => "Bob", :iat => Time.now.to_i, :jti => rand(2<<64).to_s
}, "Our shared secret")

response.headers["Location"] = "https://example.zendesk.com/access/jwt?jwt=#{payload}"

The return_to URL

When Zendesk redirects a user to your login script, it will also pass a return_to parameter in the URL. This parameter contains the page that Zendesk will return the user to after the authentication succeeds. For example:
  1. A user visits https://mycompany.zendesk.com/tickets/1232.
  2. Zendesk recognizes that the user is not authenticated.
  3. Zendesk redirects the user to: 
    https://mycompany.com/zendesk/sso?return_to=https://mycompany.zendesk.com/tickets/123
All your script needs to do, is take the return_to value from the invoked URL and pass it back to Zendesk when submitting the JWT token. In other words, upon authentication on your side, your script redirects the user to:
https://mycompany.zendesk.com/access/jwt?jwt=payload&return_to=https://mycompany.zendesk.com/tickets/123

The return_to parameter is an absolute URL for the agent interface and the Web portal, and a relative one for Help Center.

Whether you pass in the return_to parameter or not is optional, but we recommend it for the smoothest user experience.

Note: If your return_to address contains its own URL parameters, make sure that your script URI-encodes the entire return_to value when submitting the JWT token.

Error handling

If Zendesk encounters an error while processing a JWT login request, it will report a message that explains what the issue is. If you have a return URL configured for your JWT integration, it will redirect to that and pass a "message" and a "kind" parameter. In case of error, the "kind" parameter will always have the value "error". We recommend having a return URL as well as logging messages from Zendesk alongside the type, most of the errors that can happen are ones that you will want to fix (for example, clock drifts, rate limits being hit, invalid tokens, and so on).

Enabling JWT single sign-on in your Zendesk

To start using JWT single sign-on, you need to enable and configure it in your Zendesk account.

To enable JWT single sign-on

  1. Click the Admin icon () in the sidebar, then select Settings from the Security category.
    Zendesk Classic: Select the Setting menu, then select Security.
  2. Select the Admins & Agents or End-users tab.

    You can enable JWT single sign-on only for end-users, only for agents, or for both groups. You can't enable JWT for one group if the SAML SSO option is enabled for the other group. If you want to use single sign-on for both groups, both must be JWT or both must be SAML.

    If you started using Zendesk on or after August 21, 2013, the End-users tab is not available until you activate the Help Center. See Getting started with the Help Center.

    Zendesk Classic: Select the Single Sign-On tab.
  3. Select the JSON Web Token option.
    Zendesk Classic: Next to the JSON Web Token option, click Edit, and then select Enabled.
  4. Enter the Remote Login URL. This is where your user will be redirected when they attempt to access your Zendesk URL.
  5. Enter the Remote Logout URL. This is the URL that Zendesk will return your users to after they log out.

    Zendesk automatically adds email and external_id parameters to the URL. If you prefer not having this information in the URL, specify blank parameters in the logout URL. Example:

    https://www.xyz.com/user/signout/?email=&external_id=

  6. You can optionally restrict access to users within a range of IP addresses.
  7. If you use external_IDs for your users, you can update these in your Zendesk by selecting Allow update of external ids?.
  8. Finally, you need to generate a shared secret by clicking Generate a new token. Copy the shared secret because you'll need to use it in your JWT implementation.
    Important: You must keep the shared secret safe because if that gets compromised all the data in your Zendesk account is at risk.
  9. When you're done configuring JWT, click Save.

Additional information about JWT

JWT is a recent open standard that is being driven by the international standards body IETF and has top-level backers from the technology sector (for example, Microsoft, Facebook, and Google).

The fundamental building blocks of JWT are very well understood components and the result of this is a fairly simple spec, which is available here http://tools.ietf.org/html/draft-jones-json-web-token-10. There are a lot of open source implementations of the JWT spec that cover most modern technologies. This means that you can get JWT single sign-on set up without much difficulty.

One thing to be aware of is that the JWT payload is merely encoded and signed, not encrypted, so don't put any sensitive data in the hash table. JWT works by serializing the JSON that is being transmitted to a string. It then base 64 encodes that string and then makes an HMAC of the base 64 string which depends on the shared secret. This produces a signature that the recipient side can use to validate the user.

Have more questions? Submit a request

Comments

  • Avatar
    James Dietrich

    Though not terribly different than the Django example, here's an implementation for the Flask framework: https://gist.github.com/jbdietrich/5489562

  • Avatar
    Daryl Antony

    Hey there, I'm looking around for a Javascript implementation... throw us a link good sirs? (Lest google gets me there)

  • Avatar
    Daryl Antony

    Oh and I did mean node.js!

  • Avatar
    Ryan Nguyen

    I get this error using the php example in github (https://github.com/zendesk/zendesk\_jwt\_sso\_examples/blob/master/php\_jwt.php):

    "Timestamp invalid. Ensure that your iat parameter is correct."

     

    That example is using the time()  to generate current timestamp (iat parameter) .

     

    Is that error caused by mismatching server timestamp? If so, what offset do we need to set to iat to make sure Zendesk accept the timestamp?

     

    Thank you.

  • Avatar
    Morten Primdahl

    @Ryan - your time() call should return something that's close to http://www.epochconverter.com/ - if that's not the case, then your server clock may be drifting. The timestamp required is number of seconds since Unix epoch - aka. the time 00:00:00 UTC on 1 January 1970. If your server clock is looking fine, then submit a ticket and we can take a closer look.

    @Daryl - we hear you, keep an eye on https://github.com/zendesk/zendesk\_jwt\_sso\_examples

  • Avatar
  • Avatar
    Dave Fraleigh

    Not for nothing, but could you maybe have provided a little more time for transition than 3 weeks - or maybe taken a proactive approach and notified customers who were using the redirect method, before turning it off?

    -1 for  poor operational account management, Zendesk.

  • Avatar
    Brandon K.

    Hey Dave,

    I just talked with the Product Manager in charge of this transition and he let me know that we are giving our users 90 days to transition to JSON web tokens and not 3 weeks. So if your main concern is that 3 weeks is not enough, I hope this is good news! I'm going to open a ticket with you so we can discuss this transition with you and give you advice on any concerns you have with your specific account. 

    Thank you for letting us know about your concerns so we can sort this out!

  • Avatar
    Josh Roman

    Brandon et. al -- we have an active open ticket b/c our redirect-based SSO quit working w/o advanced warning. We're dead in the water for our customers.  Was there advance notification? (If so, how?) How do we get this turned back on so we have months (not weeks) to adapt?

  • Avatar
    Brandon K.

    Hey Josh,

    This should definitely not have already been turned off for you. I'll look for your ticket in our queue and escalate it right away if it has not been already. We should be able to work with you to get this turned back on so you can have a full 3 months to set up a smooth transition.

  • Avatar
    Dave Fraleigh

    @Josh - we had the same problem this morning - and I'm equally annoyed.

    Our problem was that we were expecting the return_to parameter to be returned all the time - some change apparently has made it an optional field, only returned in certain circumstances (ie, when redirecting to a specific article, rather than to the base site).

     

  • Avatar
    Josh Roman

    Thanks, Brandon. Our internal Zendesk manager also received some potentially erroneous advice from a Zendesk CSR earlier today in his attempts to resolve (i.e. "turn off SSO") which resulted in Remote Auth disappearing as an option on our account, so that may be complicating things.

    BTW, we did receive an email on 8/23/12 w/ the title "Zendesk Remote Authentication Security Enhancements" that referenced a future deprecation of the old approach, but nothing since. 

    @Dave -- love it when these things happen on a Friday afternoon! :(

  • Avatar
    Josh Roman

    Oh, and our ticket was submitted by Nick Konarski (Koko FitClub)

  • Avatar
    Brandon K.

    @Josh: It looks like your account was accidentally identified as inactive and we switched remote authentication off. Our development team has circled and re-enabled it on your account. Everything should be working for you right now, but let me know if it isn't. We're really sorry about causing this issue with you.

    @Dave: It looks like your issue is being cause by a misunderstanding with the API. I'll follow up in the ticket I have with you.

  • Avatar
    Josh Roman

    @Brandon: nope, not working. Let's take this up on the ticket:  403323

  • Avatar
    Brandon K.

    @Josh: Ok, thanks for letting me know. Our senior technical agent is taking a look at your ticket and should be able to work with you to find a resolution. Lets communicate through that ticket from now on so we don't accidentally miss any information. Thank you so much for your cooperation!

  • Avatar
    Josh Roman

    @Brandon & @Dave -- the "return_to" parameter is our issue as well. If we manually add it to the URL, we can get in. How do we get this added back?

     

  • Avatar
    Dave Fraleigh

    @Josh - we amended our internal code to make it an optional parameter and it works fine.  @Brandon - this is definitely a change introduced in the last 24 hours - and should be addressed.

  • Avatar
    Josh Roman

    @Dave -- thanks. While our devs were working on making the parameter optional, we tried just adding "?return_to=foo" to our Remote Login URL in the Zendesk control panel and it works -- Zendesk passes it back to us and we can get in. 

  • Avatar
    TJ Baker

    We too found out that things were being deprecated by finding out users could no longer log in to support, and we're now scrambling to implement the JWT method.

  • Avatar
    Morten Primdahl

    @TJ - for all intents and purposes, standard remote auth should be back to normal, there was a glitch due to mismatched expectations on the return_to parameter and how, specifically, IIS interprets HTTP parameters with no value, that has been fixed. Your case, I believe, is that you are running a very special setup which does not go the supported route, we're happy to help you transition to the proper setup as best we can.

    Morten

     

  • Avatar
    TJ Baker

    Thanks Morten.  Actually, I'd not checked today but it appears that things are back to working.  We will still work to incorporate the JWT method so we'll be up to date.

    Sorry for my premature complaint ... we must have been bit by the temporary change as well.

    Cheers

  • Avatar
    calos

    Why return_to always 'https://xxx.zendesk.com/home'  with zendesk login by JWT ? 

  • Avatar
    Vasudevan.D

    Hello,

    I wanted to pass on our code and see if you can help point us in the right direction, please.

    This is what we have

       public string SHARED_KEY = "...";

        var payload = new Dictionary<string, object>() {

                    { "iat", (new DateTime()).ToLongTimeString() },

                    { "jti", System.Guid.NewGuid()},

                     { "name", "Mani" },

                     { "email", " v.s.manivannan@gmail.com" }

                    

                };

               

                  string token = JWT.JsonWebToken.Encode(payload, SHARED_KEY, JWT.JwtHashAlgorithm.HS256);

    string redirectUrl = " https://mygreenedesk.zendesk.com/access/jwt?jwt="+ token;

    Response.Redirect(redirectUrl);

    this keeps returning us back to our homepage

    http://www.mygreenedesk.com

  • Avatar
    Morten Primdahl

    Vasudevan, we'll help you out in a ticket - don't ever post your secret anywhere, please update it in your account.

  • Avatar
    Samuel Sweet

    So I only JUST set up SSO with Remote Authentication about 4 weeks ago. Had to temporarily disable to transfer ownership between accounts. Going back to re-enable it and the "old" version isn't available to select anymore?!? Am I missing something or is Zendesk now dead for me until I rewrite our SSO module?

     

    I sure hope not. That would be seriously lame. 

  • Avatar
    Baptiste PAPINEAU

    @Vasudevan :

     

    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);

    TimeSpan diff = DateTime.Now.ToUniversalTime() - origin;

    double iat = Math.Floor(diff.TotalSeconds);

    var payload = new Dictionary<string, object>() {

    { "iat", iat.ToString()},

    { "jti", System.Guid.NewGuid() },

    { "name", [the name] },

    { "email", [the email] }

    };

  • Avatar
    Alex Osipov

    An issue we are having is remote auth allows embedding zendesk in an iframe and it's worked well for a long time.  Using JWT, the X-Frame-Options: SAMEORIGIN header is set and existing functionality has been broken.  

    What do we do with this?  All of our customers access an intranet and have gotten pretty used to have an embedded iframe to look at existing tickets, submit new ones.

  • Avatar
    Thamara Hettiarachchi

    Hi

    Anyone try to do with C#

    Thanks

  • Avatar
    Tadiraman

    Hi,

    I tried this new code by having a generic handler(ashx) using c#. 

    I can log in once & can logout, if I try to log in again I get an error message 'message=JWT+format+invalid.&kind=error'. Any ideas??

    Any help would be appreciated!

    -Thanks

    Raj

     

Please sign in to leave a comment.