At the core of single sign-on authentication is a technology called JSON Web Token (JWT) that allows Zendesk to trust the login requests it gets from your systems. See Setting up single sign-on with JWT (JSON Web Token) for details
Do not place the JWT directly in the URL as URLs can potentially be exposed to attackers through server logs, browser history, and network intermediaries.
For Zendesk SSO integrations, a server-side mechanism should generate the JWT and redirect the user's browser to a Zendesk-specific URL, passing the JWT as a parameter in the query string rather than in the HTTP header.
A JWT typically consists of three parts separated by periods (full stops). Each part serves a different purpose within the JWT structure: the header, payload, and signature.
This article contains the following sections:
Chunk 1: JWT Header
The first chunk is the JWT header. It indicates that it's a JWT request, and indicates the type of hashing algorithm used. (More on that later.)
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
The big reveal about this (and the rest of the data) is that it is base64 encoded. This is not actually encryption so it can be easily decoded by tools like the following:
- Web tool: http://www.base64decode.org/
- Terminal: http://drewsymo.com/how-to/quick-and-simple-base64-encode-on-mac-osx-terminal/
This is what that string looks like decoded:
{"typ":"JWT",
"alg":"HS256"}
You can see that it takes a JSON structure and has two key-value pairs that effectively mean type: JWT
and Algorithm: HMAC SHA 256
. SHA 256 is a 256-bit encryption algorithm designed by the U.S. National Security Agency. It's used to generate the third chunk -- the signature -- which we'll get to in a bit.
Chunk 2: JWT Claims Set/Payload
The second chunk is considerably longer because it contains the payload. It's known as the 'JWT Claims Set'.
eyJpYXQiOjEzNzIxMTMzMDUsImp0aSI6ODg4MzM2MjUzMTE5Ni4zMjYsIm5hbWUiOiJUZXN0IFVzZXIiLCJlbWFpbCI6InR1c2VyQGV4YW1wbGUub3JnIiwiZXh0ZXJuYWxfaWQiOiI1Njc4Iiwib3JnYW5pemF0aW9uIjoiQXBwbGUiLCJ0YWdzIjoidmlwX3VzZXIiLCJyZW1vdGVfcGhvdG9fdXJsIjoiaHR0cDovL21pdC56ZW5mcy5jb20vMjA2LzIwMTEvMDUvQmFybmFieV9NYXR0X2Nyb3BwZWQuanBnIiwibG9jYWxlX2lkIjoiOCJ9
This contains a timestamp, a random value, a user name, email address, external ID, and some tags. There are even more options available. Again, just base64 decode it to make sense of the payload. I'll split the lines to make it easier to digest.
{
"iat":1372113305,
"jti":8883362531196.326,
"name":"Test User",
"email":"tuser@example.org",
"external_id":"5678",
"organization":"Apple",
"tags":"vip_user",
"remote_photo_url":"http://mit.zenfs.com/206/2011/05/Barnaby_Matt_cropped.jpg",
"locale_id":"8"
}
Required properties
IAT
The first key is iat, which stands for issued at. This is a timestamp formatted as whole seconds since January 01, 1970, which is a standard UNIX representation of time. The timestamp must be an integer (no decimals) and it must be in UTC. It also must be within 3 minutes of the current time when the Zendesk server receives it. This puts a self-destruct mechanism in each remote authentication request, preventing any single request from being used more than 3 minutes after it was generated.
JTI
The second key, jti, stands for JSON Token ID. It's really just a random string. It has to be sufficiently long and random so that it's very unlikely to be used again on this account. If, by accident or chance, it is re-used for another authentication request, the request will fail. What this amounts to is a disposable key. By including a (mandatorily) random value like this, we ensure that no two authentication requests are ever the same. This prevents reuse of a valid request URL. For example, imagine that someone succeeded in installing malware on your computer or network and has started logging your traffic. They can see every URL you hit. If they see and grab the URL you were issued to log in to Zendesk, they could use it themselves to log in as you within 3 minutes without this single-use key.
Name
Next is the user's full name with spaces included. Whatever Zendesk receives here is set as the user name, even if they had a different name set before.
After that is the user's email. Email is always required. It is used as the unique identifier for a user unless an external ID is received*. This means that if we get an email and an external ID, we try to match the ID first. If we do, we update that user with the specified email address.
*Note: If the option 'Allow update of external IDs' is enabled in Zendesk, we'll continue to key off the email even if an external ID is received. If the email and external ID differ, we'll change the ID.
Optional properties
External ID
External ID is an optional ID you can use to identify users instead of using their email address (noted above).
Organization
You can also pass an organization value to add a user to an organization. The named organization must already exist and the name must match exactly. Otherwise no action is taken.
Tags
The tags key allows you to set tags on the user being logged in. The key replaces any existing tags on the user with the tags you specify, so use it carefully. Passing a blank tags parameter removes all tags from a user.
Remote Photo URL
You can also pass a value for remote_photo_url, which accepts a public URL containing a photo and sets this photo as the user's profile picture.
Locale (Language)
You can pass a value for locale_id to set or update the authenticated user's language in Zendesk. The value must be a number that matches a currently activated locale in your Zendesk. You can find the locales by using the locales.json endpoint of our API: https://developer.zendesk.com/api-reference/ticketing/account-configuration/locales/#list-locales
User Fields (not shown in example)
This must be a JSON object that includes key-value pairs for each field key and value. The field key can be found or defined in the User Fields interface. Note that only custom user fields can be passed.
Example:
"user_fields": {"checked": false,"date_joined": "2013-08-14T00:00:00+00:00","region": "EMEA","text_field": null}
Checkboxes use boolean values, date codes follow the example above, and drop-downs accept the name of the option. Text fields accept strings.
Phone Number (not shown in example)
This accepts a string for a phone number identity. Make sure to specify the phone number in an accepted format. For more information, see What are the accepted phone number formats?
Chunk 3: JWS Signature
The last chunk of the request is the encrypted part. You don't need to worry too much about this, but basically it takes all the information above (iat, jti, name, email, etc) along with the shared secret and generates an encrypted string out of all of it. Then, per JWT standards, a chunk of that encrypted string is taken (known as a checksum), and that is the JWS signature.
So how is this secure?
To generate a valid encrypted string, you must know the shared secret.
Encryption is designed so that you cannot work backwards from the encrypted string to the data and the key. Even if you have the data contents, it's practically impossible to deduce the key.
But since you have the key, and we have the key, and you send us the same data unencrypted as you do encrypted, we can build the signature ourselves and check that it matches what you sent.
It also insures nobody can tamper with the data in transit.
This is how you build it in pseudo-code:
URLBase64Encode(
HMAC-SHA256(
URLBase64Encode( header_json ).URLBase64Encode( payload_json )
)
)
When generating the HMAC-SHA256 of the encoded header and payload, you include the shared secret.
This is the result:
Zv9P7PNIcgHfxZaMwQtMpty3TZnmVHRWcsmAMM-mNHg
6 comments
Daniel Korsgaard
Sorry to bring this up again, I have an issue when trying to match the user_fields in PHP. The User does not get updated / created with the custom fields filled.
I'm not sure if I have misinterpreted this part. But when using the web widget, this seems like the only plausible way to create/update a user without calling the API.
0
Remi
Hi Daniel Korsgaard,
Thank you for your post here, I hope you are doing well today !
I understand you want to use your JWT within the Web Widget, right?
If the idea is to pass some Values to the User Fields, you need to ensure that :
You may find a list of examples for JWT attributes here.
Otherwise, if the idea is to auth a user within the Web Widget, then you might need to follow this doc : Is it possible to set up SSO auth within the Web Widget and the Chat Widget?
If the issue persist, let us know, and we can create a ticket with the support for you (or else you can create a ticket directly) and we will look into this as well as checking in our logs what we received in our backend through your Auth request.
Hope this helps! Feel free to let us know if further assistance is required on your side.
Have a great rest of your day,
Best regards.
0
Jon Simone
Where is the option to 'Allow update of external IDs'?
Edit: Found it! domain.zendesk.com/admin/account/security/sso
My understanding is that this will use the external ID instead of the email if enabled. However, the request must have an external ID in order for this to work. Will it default to email if external ID is missing?
Thanks!
0
Dave Dyson
Hi Jon –
There's a note in the documentation above:
"If the option 'Allow update of external IDs' is enabled in Zendesk, we'll continue to key off the email even if an external ID is received. If the email and external ID differ, we'll change the ID."
Does that help?
-1
Ignacio Montecinos
Hi, I have a question about the use of the locale_id, because for me is not working my payload looks like this:

the locale_id 1 is referent to the English language, but when I try to open zendesk it's showing on Spanish
Thanks.
0
Neil Murphy
Is all of this equally applicable to Mobile SDK integration, as well as SSO integration?
Is it the case that only the “optional properties” listed above are supported? The Mobile SDK integration tutorial indicates that “everything else is optional,” and we'd like to stuff additional custom properties at the payload's top level.
0