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.
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
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
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