Using OAuth authentication with your application

You can use OAuth 2 to authenticate all your application's API requests to Zendesk. OAuth provides a secure way for your application to access Zendesk data without having to store and use the passwords of Zendesk users, which is sensitive information.

To use OAuth authentication, you need to register your application with Zendesk. You also need to add some functionality to your application to support the OAuth authorization flow.

Topics covered in this article:

For a tutorial on building a web application that implements an OAuth authorization flow, see Building an OAuth web app.

Registering your application with Zendesk

You must register your application to generate OAuth credentials that your application can use to authenticate API calls to Zendesk.

Note: This section describes how to set up an OAuth client for users of one Zendesk account. If your application will interact not only with one Zendesk account but with lots of them, you can request a global OAuth client. A global OAuth client is a secure, cleaner way of doing API authentication with multiple Zendesk instances. For more information, see Using a global OAuth client to integrate with Zendesk.

To register your application

  1. In Zendesk, click Manage () and then select API in the Channels category.
  2. Click the OAuth Clients tab on the Channels/API page, and then click Add a Client on the right side of the client list.

    A page for registering your application appears. The Secret field is pre-populated. This is the "client_secret" value specified in the OAuth2 spec.

  3. Complete the following fields:
    • Name - Enter a name for your app. This is the name that users will see when asked to grant access to your application, and when they check the list of third-party apps that have access to their Zendesk.
    • Unique Identifier - Click the field to auto-populate it with the name you entered for your app. You can change it if you want.
    • Logo - Optional. This is the logo that users will see when asked to grant access to your application. The image can be a JPG, GIF, or PNG. For best results, upload a square image. It will be resized for the authorization page.
    • Company - Optional. This is the company name that users will see when asked to grant access to your application. The information can help them understand who they're granting access to.
    • Description - Optional. This is a short description of your app that users will see when asked to grant access to it.
    • Redirect URLs - Enter the URL or URLs that Zendesk should use to send the user's decision to grant access to your application. The URLs must be absolute and not relative, https (unless localhost or 127.0.0.1), and newline-separated.
  4. Copy the Secret value to your clipboard and save it somewhere safe. Note: The characters may extend past the width of the text box, so make sure to select everything before copying.
    Important: For security reasons, your secret is displayed fully only once. After clicking Save, you'll only have access to the first nine characters.
  5. Click Save.

Use the unique identifier and the secret value in your application as described in this following topic.

Implementing an OAuth authorization flow in your application

Zendesk supports several OAuth flows. This article describes the authorization code grant flow in detail. Another flow, the implicit grant flow, is similar to the first except it doesn't use an authorization code. The other two options are server-side grant types that don't require interacting with end-users.

Authorization code grant flow

This flow is called the authorization code grant flow because you have to get an authorization code before you can request an access token.

The flow doesn't use refresh tokens. The access token doesn't expire.

To implement the authorization code grant flow, you need to add the following functionality to your application:

For a tutorial on building a web application that implements an OAuth authorization flow, see Building an OAuth web app.

Step 1 - Send the user to the Zendesk authorization page

First, your application has to send the user to the Zendesk authorization page. The page asks the user to authorize your application to access Zendesk on their behalf. After the user makes a choice, Zendesk sends the choice and a few other bits of information back to your application.

To send the user to the Zendesk authorization page

Add a link or button in your application that sends the user to the following URL:

https://{subdomain}.zendesk.com/oauth/authorizations/new

where {subdomain} is your Zendesk subdomain. You can use either a POST or a GET request. Include the following parameters:

  • response_type - Required. Zendesk returns an authorization code in the response, so specify code as the response type. Example: response_type=code.
  • redirect_uri - Required. The URL that Zendesk should use to send the user's decision to grant access to your application. The URL has be absolute and not relative. It also has to be secure (https), unless you're using localhost or 127.0.0.1.
  • client_id - Required. The unique identifier you obtained when you registered your application with Zendesk. See the section above.
  • scope - Required. A space-separated list of scopes that control access to the oauth endpoints. The following scopes are supported: "read" and "write". Example: scope=read%20write. The "read" scope gives access to APIs for showing and indexing. The "write" scope gives access to APIs for creating, updating, and deleting.
  • state - An arbitrary string included in the response from Zendesk after the user decides whether or not to grant access. You can use the parameter to guard against cross-site request forgery (CSRF) attacks. In a CSRF attack, the end-user is tricked into clicking a link that performs an action in a web application where the end-user is still authenticated. To guard against this kind of attack, add some value to the state parameter and validate it when it comes back.

