To securely access a protected API using OAuth2, follow these steps:
- Request a token from the Auth0 token endpoint using your client_id, client_secret, and audience.
- Store the token in memory (or cache), along with the expiration time.
- Reuse the token for subsequent API requests as long as it's still valid.
- Refresh the token by repeating step 1 when it's close to or past expiration.
โ ๏ธ Token Expiration Notice
Tokens obtained via the Client Credentials flow typically expire in 3600 seconds (1 hour). Itโs recommended to subtract a small buffer (e.g., 60 seconds) when checking for expiration to avoid race conditions.
POST https://bookabletech.uk.auth0.com/oauth/token
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "api.bookabletech.com"
}Expected response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtMUDc4SkVmTWdERi1wdXEyckduZyJ9.eyJodHRwczovL2FwaS5ib29rYWJsZXRlY2guY29tL2FwcF9tZXRhZGF0YSI6eyJzbHVnIjoiYm90dG9tbGVzc2JydW5jaCJ9LCJpc3MiOiJodHRwczovL2Jvb2thYmxldGVjaC51ay5hdXRoMC5jb20vIiwic3ViIjoiVWVjYnVoQ3Z5TnA1Vk5uNWNkajJodVhiSUJ2Z0RMNlpAY2xpZW50cyIsImF1ZCI6ImFwaS5ib29rYWJsZXRlY2guY29tIiwiaWF0IjoxNzQ3NjQ5NjE2LCJleHAiOjE3NTAyNDE2MTYsInNjb3BlIjoidmVudWU6cmVhZCB2ZW51ZS1ib29raW5nOmNyZWF0ZSIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyIsImF6cCI6IlVlY2J1aEN2eU5wNVZObjVjZGoyaHVYYklCdmdETDZaIiwicGVybWlzc2lvbnMiOlsidmVudWU6cmVhZCIsInZlbnVlLWJvb2tpbmc6Y3JlYXRlIl19.PFcKgigRn5TVcUIxxgnNE9pcjQ9gZ2wb3MplGaMkOX_zSvawKXY189F8FPS58HsDn3IXJqzJVHXRIJuaNJJYY96tqYtMD0xou6zyYhLMctdHpMBfnmBVyH7WnMcdN_chaZ83IVn1yFwtyPyAMvgkwaprs16I6ib3ejhhn0sB4NgXIo7I8Pacl2-9amLVsv1mjs66n2DX0_jY29pBCM0hvcBvK-XB37kXHEFkUb5mJizZBW2Q_tk0Uh48JitIg-RIeZVTKkGCYgXYuaEwrDL62oLa2eig_N6CTveJm3qgpqquwROsyvoq0eRVX0fSSrJ5Ezo72Kh1um4F8YYNUlVhuA",
"scope": "venue:read venue-booking:create",
"expires_in": 3600,
"token_type": "Bearer"
}access_token This is the actual JWT (JSON Web Token) returned by Auth0.
It is used to authenticate requests to your protected API.
Include it in the Authorization header of your requests:
Authorization: Bearer <access_token>scope Lists the permissions (scopes) granted to this token.
In this example:
venue:read โ allows read access to venue data.
venue-booking:create โ allows creation of bookings.
expires_in Represents the lifetime of the token in seconds.
3600 seconds = 1 hour.
You should always track this value and refresh the token when it's close to expiring.
๐ Important
Do not hardcode the expiration time โ always read and store the expires_in value
token_type Indicates how the token should be used.
"Bearer" in this context, meaning:
Authorization: Bearer <access_token>
import java.net.http.*;
import java.net.URI;
import java.time.Instant;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TokenManager {
private static final HttpClient client = HttpClient.newHttpClient();
private static final ObjectMapper mapper = new ObjectMapper();
private static String accessToken;
private static Instant expirationTime;
public static String getAccessToken() throws Exception {
if (accessToken != null && Instant.now().isAfter(expirationTime.minusSeconds(300))) return accessToken;
// Refresh token
String body = """
{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "api.bookabletech.com"
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://bookabletech.uk.auth0.com/oauth/token"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
var tokenData = mapper.readValue(response.body(), Map.class);
accessToken = (String) tokenData.get("access_token");
int expiresIn = (Integer) tokenData.get("expires_in");
expirationTime = Instant.now().plusSeconds(expiresIn);
return accessToken;
}
}
using System.Net.Http.Json;
using System.Text.Json;
using System.Net.Http.Json;
using System.Text.Json;
public class TokenManager
{
private static string? _accessToken;
private static DateTime _expirationTime;
private static readonly HttpClient Client = new();
public static async Task<string> GetAccessTokenAsync()
{
if (_accessToken != null && DateTime.UtcNow < _expirationTime.AddMinutes(-5)) return _accessToken!;
var payload = new
{
grant_type = "client_credentials",
client_id = "YOUR_CLIENT_ID",
client_secret = "YOUR_CLIENT_SECRET",
audience = "api.bookabletech.com"
};
var response = await Client.PostAsJsonAsync("https://bookabletech.uk.auth0.com/oauth/token", payload);
var content = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(content);
_accessToken = doc.RootElement.GetProperty("access_token").GetString();
var expiresIn = doc.RootElement.GetProperty("expires_in").GetInt32();
_expirationTime = DateTime.UtcNow.AddSeconds(expiresIn);
return _accessToken!;
}
}require 'net/http'
require 'json'
require 'time'
class TokenManager
@@access_token = nil
@@expiration_time = Time.now
def self.get_access_token
if @@access_token.nil? || Time.now >= (@@expiration_time - 300)
uri = URI("https://bookabletech.uk.auth0.com/oauth/token")
request = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
request.body = {
grant_type: 'client_credentials',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
audience: 'api.bookabletech.com'
}.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
data = JSON.parse(response.body)
@@access_token = data["access_token"]
@@expiration_time = Time.now + data["expires_in"].to_i
end
@@access_token
end
end