Add auth to your APIs
Secure your APIs in minutes with OAuth 2.0 client credentials, scoped access, and JWT validation using Scalekit
APIs are a fundamental mechanism for systems to communicate and exchange data. By exposing API endpoints, applications enable customers, partners, and external systems to interact seamlessly, creating deeper integrations and increasing the stickiness to your app and it’s usecases.
Exposing APIs without any authentication is like leaving your door wide open for anyone to walk in. Anyone can access your API endpoints, potentially causing security breaches, data leaks, and unauthorized usage.
In this guide let’s take a look at how your can add OAuth2.0 based client credentials authentication to your APIs to ensure only authorized clients can consume your APIs.
Review the authentication flow
-
Installation
Section titled “Installation”Scalekit becomes the authorization server for your APIs. Using Scalekit provides necessary methods to register and authenticate API clients.
Terminal window pip install scalekit-sdk-pythonAlternatively, you can use the REST APIs directly.
-
Enable API client registration for your customers
Section titled “Enable API client registration for your customers”To enable secure API access, you must first register an application as an API client. This process generates unique credentials that will authenticate the client when interacting with your API.
Upon registration, your app obtains a client ID and secret — essentially an application-level authentication mechanism. These credentials should be securely shared with your API client developers for integration (usually via view-once UI).
POST /organizations/{organization_id}/clients # For authentication details, see: http://docs.scalekit.com/apis#description/authenticationcurl -L '<SCALEKIT_ENVIRONMENT_URL>/api/v1/organizations/<ORGANIZATION_ID>/clients' \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <SCALEKIT_ACCESS_TOKEN>' \-d '{"name": "GitHub Actions Deployment Service","description": "Service account for GitHub Actions to deploy applications to production","custom_claims": [{"key": "github_repository","value": "acmecorp/inventory-service"},{"key": "environment","value": "production_us"}],"scopes": ["deploy:applications","read:deployments"],"audience": ["deployment-api.acmecorp.com"],"expiry": 3600}'Sample response {"client": {"client_id": "m2morg_68315758685323697","secrets": [{"id": "sks_68315758802764209","create_time": "2025-04-16T06:56:05.360Z","update_time": "2025-04-16T06:56:05.367190455Z","secret_suffix": "UZ0X","status": "ACTIVE","last_used_time": "2025-04-16T06:56:05.360Z"}],"name": "GitHub Actions Deployment Service","description": "Service account for GitHub Actions to deploy applications to production","organization_id": "org_59615193906282635","create_time": "2025-04-16T06:56:05.290Z","update_time": "2025-04-16T06:56:05.292145150Z","scopes": ["deploy:applications","read:deployments"],"audience": ["deployment-api.acmecorp.com"],"custom_claims": [{"key": "github_repository","value": "acmecorp/inventory-service"},{"key": "environment","value": "production_us"}]},"plain_secret": "test_ly8G57h0ErRJSObJI6dShkoaq6bigo11Dxcfa6reKG1kKNVbqBKW4H5Ctmb5UZ0X"}Before registering an API client, you must first authenticate with Scalekit by obtaining a bearer token using your environment’s API credentials.
4 collapsed linesfrom scalekit.v1.clients.clients_pb2 import OrganizationClientorg_id = "<SCALEKIT_ORGANIZATION_ID>"api_client = OrganizationClient(name="GitHub Actions Deployment Service",description="Service account for GitHub Actions to deploy applications to production",custom_claims=[{"key": "github_repository","value": "acmecorp/inventory-service"},{"key": "environment","value": "production_us"}],scopes=["deploy:applications", "read:deployments"],audience=["deployment-api.acmecorp.com"],expiry=3600)response = scalekit_client.m2m_client.create_organization_client(organization_id=org_id,m2m_client=api_client)# Persist the generated credentials securely in your applicationclient_id = response.client.client_idplain_secret = response.plain_secretSample response {"client": {"client_id": "m2morg_68315758685323697","secrets": [{"id": "sks_68315758802764209","create_time": "2025-04-16T06:56:05.360Z","update_time": "2025-04-16T06:56:05.367190455Z","secret_suffix": "UZ0X","status": "ACTIVE","last_used_time": "2025-04-16T06:56:05.360Z"}],"name": "GitHub Actions Deployment Service","description": "Service account for GitHub Actions to deploy applications to production","organization_id": "org_59615193906282635","create_time": "2025-04-16T06:56:05.290Z","update_time": "2025-04-16T06:56:05.292145150Z","scopes": ["deploy:applications","read:deployments"],"audience": ["deployment-api.acmecorp.com"],"custom_claims": [{"key": "github_repository","value": "acmecorp/inventory-service"},{"key": "environment","value": "production_us"}]},"plain_secret": "test_ly8G57h0ErRJSObJI6dShkoaq6bigo11Dxcfa6reKG1kKNVbqBKW4H5Ctmb5UZ0X"}API client registration parameters
Parameter Type Description name
string
A descriptive name for the API client (e.g., “GitHub Actions Deployment Service”) description
string
A detailed explanation of the client’s purpose and usage custom_claims
array
Key-value pairs that provide additional context about the client. Each claim must have a keyandvaluefieldscopes
array
List of permissions the client needs (e.g., [“deploy:applications”, “read:deployments”]) audience
array
List of API endpoints this client will access (e.g., [“deployment-api.acmecorp.com”]) expiry
number
Token expiration time in seconds. Defaults to 3600 (1 hour) -
Access token (type: Bearer) issued using client credentials
Section titled “Access token (type: Bearer) issued using client credentials”After registration, the API client uses them with your Scalekit environment using its
client_idandclient_secret(theplain_secretobtained earlier) to request an access token.The client sends a POST request to the
/oauth/tokenendpoint:POST /oauth/token curl -X POST \"https://<SCALEKIT_ENVIRONMENT_URL>/oauth/token" \-H "Content-Type: application/x-www-form-urlencoded" \-d "grant_type=client_credentials" \-d "client_id=<API_CLIENT_ID>" \-d "client_secret=<API_CLIENT_SECRET>" \client_id = "API_CLIENT_ID"client_secret = "API_CLIENT_SECRET"token_response = scalekit_client.generate_client_token(client_id=client_id,client_secret=client_secret)Upon successful authentication, Scalekit issues a JWT access token.
Access token response {"access_token":"<API_CLIENT_JWT_ACCESS_TOKEN>","token_type":"Bearer","expires_in":86399,// Same scopes that were granted during client registration"scope":"deploy:applications read:deployments"}The client includes this access token in the
Authorizationheader of subsequent requests to your API server. -
Validate and authenticate API client’s access tokens
Section titled “Validate and authenticate API client’s access tokens”Your API server must validate the incoming JWT access token to ensure the request originates from a trusted API client and that the token is legitimate.
Validate the token in two steps:
-
Retrieve the public key: Fetch the appropriate public key from your Scalekit environment’s JSON Web Key Set (JWKS) endpoint. Use the
kid(Key ID) from the JWT header to identify the correct key. Cache the key according to standard JWKS practices.import jwksClient from 'jwks-rsa';const client = jwksClient({jwksUri: 'YOUR_JWKS_URI',cache: true});async function getPublicKey(header: any): Promise<string> {return new Promise((resolve, reject) => {client.getSigningKey(header.kid, (err, key) => {if (err) reject(err);else resolve(key.getPublicKey());});});}# This is automatically handled by Scalekit SDKtoken = token_response["<API_CLIENT_JWT_ACCESS_TOKEN>"]claims = scalekit_client.validate_access_token_and_get_claims(token=token) -
Verify the token signature: Use the retrieved public key and a JWT library to verify the token’s signature and claims (like issuer, audience, and expiration).
import jwt from 'jsonwebtoken';async function verifyToken(token: string, publicKey: string) {try {const decoded = jwt.decode(token, { complete: true });const verified = jwt.verify(token, publicKey, {algorithms: ['RS256'],complete: true});return verified.payload;} catch (error) {throw new Error('Token verification failed');}}# Token from the incoming API request's authorization headertoken = token_response["<API_CLIENT_JWT_ACCESS_TOKEN>"]claims = scalekit_client.validate_access_token_and_get_claims(token=token)
Upon successful token verification, your API server gains confidence in the request’s legitimacy and can proceed to process the request, leveraging the authorization scopes embedded within the token.
-