Make sure to URL-encode the parameters.

Example GET request

https://{subdomain}.zendesk.com/oauth/authorizations/new?response_type=code&redirect_uri={your_redirect_url}&client_id={your_unique_identifier}&scope=read%20write

The Zendesk authorization page opens in the end-user's browser. After the user makes a decision, Zendesk sends the decision to the redirect URL you specified in the request.

Step 2 - Handle the user's authorization decision

Your application has to handle the response from Zendesk telling it what the user decided. The information is contained in URL parameters in the redirect URL.

If the user decided to grant access to the application, the redirect URL contains an authorization code. Example:

{redirect_url}?code=7xqwtlf3rrdj8uyeb1yf

The authorization code is valid only for a short time.

If the user decided not to grant access to the application, the redirect URL contains error and error_description parameters that inform the app that the user denied access:

{redirect_url}?error=access_denied&error_description=The+end-user+or+authorization+server+denied+the+request

Use these values to control the flow of your application. If the URL contains a code parameter, get an access token from Zendesk as described in the following section. This is the token to include in API calls to Zendesk.

Step 3 - Get an access token from Zendesk

If your application received an authorization code from Zendesk in response to the user granting access, your application can exchange it for an access token. To get the access token, make a POST request to the following endpoint:

https://{subdomain}.zendesk.com/oauth/tokens

Include the following required parameters in the request:

  • grant_type - Specify authorization_code as the value.
  • code - Use the authorization code you received from Zendesk after the user granted access.
  • client_id - Use the unique identifier you received when you registered your application with Zendesk.
  • client_secret - Use the Secret value you received when you registered your application with Zendesk.
  • redirect_uri - The same redirect URL as in step 3. For ID purposes only.
  • scope - Specify read as the value.

The request must be over https and the parameters must be formatted as JSON.

Using curl

curl https://{subdomain}.zendesk.com/oauth/tokens \
  -H "Content-Type: application/json" \
  -d '{"grant_type": "authorization_code", "code": "{your_code}",
    "client_id": "{your_client_id}", "client_secret": "{your_client_secret}", 
    "redirect_uri": "{your_redirect_url}", "scope": "read" }' \
  -X POST

Example response

Status: 200 OK

{
  "access_token": "gErypPlm4dOVgGRvA1ZzMH5MQ3nLo8bo",
  "token_type": "bearer",
  "scope":"read"
}

Step 4 - Use the access token in API calls

The app can use the access token to make API calls. Include the token in an HTTP Authorization header with the request, as follows:

Authorization: Bearer {a_valid_access_token}

For example, a curl request to list tickets would look as follows:

curl https://{subdomain}.zendesk.com/api/v2/tickets.json \
  -H "Authorization: Bearer gErypPlm4dOVgGRvA1ZzMH5MQ3nLo8bo"

Implicit grant flow

The implicit grant flow is similar to the authorization code grant flow except there's no step 3. You request a token instead of an authorization code. In other words, you set the value of the response_type parameter to "token" instead of "code". If the end-user authorizes access, the token is sent immediately in the redirect URL. The implicit grant flow is meant for browser-based JavaScript apps with no server-side component. Since all of the app's source code is loaded in the browser, the client secret can't be kept confidential and isn't used.

Example request

https://{subdomain}.zendesk.com/oauth/authorizations/new?response_type=token&client_id={your_unique_identifier}&scope=read%20write   

Example responses

If the user grants access to the application, the token is included in the redirect URL.

{redirect_url}#access_token=gErypPlm4dOVgGRvA1ZzMH5MQ3nLo8bo&token_type=bearer

If the user decides not to grant access to the application, the URL contains error and error_description parameters.

{redirect_url}#error=access_denied&error_description=The+end-user+or+authorization+server+denied+the+request

Password grant type

Use the password grant type to exchange a Zendesk username and password for an access token directly. This grant type should only be used if your application can get Zendesk usernames and passwords. This is usually a highly privileged application with Zendesk. The application should never store the usernames and passwords. It should also be highly secure about how it gets them.

Example request

curl https://{subdomain}.zendesk.com/oauth/tokens \
  -H "Content-Type: application/json" \
  -d '{"grant_type": "password", "client_id": "{your_client_id}", 
    "client_secret": "{your_client_secret}", "scope": "read",
    "username": "{zendesk_username}", "password": "{zendesk_password}"}' \
  -X POST

A Zendesk username is usually an email address such as agent@zendesk.com.

Example response

Status: 200 OK

