How to implement the Client Credentials Grant
The Client Credentials Grant (defined in RFC 6749, section 4.4) allows an application to request an Access Token using its Client Id and Client Secret. It is used for non interactive applications (a CLI, a daemon, or a Service running on your backend) where the token is issued to the application itself, instead of an end user.
Before beginning this tutorial, please:
-
Make sure you that your application has the
Client Credentials
grant type enabled. Regular web applications and machine to machine applications have it enabled by default. -
Register the API with Auth0 with the required scopes.
-
Authorize the application to call the API by creating a Client Grant either using the Dashboard or using the Management API.
Ask for a token
To ask Auth0 for tokens for any of your authorized applications, perform a POST
operation to the https://YOUR_DOMAIN/oauth/token
endpoint with a payload in the following format:
to configure this snippet with your account
curl --request POST \
--url 'https://YOUR_DOMAIN/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data 'client_id=YOUR_CLIENT_ID' \
--data client_secret=YOUR_CLIENT_SECRET \
--data audience=YOUR_API_IDENTIFIER
to configure this snippet with your account
var client = new RestClient("https://YOUR_DOMAIN/oauth/token");
var request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&audience=YOUR_API_IDENTIFIER", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
to configure this snippet with your account
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://YOUR_DOMAIN/oauth/token"
payload := strings.NewReader("grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&audience=YOUR_API_IDENTIFIER")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("content-type", "application/x-www-form-urlencoded")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
to configure this snippet with your account
HttpResponse<String> response = Unirest.post("https://YOUR_DOMAIN/oauth/token")
.header("content-type", "application/x-www-form-urlencoded")
.body("grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&audience=YOUR_API_IDENTIFIER")
.asString();
to configure this snippet with your account
var request = require("request");
var options = {
method: 'POST',
url: 'https://YOUR_DOMAIN/oauth/token',
headers: {'content-type': 'application/x-www-form-urlencoded'},
form: {
grant_type: 'client_credentials',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
audience: 'YOUR_API_IDENTIFIER'
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
to configure this snippet with your account
#import <Foundation/Foundation.h>
NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" };
NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"grant_type=client_credentials" dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[@"&client_id=YOUR_CLIENT_ID" dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[@"&client_secret=YOUR_CLIENT_SECRET" dataUsingEncoding:NSUTF8StringEncoding]];
[postData appendData:[@"&audience=YOUR_API_IDENTIFIER" dataUsingEncoding:NSUTF8StringEncoding]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/oauth/token"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:@"POST"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"%@", error);
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(@"%@", httpResponse);
}
}];
[dataTask resume];
to configure this snippet with your account
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://YOUR_DOMAIN/oauth/token",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&audience=YOUR_API_IDENTIFIER",
CURLOPT_HTTPHEADER => array(
"content-type: application/x-www-form-urlencoded"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
to configure this snippet with your account
import http.client
conn = http.client.HTTPSConnection("")
payload = "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&audience=YOUR_API_IDENTIFIER"
headers = { 'content-type': "application/x-www-form-urlencoded" }
conn.request("POST", "/YOUR_DOMAIN/oauth/token", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
to configure this snippet with your account
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://YOUR_DOMAIN/oauth/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/x-www-form-urlencoded'
request.body = "grant_type=client_credentials&client_id=%24%7Baccount.clientId%7D&client_secret=YOUR_CLIENT_SECRET&audience=YOUR_API_IDENTIFIER"
response = http.request(request)
puts response.read_body
to configure this snippet with your account
import Foundation
let headers = ["content-type": "application/x-www-form-urlencoded"]
let postData = NSMutableData(data: "grant_type=client_credentials".data(using: String.Encoding.utf8)!)
postData.append("&client_id=YOUR_CLIENT_ID".data(using: String.Encoding.utf8)!)
postData.append("&client_secret=YOUR_CLIENT_SECRET".data(using: String.Encoding.utf8)!)
postData.append("&audience=YOUR_API_IDENTIFIER".data(using: String.Encoding.utf8)!)
let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/oauth/token")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Where:
grant_type
: This must beclient_credentials
.client_id
: Your application's Client ID. You can find this value at the application's settings tab.client_secret
: Your application's Client Secret. You can find this value at the application's settings tab.audience
: The Identifier value on the Settings tab for the API you created as part of the prerequisites for this tutorial.
The response contains a signed JSON Web Token (JWT), the token's type (which is Bearer
), and in how much time it expires in Unix time (86400 seconds, which means 24 hours).
{
"access_token":"eyJz93a...k4laUWw",
"token_type":"Bearer",
"expires_in":86400
}
If you decode the access_token
you will see that it contains the following claims:
to configure this snippet with your account
{
"iss": "https://YOUR_DOMAIN/",
"sub": "YOUR_MACHINE_TO_MACHINE_APPLICATION_CLIENT_ID@clients",
"aud": "YOUR_API_IDENTIFIER",
"exp": 1489715431, // unix timestamp of the token's expiration date,
"iat": 1489679431, // unix timestamp of the token's creation date,
"scope": ""
}
Modify scopes and claims
You can change the scopes and add custom claims to the Access Token you got, using Hooks.
Hooks allow you to customize the behavior of Auth0 using Node.js code. They are actually Webtasks, associated with specific extensibility points of the Auth0 platform (like the Client Credentials grant). Auth0 invokes the Hooks at runtime to execute your custom logic.
For more information and details on how to do that refer to Using Hooks with Client Credentials Grant.
Verify the token
Once your API receives a request with a Bearer Access Token, the first thing to do is to validate the token. This consists of a series of steps, and if any of these fails then the request must be rejected.
For details on the validations that should be performed by the API, refer to Validate an Access Token. You can find examples on how to do it in different platforms in the Quickstarts for backend applications.
Sample application
For an example implementation see the Server Client + API architecture scenario.
This is a series of tutorials that describe a scenario for a fictitious company that wants to implement a Timesheets API and send timesheets entries from a server process using OAuth 2.0. The tutorials are accompanied by a sample that you can access in GitHub.