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

team professional enterprise

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 Support without being prompted to enter separate login credentials. JWT SSO is available on Team and above.

Related articles:

About Zendesk single sign-on

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). Professional 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 straightforward and most modern languages have libraries that support it. 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 Zendesk Support).

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

  1. An unauthenticated user (not already logged in) navigates to your Zendesk Support 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

To perform SSO for a user, you need to send several required user attributes to Zendesk as a base64-encoded 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 Zendesk Support.

Specify HS256 as the JWT algorithm in the header of your JWT payload:

{
  "typ":"JWT",
  "alg":"HS256"
}

HS256 stands for HMAC SHA 256, a 256-bit encryption algorithm designed by the U.S. National Security Agency.

Note: Zendesk does not support the RS256 and ES256 JWT algorithms.

Redirect the user along with the JWT payload to the following Zendesk endpoint: https://mycompany.zendesk.com/access/jwt. The payload should be base64-encoded and appended to the URL as a query string. See Anatomy of a JWT request.

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

Note: 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.
Table 1. Supported attributes
Attribute Mandatory Description
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 Support.
name Yes The name of this user. The user in Zendesk Support 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 Support, specified as a number.
organization No The name of an existing organization to add the user to. You cannot create a new organization here. 
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's role is different than it is in Zendesk Support, the role is changed in Zendesk Support.
custom_role_id No Applicable only if the role of the user is agent.
user_fields No

A JSON hash of custom user field key and values to set on the user. The custom user field must exist in order to set the field value. Each custom 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 custom user field key or value is invalid, updating the field will fail silently and the user will still log in 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. 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 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 sends a message that explains what the issue is. If you have a return URL configured for your JWT integration, it redirects to that and passes a message and a kind parameter. In case of error, the kind parameter always has 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'll want to fix. Examples: clock drifts, rate limits being hit, invalid tokens, and so on.

Enabling JWT single sign-on in Zendesk Support

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