{
  "access_token": "gErypPlm4dOVgGRvA1ZzMH5MQ3nLo8bo",
  "token_type": "bearer",
  "scope":"read"
}

Client credentials grant type

This grant type is experimental for now. The client credentials grant flow is used to access API endpoints designed specifically for applications rather than for users. An example might be an endpoint that lets an application get statistics about its users. The access token won't work with any endpoint that has a notion of an authenticated user and therefore requires a user's credentials. Example: Getting tickets. Zendesk currently doesn't have any application-only endpoints.

Example request

curl https://{subdomain}.zendesk.com/oauth/tokens \
  -H "Content-Type: application/json" \
  -d '{"grant_type": "client_credentials", "client_id": "{your_client_id}", 
    "client_secret": "{your_client_secret}", "scope": "read", 
    "user_id": "{your_user_id}"}'  \
  -X POST

Example response

Status: 200 OK

{
  "access_token": "gErypPlm4dOVgGRvA1ZzMH5MQ3nLo8bo",
  "token_type": "bearer",
  "scope":"read"
}
Have more questions? Submit a request

Comments

  • Avatar
    Rachel Cantor

    Thanks for the guide! This was extremely helpful in creating a Google Apps Script that creates tickets from a Google spreadsheet. If anyone is interested in doing the same, visit my blog post for the guide! 

    http://blog.controlgroup.com/2013/12/08/connecting-zendesk-google-spreadsheets-using-google-apps-script/

  • Avatar
    Jennifer Rowe

    Glad to hear this was helpful! And I love your article. Thanks for sharing with the community! :)

  • Avatar
    Bryan Dreyer

    Hoping a comment here will help... I'm working on a powershell implementation using the client_credentials grant type.  I'm able to successfully get a bearer token, however when I try to use the token I get an error "Couldn't authenticate you"  Not sure what I'm doing wrong... attached a code sample.

     

  • Avatar
    Charles Nadeau

    Hi Bryan,

    I think there's a ticket open and we're investigating. Typically, the access token for this kind of grant is good only for "app-only" endpoints that don't have a notion of an authenticated user and therefore don't require a user's credentials. In other words, any request to an endpoint with a user context, such as getting tickets, won't work. An example of an app-only endpoint might be one that gets statistics about the users of the app. We're looking to see if we have any undocumented "app-only" endpoints, or if any are in the works.

  • Avatar
    Bryan Dreyer

    Thanks Charles, that makes sense.  I'm trying to create a simple queue monitor scheduled task for my team and didn't want them to have to use their u/p since they expire.  Thought the client_credentials type would work.  For now we are using the API Token of our managers account (since I don't have permissions to see a token of my own).  Do the API tokens expire (from the admin/channels area)?

  • Avatar
    Charles Nadeau

    Hi Bryan,

    The API token doesn't expire. One thing to be aware is that an admin can generate a new token fairly easily -- at the click of a button -- which may or may not be a problem depending on your admins. Another option might be getting a password grant type token above, which requires a username and password to get the initial token, but then you can use the token in your app and it doesn't expire as far as I know.

    Charles

  • Avatar
    Bryan Dreyer

    Charles, thanks!  I just implemented this and it works great using the password grant type and is relatively easy setup for my team members.

  • Avatar
    John Berlet

    Had a question, when I try to request the following in my Ajax call, am getting a JSON error:

    $j(document).ready(function() {

          new Ajax.Request('https://evolvondemand.zendesk.com/oauth/tokens', {

                     method: 'POST',

                     data: '{"grant_type": "password", "client_id": "self-registration", "client_secret": "3c60", "scope": "read","username": " jb@company.com", "password": "!"}',

                     asynchronous: true,

                     onSuccess: function(transport){

                     var obj = transport.responseText.evalJSON();

                     alert("Value is: "+obj);

                     }

                   });

                   });

      

    Error is: {"error":"invalid_request","error_description":"'client_id', 'grant_type' required."}

    Is this just a problem with my data parameter (JSON)?

    The next step once this is working is to actually pass the returned token in my POST request the API endpoints...but first need to get this working. 

    Thank you!

    John

  • Avatar
    Laura D.

    John - I see that you're working with Sean on this in a ticket, he's a great resource so I'll let you two continue there! 

  • Avatar
    Simon Pledger

    Hi Charles

    Thanks for your article.

    In other OAuth systems we have used, the access token typically has a short lifetime (say 20mins) after which it has to be refreshed using a refresh token. The refresh token is passed back along  with the access_token when first obtaining it using the Auth token. From then on, when the access_token expires the refresh_token is used to get a new access_token and refresh_token pair. Does your OAuth implementation not use refresh tokens and therefore does this imply that the access_token lasts indefinitely?

    Also you mention that the intial OAuth token does have a short lifetime. Can you elaborate on how long this is?

    Thanks

    Simon

  • Avatar
    Ben Rohrs

    @Simon: At this point in time we do not utilize refresh tokens

  • Avatar
    John Ashton

    Hello,

    Im just starting to setup an OAuth Connection from my app to Zendesk

    On the very first step i am having some troubles.

    URL:

    https://amazinghourse.zendesk.com/oauth/authorizations/new?redirect\_uri=https%3A%2F%2Flocalhost%2Fprofile%2Fzendeskconnect&client\_id=vanilla&response\_type=code&scope=read

    What is wrong with this request?  The error message is not telling me enough,

    https://www.evernote.com/shard/s30/sh/73c7660d-0897-4fae-89b1-02fd8c6d74dc/b0d96f5faf94de273569b02f805049ae/deep/0/AmazingHourse.png

    Any help would be greatly appreciated.

  • Avatar
    John Ashton

    My problem was that i had the incorrect return url entered.  The error should say this IMHO.

    Maybes its not there for security reasons.  If so it would be nice to have a debug option or something to help with the initial setup.

  • Avatar
    Meel Velliste

    Is it possible to use Zendesk's OAuth API to authenticate for someone else's Zendesk domain? I am trying to develop a web app that allows Zendesk users to pipe their Zendesk data into our web app. I would like to be able to set up a Client ID and Client Secret that belongs to us, rather than to each Zendesk domain.

    Suppose that company1.zendesk.com and company2.zendesk.com both want to connect to my web app. Correct me if I am wrong, but it seems that under the current implementation of the Zendesk OAuth API, both Company 1 and Company 2 would each have to set up an OAuth Client configuration in their Zendesk accounts, and then provide me with the Client ID and Client Secret, so that I can put these paramters into my web server. Instead, what I would like to be able to do, is for me to create my own Client ID and Client Secret  in my Zendesk configuration just one time. And then to be able to use the same Client ID and Secret in my web server to allow any Zendesk domain to authenticate, and to give API access for my web app to pull their data. Is this possible?

  • Avatar
    Charles Nadeau

    @Meel:

    You can request a global OAuth client. A global OAuth client is a secure, cleaner way of doing API authentication with multiple Zendesk instances. For more information, see Using a global OAuth client to integrate with Zendesk.

  • Avatar
    Meel Velliste

    @Charles:

    Thank you, that answers my question!

  • Avatar
    Aaron Hundt

    @John:

    Thank you for letting us know about this error description. I have submitted a ticket to our development team to update the returned error. 

  • Avatar
    Michael Ho

    How does the information here relate to the information found at http://developer.zendesk.com/documentation/rest\_api/introduction.html#security-and-authentication ?  Is the SSL-only authentication scheme still supported?  Is it dead?  Should the information on that page be replaced with this page?

  • Avatar
    Meel Velliste

    @Michael

    SSL refers to the fact that the api calls are encrypted, i.e. the calls have to be made against an "https" url as opposed to plain "http". Authentication is independent of that.

  • Avatar
    Michael Ho

    Thanks for the reply, though I think I was asking the wrong question =)

    Here's a better question: should this OAuth page be linked to in the Security and Authentication section of the main API documentation page? The current wording of that section suggests that API tokens are only obtained manually through the Zendesk settings page.

  • Avatar
    Meel Velliste

    You're right about that, Michael. I think they should definitely link it to that page. It would have saved me a bunch of time.

  • Avatar
    Fand Ra

    Hi everyone, I'm still not very clear about refresh token. Does Zendesk have it?

    I cannot retrieve it in my app. Is this something we would have after getting a global oauth client?

    After user authorizes the app(oauth2), and after our app successfully receives the token, I make a call to  /api/v2/oauth/tokens/current.json . But refresh_token and expiry_date both come as null. Does this mean that token never expires? Is it going to be different we we get our global oauth? Here is what I get after the call. Btw, I use 'scope=read' for this call, should I use a different 'scope' to be able to get access to refresh_token.

    <200 OK,{"token":{"url":" .../api/v2/oauth/tokens/52339.json","id":51339,"user_id":34563629,"client_id":969,"token":"1047abf810","refresh_token":null,"created_at":"2014-08-23T22:46:40Z","expires_at":null,"used_at":"2014-08-25T22:47:59+05:00","scopes":["read","write"]}

    Would greatly appreciate clarification. Thanks very much.

  • Avatar
    Sean Kinney

    @Esfand: I confirmed with our security team and the lead engineer behind our OAuth implementation that OAuth tokens currently do not expire and must be manually revoked in order for the token to stop working.

  • Avatar
    Fand Ra

    Thanks Sean, Thanks very much for clarification. I really appreciate your quick responses.

    Best regards

  • Avatar
    Michael Ho

    Hey guys, through staring at John Ashton's post above and performing some painful experimentation, I discovered that Zendesk's OAuth flow requires the value of the redirect_uri param to EXACTLY match the URL(s) specified in the "Redirect URLs" section of the OAuth Client setup. This means that I cannot specify dynamic callback parameters in the redirect_uri.

    Specifically, I would like to specify a redirect_uri that looks like this: https://localhost/oauth2/zendesk?zendeskSubdomain=mycompany where "mycompany" is the subdomain of the Zendesk account for which I'm trying to obtain an access token.

    Because Zendesk's OAuth domains vary with each account's subdomain, it would be very useful to be able to specify a callback parameter containing the subdomain in order to avoid having to store the subdomain in the session or in a cookie. This would be especially useful for Global OAuth Clients.

    Any thoughts on allowing callback parameters in the redirect_uri?

  • Avatar
    Meel Velliste

    @Michael: the 'redirect_uri' is not meant to be used dynamically. Use the 'state' parameter instead to pass an arbitrary string to Zendesk and back to you. This is the same with any OAuth2 server that I have seen, not just Zendesk.

  • Avatar
    Michael Ho

    Ah okay that works.  Thanks for the quick response!

  • Avatar
    Prabhu Kannan

    I can't able to create ticket using Access token (OAuth key).

    This is sample program:

    $scopeurl = '/api/v2/tickets.json';
    $ticket = '{"ticket":{"submitter
    id":"620211662","subject":"In proc","requester":{"name":"New Change","phone":"01191996565654"},"comment":{"body":"Value must been add\n\n\n--\nCalltype:Outgoing call\nCall from:\nCall to:01191996565654\nTime of Call:11\/04\/14 12:48 PM\nAnswered by:\nCall Duration:1Seconds\nBill Type :Not Billable\nCall Recording :http:\/\/goo.gl\/zYshZj","authorid":"620211662"},"priority":"urgent","type":"problem","status":"new"}}';
    $create
    ticket = getZDAccessToken($scopeurl, $ticket, "POST",ZENDESKDOMAINNAME,"Authorization: Bearer $accesstoken");
    echo "<pre>";printr($createticket);exit;

    function getZDAccessToken($scopeurl, $json="", $action,$ZDURL,$header)
    {
    $ch = curl
    init();
    curlsetopt($ch, CURLOPTFOLLOWLOCATION, true);
    curlsetopt($ch, CURLOPTMAXREDIRS, 10 );
    curlsetopt($ch, CURLOPTURL, $ZDURL.$url);
    switch($action){
    case "POST":
    curlsetopt($ch, CURLOPTCUSTOMREQUEST, "POST");
    curlsetopt($ch, CURLOPTPOSTFIELDS, $json);
    break;
    case "GET":
    curlsetopt($ch, CURLOPTCUSTOMREQUEST, "GET");
    break;
    case "PUT":
    curlsetopt($ch, CURLOPTCUSTOMREQUEST, "PUT");
    curlsetopt($ch, CURLOPTPOSTFIELDS, $json);
    break;
    case "DELETE":
    curlsetopt($ch, CURLOPTCUSTOMREQUEST, "DELETE");
    break;
    default:
    break;
    }

    curl_setopt($ch, CURLOPT_HTTPHEADER, array($header));
    curl_setopt($ch, CURLOPT_USERAGENT, "MozillaXYZ/1.0");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $output = curl_exec($ch);
    curl_close($ch);
    $decoded = json_decode($output);
    return $decoded;
    

    }

    Example response I got from Zendesk:

    stdClass Object
    (
    [error] => RecordInvalid
    [description] => Record validation errors
    [details] => stdClass Object
    (
    [base] => Array
    (
    [0] => stdClass Object
    (
    [description] => Description: cannot be blank
    )

                )
    
        )
    

    )

    Please give a sample code to create ticket via OAuth.

  • Avatar
    Jessie Schutz

    Hi Prabhu!

    I looks like you were able to get some assistance on this in a ticket. I'm glad things are working for you! Let us know if you need anything else!

Please sign in to leave a comment.