## πŸ“˜ Overview: How It Works To securely access a protected API using OAuth2, follow these steps: 1. Request a token from the Auth0 token endpoint using your client_id, client_secret, and audience. 2. Store the token in memory (or cache), along with the expiration time. 3. Reuse the token for subsequent API requests as long as it's still valid. 4. 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. ## 🧾 Token Endpoint Specification ```yaml 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: ```yaml { "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" } ``` ### πŸ“˜ Field-by-Field Explanation ***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 ``` ***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 ``` # Example ### Java (Java 11+) ``` 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 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; } } ``` ### C# ``` 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 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!; } } ``` ### Ruby ``` 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 ```