To enable JWT single sign-on

  1. Click the Admin icon () in the sidebar, then select Settings from the Security category.
  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.

  3. Select the JSON Web Token option.
  4. Enter the Remote Login URL. This is where your user will be redirected when they attempt to access your Zendesk URL. Zendesk automatically adds a brand_id parameter to the URL. This is the Zendesk Support brand the user was on when they attempted to sign in.
  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, external_id, and brand_id parameters to the URL. If you prefer not having email and external id 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 Zendesk Support 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 Support 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

  • 0

    Hi. Is there any article that clarifies the conditions when Zendesk redirects user to JWT authentication endpoint? Everything works flawlessly and completely transparently when I manually click the "sign in" button in Help Center, but I don't see any redirections when I just visit the Help Center in a freshly opened browser's incognito mode window.

  • 0

    Hi Aleksey,

    There's no particular article for this, however we only redirect when a user selects either the "Sign In" button or directly clicks a ticket link from say, an email notification that requires sign in.

    You can of course, require that all users are signed into the Help Center, to ensure they get redirected if required.

    Cheers,
    Dara

  • 0

    Hi everyone,

    Can anyone confirm that SSO using JWT is possible on a "Starter" plan account? The "Plan Availability" header at the top of this article says that it is NOT available, but the main Pricing page (https://www.zendesk.com/product/pricing/) says it IS available. I've also read posts in this Forum that say that SSO via JWT is available on all plans, but maybe that person was just reading the Pricing page. Has anyone actually successfully done it?

    I ask because I've attempted an SSO integration that returns a 401 "unauthorized" status page every time with body content set to this sparse and rather unenlightening message:

    {"error":"Couldn't authenticate you"}

    Even more interesting is the fact that an end-user account is created even though I get the error. I'd sort of figure that this would be all-or-nothing; I'd assume I'd either get a new end-user created (if needed) and logged in or I'd get completely rejected and nothing would happen. The fact that the end-user account gets created makes me believe that the JWT got decoded and verified successfully, but then I can't explain the "Couldn't authenticate you" message.

    Has anyone gotten this before? Is there a "verbose" mode where you get a bit more feedback about why you can't be authenticated? Are there any other debugging tips anyone can pass along.

    Thanks in advance!

    -- Dan

    Edit: I figured out how to get around the error. Activate Help Center from your main Admin control panel. That allows you to determine if users must register and log in or not. Turn that on. Then make sure you supply a return_to param with the JWT call, and make sure the return_to URL is somewhere that a logged in user could go -- like the new request form. Don't leave it empty. Whatever the logic is, the JWT handler does not have a reasonable default value for return_to.

    Edited by Dan Zaner
  • 1

    Hello,

    I have the following issue, after doing all what is required, there is only one user get redirected with a message "Please use one of the options below to sign in to Zendesk" this case only happens for a single user, and there is nothing special in the data being sent to Zendesk through the JWT for this user, also tried to look around the website if there is any descriptions for the error am receiving, couldn't find any.

    Any help?

  • 0

    @Moath - it sounds like you don't have SSO enabled for both agents and end users. In the security settings page, there's a separate tab for each set of users, and you have to enable (and hit save!) on both tabs. And if you're trying to sign in as an end user, make sure to have your Help Center activated. Also note that if you're signing in with an email address that is not already associated with a user profile in Zendesk and you're not also passing in a role with your JWT payload, that user will automatically be provisioned as an end user.

    If you need more help, send us an email at support@zendesk.com. Thanks!

  • 0

    Is it possible to manually change the Shared Secret, or revert back to an old Shared Secret?

  • 0

    @Taylor - Due to security concerns, you cannot manually set the shared secret or revert it to an old one. Your only option is to generate another shared secret.

  • 0

    Question about "Error handling" section

    >> If you have a return URL configured for your JWT integration, it will redirect to that and pass a "message" and a "kind" parameter.

    What do you mean by "return URL"? Remote logout URL? If yes, change it in text

  • 0

    Hi Dmitry - the "return URL" is referring to the return_to parameter discussed in the section immediately before the "Error Handling" section. It is a parameter that you can (optionally) pass along with your JWT payload to redirect the user after they log in. The remote logout URL is separate, and is used to direct the user to your logout page in your SSO system when the user selects "logout" in Zendesk.

  • 0

    I have a problem with our JWT SSO setup, which is working fine in my tests but not for one user who cannot login because:

    - Sign In keeps invoking our /support/logout URL (which is /support/logout in our case here).

    - This invocation is done without a return_to argument being passed to it, so it has nowhere to go after signout.

    I only added the Sign Out code last week, and as far as I know it was working fine then. However, at this point the two problems above are preventing this user from logging in. I've asked her to clear cache, try a different browser, etc. They all fail the same way.

    Is it ever normal for my Sign Out SSO URL to be invoked on a Sign In? If so, shouldn't it specify a return_to URL?

    - It's only enabled for end-users

    - It's set to SSO -> JWT and the Remote URLs are:

    Sign In: (domain)/support/login/

    Sign Out: (domain)/support/logout/

    Summary: We've set the two URL fields in the SSO JWT options to the values above and in most cases it's working fine (very smooth, no problems implementing SSO), but the second one is being invoked on a Sign In.  Clearing cache, changing browsers, etc, seems to have no effect.

    Edited by Jim Tarber
  • 0

    Hi,

    I always get a 500 error page when I try to reach http://server/classic_asp_jwt_with_ad.asp based on a IIS W2008 Server.

    Is anyone facing this error ?

  • 0

    It would be helpful to add a table of contents to the top of this article with anchor links down to each section (similar to most ZD articles). I use this page all the time! Thanks.

  • 0

    @Jim:

    "Is it ever normal for my Sign Out SSO URL to be invoked on a Sign In?"

    No, this is probably happening due to a problem in the authentication process. If an error occurs during auth, the user is automatically redirected to the remote logout URL. If you could open a ticket with us, we can help you sort out what exactly is happening.

    @Eldien:

    I'm not sure what URL you're looking for, but if you're looking for our example code for JWT and AD, the code is here:
    https://github.com/zendesk/zendesk_jwt_sso_examples/blob/master/classic_asp_jwt_with_ad.asp

    @Justin:

    Thank you for your feedback, I will pass that along!

  • 0

    @Justin (again) - Done! Thanks again for your input!

  • 0

    @Anna: Yes I was made some changes for our environment and deployed.

    When I access my zendesk url, it will redirect to AD basic authentication after that instead of return to my zendesk, the page return error page 500

  • 0

    Hello,

     

    I am trying to setup jwt with IIS on win2008.  The script classic_asp_jwt_with_ad.asp and his dependencies are located in C:\inetpub\wwwroot\zendesk on my server.

    However I have an issue when I try to connect.

    When I setup my Remote login URL to http://mycompany.com/zendesk/  the login works with my AD users but I just get the web page index of /zendesk/ on nothing happen (no redirection to zendesk) like the script was not executing.

    If I change my Remote login URL http://mycompany.com/zendesk/script classic_asp_jwt_with_ad.asp  the login works but I get an HTTP error 500...

    Could you please clarify what I should exactly setup for the "Remote login URL" in order for the script to execute and redirect me to sendesk once I log in ?

    Thanks

  • 0

    @Mat

    I'm also facing the same error. Hopefully someone can help to this matter

  • 0

    @Mat & @Eldien -  In an effort to assist you further, I've created individual tickets for you.  I'll send you an update via your ticket shortly.  

  • 0

     

     

    Edited by Gary Bunofsky
  • 0

    Hello and thank you for this helpful article. 

    We have been using the normal zendesk authentication for quite a while for our users and now we would like to use the SSO authentication. 

    If a user had already a zendesk account and then we switch to SSO auth. Does that user will end up with 2 different zendesk accounts ? Is-there some merging rule we could take advantage of ? We just don't want to lose or duplicate user data. 

    In advance, thank you very much, 

    Alexandre 

  • 0

    Hi Alexandre,

    As long as the email address that is used in the JWT login is the same one already associated with their Zendesk account, it will recognize them as the same user and no duplicate user will be created.

    It is possible to merge users however, should you need to do so:
    https://support.zendesk.com/hc/en-us/articles/203690896-Merging-a-user-s-duplicate-account

    Thanks!

  • 0

    We are trying to setup JWT with IIS on Windows 2016.  Using the script classic_asp_jwt_with_ad.asp and its dependencies we are able to log into Zendesk without issue.

     

    We want to be able to pass the phone number and department with our end user logins in doing so we are trying to use the "user_fields" API.  I am woefully unqualified as a Classic ASP programmer, I understand most of the code and how the Scripting.Dictionaries are working.  However when I add the string "employee_department Information Technology" to the scripting object, it fails miserably.

     

    Can anyone point me in the right direction on how to pass these hashes so that it works.  I can work with pseudo code if you are unfamiliar with ASP.

     

  • 1

    Hey Matthew!

    I saw that you posted this in another thread as well, and one of my colleagues was able to point you to some resources. Let us know if you need anything else!

  • 2

    Is it possible to authenticate users by UUID as well as email address?  If a user changes his email address on our website to match someone else's, he would then be logged into that user's account on Zendesk.

  • 0

    Joan, we did that. I'm not sure if it's the official way, but what we did was enable the "external_id" in the authentication settings and always provide the user UUID in that. That supports both name and email changes while preserving the account.

    The only negative from this that I could think of was that support for any external sources such as Facebook or Twitter may not be able to match up that user on those services with the user on zendesk, however that's probably a Good Thing anyway, if they are using different email address on another system.

    Using email to sync information about accounts on external systems is a bit evil in my mind anyway, because the user in question may not want any connection between their accounts, e.g. between their Facebook and Twitter accounts, or Zendesk.  In our service, users use only anonymous avatar names (and images) which do not match their real (wallet) info. Using the email to pull information from another system is potentially a serious privacy violation.  For this reason, one of my urgent work items is to replace the use of gravatar profile images with the image the users have provided on their profiles for our (separate) service. Their gravatar image may be their real life identity.

  • 0

    Thank you Jim - I know we can pass in the UUID, but my question is whether that will be validated for login along with the email address, or is just the email address used to authenticate the user?

  • 0

    I can't check (I don't have admin access currently) but I believe there was a checkbox on the Admin->Auth->User tab that enabled or disabled use of the "external_id" field as the key for the user. I believe we've already done email address changes and had the zendesk account retain the previous info associated with that account.  It basically just works, as far as I can tell.

    However, there is a potential "gotcha" here.  If you already have existing user accounts showing up in Zendesk, I'm not sure how transitions to that setup would work. If you don't already have it enabled and filled in per-user, then enable it, new JWT fetches with an external_id may determine that to be a new Zendesk user (losing access to old support requests, etc).   It may just fall back to some attempt to match the email address, or at least until the next JWT is returned for that user.  But I haven't found it described in this level of detail anywhere.

    Edited by Jim Tarber
  • 0

    thank you again for your help - the existing user issue was exactly what I was thinking about - we have a large number of users who already have accounts without external ids set, so it may not work to change things now.

  • 0

    Hi 

     

    I am trying to use implement SSO using JWT in node js. The example provided at https://github.com/zendesk/zendesk_jwt_sso_examples/blob/master/node_jwt.js is wrong.

    issue #1 : iat: (new Date().getTime() / 1000), 

    correct will be 

    Math.round((new Date().getTime()) / 1000)

    issue #2 : I am getting error "The%20unique%20request%20identifier%20was%20reused.%20Please%20fix%20this%20and%20try%20again."

    Below is my implementation

     

    var generateUUID = require('uuid');
    var jwt = require('jwt-simple');

    getZendeskJWTUrl : function(req, res){
    var db = req.db;
    var userUuid = req.query.userUuid;
    var subDomain = 'dev-slicepay';
    var sharedKey = 'XXXXXXXXXXXXXXXXXX';
    var userColl = db.get('userDetails');
    userColl.findOne({
    uuid : userUuid
    } , {}, function(err, userDoc){
    if(!err && userDoc){
    var payload = {
    iat: Math.round((new Date().getTime()) / 1000),
    jti: generateUUID.v4(),
    external_id : userUuid
    };
    if(userDoc.hasOwnProperty('name') && userDoc.name !== ''){
    payload['name'] = userDoc.name;
    }
    if(userDoc.hasOwnProperty('email') && userDoc.email !== ''){
    payload['email'] = userDoc.email;
    }
    var token = jwt.encode(payload, sharedKey);
    var redirectUrl = 'https://' + subDomain + '.zendesk.com/access/jwt?jwt=' + token;
    redirectUrl += '&return_to=' + encodeURIComponent('https://'+ subDomain +'.zendesk.com');
    console.log('Redirecting to : ' + redirectUrl);
    res.writeHead(302, {
    'Location': redirectUrl
    });
    res.end();
    } else {
    res.send({
    status : 'error',
    msg: 'Error occurred while fetching token payload.'
    });
    }
    })
    }
    Edited by Nehal Kumar
  • 0

    Hi Nehal, 

    I see you also sent in a ticket for the question about your JWT script. I responded to you already via this ticket. Thanks so much!

Please sign in to leave a comment.

Powered by Zendesk