The Authorization Code is an OAuth 2.0 grant that regular web apps use in order to access an API. In this document we will work through the steps needed in order to implement this: get the user's authorization, get a token and access the API using the token.
Before beginning this tutorial, please:
- Check that your Application's Grant Type property is set appropriately
- Register the API with Auth0
To begin an Authorization Code flow, your web application should first send the user to the authorization URL:
https://YOUR_AUTH0_DOMAIN/authorize?
audience=YOUR_API_AUDIENCE&
scope=YOUR_SCOPE&
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=https://YOUR_APP/callback&
state=YOUR_OPAQUE_VALUE
Where:
-
audience
: The unique identifier of the API the web app wants to access. Use the Identifier value on the Settings tab for the API you created as part of the prerequisites for this tutorial. -
scope
: The scopes which you want to request authorization for. These must be separated by a space. You can request any of the standard OIDC scopes about users, such asprofile
andemail
, custom claims that must conform to a namespaced format, or any scopes supported by the target API (for example,read:contacts
). Includeoffline_access
to get a Refresh Token (make sure that the Allow Offline Access field is enabled in the API Settings). The custom scopes must conform to a namespaced format. For more information on this, refer to the Namespacing Custom Claims panel. -
response_type
: Denotes the kind of credential that Auth0 will return (code vs token). For this flow, the value must becode
. -
client_id
: Your application's Client ID. You can find this value at your Application's Settings. -
state
: An opaque value the application adds to the initial request that Auth0 includes when redirecting back to the application. This value must be used by the application to prevent CSRF attacks, click here to learn more. -
redirect_uri
: The URL to which Auth0 will redirect the browser after authorization has been granted by the user. The Authorization Code will be available in thecode
URL parameter. This URL must be specified as a valid callback URL under your Application's Settings.
For example:
<a href="https://YOUR_AUTH0_DOMAIN/authorize?scope=appointments%20contacts&audience=appointments:api&response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https://YOUR_APP/callback">
Sign In
</a>
The purpose of this call is to obtain consent from the user to invoke the API (specified in audience
) to do certain things (specified in scope
) on behalf of the user. Auth0 will authenticate the user and obtain consent, unless consent has been previously given.
Note that if you alter the value in scope
, Auth0 will require consent to be given again.
Now that you have an Authorization Code, you must exchange it for an Access Token that can be used to call your API. Using the Authorization Code (code
) from the previous step, you will need to POST
to the Token URL:
curl --request POST \
--url 'https://YOUR_AUTH0_DOMAIN/oauth/token' \
--header 'content-type: application/json' \
--data '{"grant_type":"authorization_code","client_id": "YOUR_CLIENT_ID","client_secret": "YOUR_CLIENT_SECRET","code": "YOUR_AUTHORIZATION_CODE","redirect_uri": "https://YOUR_APP/callback"}'
var client = new RestClient("https://YOUR_AUTH0_DOMAIN/oauth/token");
var request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{\"grant_type\":\"authorization_code\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"code\": \"YOUR_AUTHORIZATION_CODE\",\"redirect_uri\": \"https://YOUR_APP/callback\"}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://YOUR_AUTH0_DOMAIN/oauth/token"
payload := strings.NewReader("{\"grant_type\":\"authorization_code\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"code\": \"YOUR_AUTHORIZATION_CODE\",\"redirect_uri\": \"https://YOUR_APP/callback\"}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("content-type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
HttpResponse<String> response = Unirest.post("https://YOUR_AUTH0_DOMAIN/oauth/token")
.header("content-type", "application/json")
.body("{\"grant_type\":\"authorization_code\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"code\": \"YOUR_AUTHORIZATION_CODE\",\"redirect_uri\": \"https://YOUR_APP/callback\"}")
.asString();
var settings = {
"async": true,
"crossDomain": true,
"url": "https://YOUR_AUTH0_DOMAIN/oauth/token",
"method": "POST",
"headers": {
"content-type": "application/json"
},
"processData": false,
"data": "{\"grant_type\":\"authorization_code\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"code\": \"YOUR_AUTHORIZATION_CODE\",\"redirect_uri\": \"https://YOUR_APP/callback\"}"
}
$.ajax(settings).done(function (response) {
console.log(response);
});
var request = require("request");
var options = { method: 'POST',
url: 'https://YOUR_AUTH0_DOMAIN/oauth/token',
headers: { 'content-type': 'application/json' },
body:
{ grant_type: 'authorization_code',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
code: 'YOUR_AUTHORIZATION_CODE',
redirect_uri: 'https://YOUR_APP/callback' },
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
#import <Foundation/Foundation.h>
NSDictionary *headers = @{ @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"grant_type": @"authorization_code",
@"client_id": @"YOUR_CLIENT_ID",
@"client_secret": @"YOUR_CLIENT_SECRET",
@"code": @"YOUR_AUTHORIZATION_CODE",
@"redirect_uri": @"https://YOUR_APP/callback" };
NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_AUTH0_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];
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://YOUR_AUTH0_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\":\"authorization_code\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"code\": \"YOUR_AUTHORIZATION_CODE\",\"redirect_uri\": \"https://YOUR_APP/callback\"}",
CURLOPT_HTTPHEADER => array(
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
import http.client
conn = http.client.HTTPSConnection("")
payload = "{\"grant_type\":\"authorization_code\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"code\": \"YOUR_AUTHORIZATION_CODE\",\"redirect_uri\": \"https://YOUR_APP/callback\"}"
headers = { 'content-type': "application/json" }
conn.request("POST", "/YOUR_AUTH0_DOMAIN/oauth/token", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require 'uri'
require 'net/http'
url = URI("https://YOUR_AUTH0_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/json'
request.body = "{\"grant_type\":\"authorization_code\",\"client_id\": \"YOUR_CLIENT_ID\",\"client_secret\": \"YOUR_CLIENT_SECRET\",\"code\": \"YOUR_AUTHORIZATION_CODE\",\"redirect_uri\": \"https://YOUR_APP/callback\"}"
response = http.request(request)
puts response.read_body
import Foundation
let headers = ["content-type": "application/json"]
let parameters = [
"grant_type": "authorization_code",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"code": "YOUR_AUTHORIZATION_CODE",
"redirect_uri": "https://YOUR_APP/callback"
]
let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)
var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/oauth/token")!,
cachePolicy: .UseProtocolCachePolicy,
timeoutInterval: 10.0)
request.HTTPMethod = "POST"
request.allHTTPHeaderFields = headers
request.HTTPBody = postData
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
println(error)
} else {
let httpResponse = response as? NSHTTPURLResponse
println(httpResponse)
}
})
dataTask.resume()
Where:
grant_type
: This must beauthorization_code
.client_id
: Your application's Client ID.client_secret
: Your application's Client Secret.code
: The Authorization Code received from the initialauthorize
call.redirect_uri
: The URL must match exactly theredirect_uri
passed to/authorize
.
The response contains the access_token
, refresh_token
, id_token
, and token_type
values, for example:
{
"access_token": "eyJz93a...k4laUWw",
"refresh_token": "GEbRxBN...edjnXbL",
"id_token": "eyJ0XAi...4faeEoQ",
"token_type": "Bearer"
}
Note that refresh_token
will only be present in the response if you included the offline_access
scope AND enabled Allow Offline Access for your API in the Dashboard. For more information about Refresh Tokens and how to use them, see our documentation.
Security Warning
It is important to understand that the Authorization Code flow should only be used in cases such as a Regular Web Application where the Client Secret can be safely stored. In cases such as a Single Page Application, the Client Secret is available to the application (in the web browser), so the integrity of the Client Secret cannot be maintained. That is why the Implicit Grant flow is more appropriate in that case.
3. Call the API
Once the access_token
has been obtained it can be used to make calls to the API by passing it as a Bearer Token in the Authorization
header of the HTTP request:
curl --request GET \
--url https://someapi.com/api \
--header 'authorization: Bearer ACCESS_TOKEN' \
--header 'content-type: application/json'
var client = new RestClient("https://someapi.com/api");
var request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer ACCESS_TOKEN");
request.AddHeader("content-type", "application/json");
IRestResponse response = client.Execute(request);
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://someapi.com/api"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("content-type", "application/json")
req.Header.Add("authorization", "Bearer ACCESS_TOKEN")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
HttpResponse<String> response = Unirest.get("https://someapi.com/api")
.header("content-type", "application/json")
.header("authorization", "Bearer ACCESS_TOKEN")
.asString();
var settings = {
"async": true,
"crossDomain": true,
"url": "https://someapi.com/api",
"method": "GET",
"headers": {
"content-type": "application/json",
"authorization": "Bearer ACCESS_TOKEN"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
var request = require("request");
var options = { method: 'GET',
url: 'https://someapi.com/api',
headers:
{ authorization: 'Bearer ACCESS_TOKEN',
'content-type': 'application/json' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
#import <Foundation/Foundation.h>
NSDictionary *headers = @{ @"content-type": @"application/json",
@"authorization": @"Bearer ACCESS_TOKEN" };
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://someapi.com/api"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];
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];
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://someapi.com/api",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"authorization: Bearer ACCESS_TOKEN",
"content-type: application/json"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
import http.client
conn = http.client.HTTPSConnection("someapi.com")
headers = {
'content-type': "application/json",
'authorization': "Bearer ACCESS_TOKEN"
}
conn.request("GET", "/api", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require 'uri'
require 'net/http'
url = URI("https://someapi.com/api")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(url)
request["content-type"] = 'application/json'
request["authorization"] = 'Bearer ACCESS_TOKEN'
response = http.request(request)
puts response.read_body
import Foundation
let headers = [
"content-type": "application/json",
"authorization": "Bearer ACCESS_TOKEN"
]
var request = NSMutableURLRequest(URL: NSURL(string: "https://someapi.com/api")!,
cachePolicy: .UseProtocolCachePolicy,
timeoutInterval: 10.0)
request.HTTPMethod = "GET"
request.allHTTPHeaderFields = headers
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
println(error)
} else {
let httpResponse = response as? NSHTTPURLResponse
println(httpResponse)
}
})
dataTask.resume()
4. 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 refer to Verify Access Tokens.
Optional: Customize the Tokens
You can use Rules to change the returned scopes of the access_token
and/or add claims to it (and the id_token
) with a script like this:
function(user, context, callback) {
// add custom claims to Access Token and ID Token
context.accessToken['http://foo/bar'] = 'value';
context.idToken['http://fiz/baz'] = 'some other value';
// change scope
context.accessToken.scope = ['array', 'of', 'strings'];
callback(null, user, context);
}
Namespacing Custom Claims
Auth0 returns profile information in a structured claim format as defined by the OIDC specification. This means that in order to add custom claims to ID Tokens or Access Tokens, they must conform to a namespaced format to avoid possible collisions with standard OIDC claims. For example, if you choose the namespace https://foo.com/
and you want to add a custom claim named myclaim
, you would name the claim https://foo.com/myclaim
, instead of myclaim
. You can add namespaced claims using Rules.
If you wish to execute special logic unique to the Authorization Code grant, you can look at the context.protocol
property in your rule. If the value is oidc-basic-profile
, then the rule is running during the Authorization Code grant.