In this section we will see how we can implement an API for our scenario.
For simplicity, we will keep our implementation solely focused on authentication and authorization. As you will see in the samples, the input timesheet entry will be hard-coded, and the API will not persist the timesheet entry. Instead, it will simply echo back some of the info.
Define the API endpoints
First we need to define the endpoints of our API.
What is an API endpoint?
AnAPI endpointis a unique URL that represents an object. To interact with this object, you need to point your application to its URL. For example, if you had an API that could return either orders or customers, you might configure two endpoints:/orders
and/customers
. Your application would interact with these endpoints using different HTTP methods; for example,POST /orders
could create a new order orGET /orders
could retrieve the dataset of one or more orders.
For this implementation we will only define two endpoints; one for retrieving a list of all timesheets for an employee, and another which will allow an employee to create a new timesheet entry.
AnHTTP GET
request to the/timesheets
endpoint will allow a user to retrieve their timesheets, and anHTTP POST
request to the/timesheets
endpoint will allow a user to add a new timesheet.
See the implementation inNode.js
Secure the Endpoints
When an API receives a request with a bearer Access Token as part of the header, 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 with aMissing or invalid token
error message to the calling app.
The validations that the API should perform are:
Check that the JWT is well formed
Check the signature
Validate the standard claims
JWT.ioprovides a list of libraries that can do most of the work for you: parse the JWT, verify the signature and the claims.
Part of the validation process is to also check the Client permissions (scopes), but we will address this separately in the next paragraph of this document.
For more information on validating Access Tokens, seeValidate Access Tokens.
See the implementation inNode.js
Check the Client's Permissions
By now we have verified that the JWT is valid. The last step is to verify that the client has the permissions required to access the protected resources.
To do so, the API needs to check thescopesof the decoded JWT. This claim is part of the payload and it is a space-separated list of strings.
See the implementation inNode.js
Determine user identity
For both endpoints (retrieving the list of timesheets, and adding a new timesheet) we will need to determine the identity of the user.
For retrieving the list of timesheets this is to ensure that we only return the timesheets belonging to the user making the request, and for adding a new timesheet this is to ensure that the timesheet is associated with the user making the request.
One of the standard JWT claims is thesub
claim which identifies the principal that is the subject to the claim. In the case of the Implicit Grant flow this claim will contain the user's identity, which will be the unique identifier for the Auth0 user. You can use this to associate any information in external systems with a particular user.
You can also use a custom claim to add another attribute of the user - such as their email address - to the Access Token and use that to uniquely identify the user.
See the implementation inNode.js
Implement the Mobile App
In this section we will see how we can implement a mobile application for our scenario.
See the implementation in Android.
Authorize the User
To authorize the user we will implement theAuthorization Code Flow with Proof Key for Code Exchange (PKCE). The mobile application should first send the user to theauthorization URLalong with thecode_challenge
and the method used to generate it:
https://{yourDomain}/authorize? audience=API_AUDIENCE& scope=SCOPE& response_type=code& client_id=YOUR_CLIENT_ID& code_challenge=CODE_CHALLENGE& code_challenge_method=S256& redirect_uri=https://YOUR_APP/callback
Was this helpful?
/
TheGET
request to the authorization URL should include the following values:
Parameter | Description |
---|---|
client_id | The value of your Auth0 Client Id. You can retrieve it from the Settings of your Application at the Auth0 Dashboard. |
audience | The value of your API Identifier. You can retrieve it from the Settings of your API at the Auth0 Dashboard. |
scope | The scopes which determine the claims to be returned in the ID Token and Access Token. For example, a scope of openid will return an ID Token in the response. In our example mobile app, we use the following scopes: create:timesheets read:timesheets openid profile email offline_access . These scopes allow the mobile app to call the API, obtain a Refresh Token, and return the user's name , picture , and email claims in the ID Token. |
response_type | Indicates the Authentication Flow to use. For a mobile application using PKCE, this should be set to code . |
code_challenge | The generated code challenge from the code verifier. You can find instructions on generating a code challenge here. |
code_challenge_method | Method used to generate the challenge. Auth0 supports only S256 . |
redirect_uri | The URL which Auth0 will redirect the browser to after authorization has been granted by the user. The Authorization Code will be available in the code URL parameter. This URL must be specified as a valid callback URL under your Application's Settings. |
See the implementation in Android.
Get the Credentials
After a successful request to the authorization URL, you should receive the following response:
HTTP/1.1 302 FoundLocation: https://{yourDomain}/callback?code=AUTHORIZATION_CODE
Was this helpful?
/
Next you can exchange theauthorization_code
from the response for an Access Token that can be used to call your API. Perform aPOST
request to theToken URLincluding the following data:
- cURL
- C#
- Go
- Java
- Node.JS
- Obj-C
- ...
- PHP
- Python
- Ruby
- Swift
curl --request POST \ --url 'https://{yourDomain}/oauth/token' \ --header 'content-type: application/x-www-form-urlencoded' \ --data grant_type=authorization_code \ --data 'client_id={yourClientId}' \ --data code_verified=YOUR_GENERATED_CODE_VERIFIER \ --data code=YOUR_AUTHORIZATION_CODE \ --data 'redirect_uri=https://{https://yourApp/callback}'
Was this helpful?
/
var client = new RestClient("https://{yourDomain}/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=authorization_code&client_id={yourClientId}&code_verified=YOUR_GENERATED_CODE_VERIFIER&code=YOUR_AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2F{https://yourApp/callback}", ParameterType.RequestBody);IRestResponse response = client.Execute(request);
Was this helpful?
/
package mainimport ("fmt""strings""net/http""io/ioutil")func main() {url := "https://{yourDomain}/oauth/token"payload := strings.NewReader("grant_type=authorization_code&client_id={yourClientId}&code_verified=YOUR_GENERATED_CODE_VERIFIER&code=YOUR_AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2F{https://yourApp/callback}")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))}
Was this helpful?
/
HttpResponse<String> response = Unirest.post("https://{yourDomain}/oauth/token") .header("content-type", "application/x-www-form-urlencoded") .body("grant_type=authorization_code&client_id={yourClientId}&code_verified=YOUR_GENERATED_CODE_VERIFIER&code=YOUR_AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2F{https://yourApp/callback}") .asString();
Was this helpful?
/
var axios = require("axios").default;var options = { method: 'POST', url: 'https://{yourDomain}/oauth/token', headers: {'content-type': 'application/x-www-form-urlencoded'}, data: new URLSearchParams({ grant_type: 'authorization_code', client_id: '{yourClientId}', code_verified: 'YOUR_GENERATED_CODE_VERIFIER', code: 'YOUR_AUTHORIZATION_CODE', redirect_uri: 'https://{https://yourApp/callback}' })};axios.request(options).then(function (response) { console.log(response.data);}).catch(function (error) { console.error(error);});
Was this helpful?
/
#import <Foundation/Foundation.h>NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" };NSMutableData *postData = [[NSMutableData alloc] initWithData:[@"grant_type=authorization_code" dataUsingEncoding:NSUTF8StringEncoding]];[postData appendData:[@"&client_id={yourClientId}" dataUsingEncoding:NSUTF8StringEncoding]];[postData appendData:[@"&code_verified=YOUR_GENERATED_CODE_VERIFIER" dataUsingEncoding:NSUTF8StringEncoding]];[postData appendData:[@"&code=YOUR_AUTHORIZATION_CODE" dataUsingEncoding:NSUTF8StringEncoding]];[postData appendData:[@"&redirect_uri=https://{https://yourApp/callback}" dataUsingEncoding:NSUTF8StringEncoding]];NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/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];
Was this helpful?
/
$curl = curl_init();curl_setopt_array($curl, [ CURLOPT_URL => "https://{yourDomain}/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={yourClientId}&code_verified=YOUR_GENERATED_CODE_VERIFIER&code=YOUR_AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2F{https://yourApp/callback}", CURLOPT_HTTPHEADER => [ "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;}
Was this helpful?
/
import http.clientconn = http.client.HTTPSConnection("")payload = "grant_type=authorization_code&client_id={yourClientId}&code_verified=YOUR_GENERATED_CODE_VERIFIER&code=YOUR_AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2F{https://yourApp/callback}"headers = { 'content-type': "application/x-www-form-urlencoded" }conn.request("POST", "/{yourDomain}/oauth/token", payload, headers)res = conn.getresponse()data = res.read()print(data.decode("utf-8"))
Was this helpful?
/
require 'uri'require 'net/http'require 'openssl'url = URI("https://{yourDomain}/oauth/token")http = Net::HTTP.new(url.host, url.port)http.use_ssl = truehttp.verify_mode = OpenSSL::SSL::VERIFY_NONErequest = Net::HTTP::Post.new(url)request["content-type"] = 'application/x-www-form-urlencoded'request.body = "grant_type=authorization_code&client_id={yourClientId}&code_verified=YOUR_GENERATED_CODE_VERIFIER&code=YOUR_AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2F{https://yourApp/callback}"response = http.request(request)puts response.read_body
Was this helpful?
/
import Foundationlet headers = ["content-type": "application/x-www-form-urlencoded"]let postData = NSMutableData(data: "grant_type=authorization_code".data(using: String.Encoding.utf8)!)postData.append("&client_id={yourClientId}".data(using: String.Encoding.utf8)!)postData.append("&code_verified=YOUR_GENERATED_CODE_VERIFIER".data(using: String.Encoding.utf8)!)postData.append("&code=YOUR_AUTHORIZATION_CODE".data(using: String.Encoding.utf8)!)postData.append("&redirect_uri=https://{https://yourApp/callback}".data(using: String.Encoding.utf8)!)let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/oauth/token")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)request.httpMethod = "POST"request.allHTTPHeaderFields = headersrequest.httpBody = postData as Datalet session = URLSession.sharedlet 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()
Was this helpful?
/
Parameter | Description |
---|---|
grant_type | This must be set to authorization_code . |
client_id | The value of your Auth0 Client Id. You can retrieve it from the Settings of your Application at the Auth0 Dashboard. |
code_verifier | Cryptographically random key that was used to generate the code_challenge passed to authorization URL (/authorize ). |
code | The authorization_code received from the previous authorize call. |
redirect_uri | The URL must match the redirect_uri passed in the previous section to /authorize . |
The response from the Token URL will contain:
{ "access_token": "eyJz93a...k4laUWw", "refresh_token": "GEbRxBN...edjnXbL", "id_token": "eyJ0XAi...4faeEoQ", "token_type": "Bearer", "expires_in":86400}
Was this helpful?
/
access_token: An Access Token for the API, specified by the
audience
.refresh_token: A Refresh Tokenwill only be present if you included the
offline_access
scope AND enabledAllow Offline Accessfor your API in the Dashboard.id_token: An ID Token JWT containing user profile information.
token_type: A string containing the type of token, this will always be a Bearer token.
expires_in: The amount of seconds until the Access Token expires.
You will need to store the above credentials in local storage for use in calling your API and retrieving the user profile.
See the implementation in Android.
Get the User Profile
To retrieve theUser Profile, your mobile application can decode theID Tokenusing one of theJWT libraries. This is done byverifying the signatureandverifying the claimsof the token. After validating the ID Token, you can access its payload containing the user information:
{ "email_verified": false, "email": "test.account@userinfo.com", "clientID": "q2hnj2iu...", "updated_at": "2016-12-05T15:15:40.545Z", "name": "test.account@userinfo.com", "picture": "https://s.gravatar.com/avatar/dummy.png", "user_id": "auth0|58454...", "nickname": "test.account", "created_at": "2016-12-05T11:16:59.640Z", "sub": "auth0|58454..."}
Was this helpful?
/
See the implementation in Android.
Display UI Elements Conditionally Based on Scope
Based on thescope
of the user, you may want to show or hide certain UI elements. To determine the scope issued to a user, you will need to inspect thescope
which was granted when the user was authenticated. This will be a string containing all the scopes, so you therefore need to inspect this string to see whether it contains the requiredscope
and based on that make a decision whether to display a particular UI element.
See the implementation in Android
Call the API
To access secured resources from your API, the authenticated user's Access Token needs to be included in requests that are sent to it. This is accomplished by sending the Access Token in anAuthorization
header using theBearer
scheme.
See the implementation in Android.
Renew the Token
Refresh Tokens must be stored securely by an application since they do not expire and allow a user to remain authenticated essentially forever. If Refresh Tokens are compromised or you no longer need them, you can revoke the Refresh Tokens using theAuthentication API.
To refresh your Access Token, perform aPOST
request to the/oauth/token
endpoint using the Refresh Token from your authorization result.
ARefresh Tokenwill only be present if you included theoffline_access
scope in the previous authorization request and enabledAllow Offline Accessfor your API in the Dashboard.
Your request should include:
- cURL
- C#
- Go
- Java
- Node.JS
- Obj-C
- ...
- PHP
- Python
- Ruby
- Swift
curl --request POST \ --url 'https://{yourDomain}/oauth/token' \ --header 'content-type: application/x-www-form-urlencoded'
Was this helpful?
/
var client = new RestClient("https://{yourDomain}/oauth/token");var request = new RestRequest(Method.POST);request.AddHeader("content-type", "application/x-www-form-urlencoded");IRestResponse response = client.Execute(request);
Was this helpful?
/
package mainimport ("fmt""net/http""io/ioutil")func main() {url := "https://{yourDomain}/oauth/token"req, _ := http.NewRequest("POST", url, nil)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))}
Was this helpful?
/
HttpResponse<String> response = Unirest.post("https://{yourDomain}/oauth/token") .header("content-type", "application/x-www-form-urlencoded") .asString();
Was this helpful?
/
var axios = require("axios").default;var options = { method: 'POST', url: 'https://{yourDomain}/oauth/token', headers: {'content-type': 'application/x-www-form-urlencoded'}};axios.request(options).then(function (response) { console.log(response.data);}).catch(function (error) { console.error(error);});
Was this helpful?
/
#import <Foundation/Foundation.h>NSDictionary *headers = @{ @"content-type": @"application/x-www-form-urlencoded" };NSData *postData = [[NSData alloc] initWithData:[@"undefined" dataUsingEncoding:NSUTF8StringEncoding]];NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/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];
Was this helpful?
/
$curl = curl_init();curl_setopt_array($curl, [ CURLOPT_URL => "https://{yourDomain}/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_HTTPHEADER => [ "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;}
Was this helpful?
/
import http.clientconn = http.client.HTTPSConnection("")headers = { 'content-type': "application/x-www-form-urlencoded" }conn.request("POST", "/{yourDomain}/oauth/token", headers=headers)res = conn.getresponse()data = res.read()print(data.decode("utf-8"))
Was this helpful?
/
require 'uri'require 'net/http'require 'openssl'url = URI("https://{yourDomain}/oauth/token")http = Net::HTTP.new(url.host, url.port)http.use_ssl = truehttp.verify_mode = OpenSSL::SSL::VERIFY_NONErequest = Net::HTTP::Post.new(url)request["content-type"] = 'application/x-www-form-urlencoded'response = http.request(request)puts response.read_body
Was this helpful?
/
import Foundationlet headers = ["content-type": "application/x-www-form-urlencoded"]let postData = NSData(data: "undefined".data(using: String.Encoding.utf8)!)let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/oauth/token")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)request.httpMethod = "POST"request.allHTTPHeaderFields = headersrequest.httpBody = postData as Datalet session = URLSession.sharedlet 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()
Was this helpful?
/
Parameter | Description |
---|---|
grant_type | This must be set to refresh_token . |
client_id | The value of your Auth0 Client Id. You can retrieve it from the Settings of your Application at the Auth0 Dashboard. |
refresh_token | the Refresh Token to use, from the previous authentication result. |
The response will include the new Access Token:
{ "access_token": "eyJz93a...k4laUWw", "refresh_token": "GEbRxBN...edjnXbL", "id_token": "eyJ0XAi...4faeEoQ", "token_type": "Bearer", "expires_in":86400}
Was this helpful?
/
See the implementation in Android.