Adding oauth2 to apis
35 skills that teach AI coding agents to integrate Scalekit auth — agent auth, full-stack login, MCP OAuth 2.1, enterprise SSO, and SCIM. Works with Claude Code, Cursor, Windsurf, and 35+ other agents.
npx -y skills add scalekit-inc/skills --skill adding-oauth2-to-apisAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Implements OAuth 2.0 client-credentials authentication on API endpoints using Scalekit as the authorization server. Use when protecting APIs with machine-to-machine auth, registering API clients for organizations, issuing bearer tokens, validating JWTs via JWKS, or enforcing scopes in middleware.
SKILL.md
6.4 KB, as published. Nobody here has run it
Adding OAuth 2.0 to APIs (Scalekit)
Flow overview
Register client (your app) → Issue client_id + secret (Scalekit) →
API client fetches bearer token → Your server validates JWT + scopes
Security-critical steps (token validation, scope enforcement) use low freedom — follow them exactly.
1. Install
pip install scalekit-sdk-python
# or
npm install @scalekit-sdk/node
Initialize once and reuse:
from scalekit import ScalekitClient
import os
scalekit_client = ScalekitClient(
env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
client_id=os.getenv("SCALEKIT_CLIENT_ID"),
client_secret=os.getenv("SCALEKIT_CLIENT_SECRET")
)
Required env vars: SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, SCALEKIT_CLIENT_SECRET.
2. Register an API client for an organization
One organization can have multiple API clients. Registration returns client_id and plain_secret — plain_secret is shown only once; never stored by Scalekit.
from scalekit.v1.clients.clients_pb2 import OrganizationClient
response = scalekit_client.m2m_client.create_organization_client(
organization_id="<ORG_ID>",
m2m_client=OrganizationClient(
name="GitHub Actions Deployment Service",
description="Deploys to production via GitHub Actions",
scopes=["deploy:applications", "read:deployments"], # resource:action pattern
audience=["deployment-api.acmecorp.com"],
custom_claims=[
{"key": "github_repository", "value": "acmecorp/inventory-service"},
{"key": "environment", "value": "production_us"}
],
expiry=3600 # seconds; default 3600
)
)
client_id = response.client.client_id
plain_secret = response.plain_secret # store this securely; not retrievable again
cURL equivalent (if not using SDK):
curl -X POST "$SCALEKIT_ENVIRONMENT_URL/api/v1/organizations/<ORG_ID>/clients" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <SCALEKIT_ACCESS_TOKEN>" \
-d '{
"name": "GitHub Actions Deployment Service",
"scopes": ["deploy:applications", "read:deployments"],
"audience": ["deployment-api.acmecorp.com"],
"expiry": 3600
}'
Scope naming convention: use
resource:action(e.g.deployments:read,applications:create).
3. API client fetches a bearer token
This step runs inside the API client's code, not your server. Shown here for reference.
curl -X POST "$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>"
Response:
{
"access_token": "<JWT>",
"token_type": "Bearer",
"expires_in": 86399,
"scope": "deploy:applications read:deployments"
}
The client sends this JWT in Authorization: Bearer <JWT> on every API request.
4. Validate the JWT on your API server
Do this on EVERY request. Never trust unverified tokens.
Python (SDK handles JWKS automatically)
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
try:
claims = scalekit_client.validate_access_token_and_get_claims(token=token)
# claims["scopes"] → list of granted scopes
except Exception:
return 401 # invalid or expired
Node.js (manual JWKS + JWT verify)
import jwksClient from 'jwks-rsa';
import jwt from 'jsonwebtoken';
const jwks = jwksClient({
jwksUri: `${process.env.SCALEKIT_ENVIRONMENT_URL}/.well-known/jwks.json`,
cache: true
});
async function verifyToken(token) {
const decoded = jwt.decode(token, { complete: true });
const key = await jwks.getSigningKey(decoded.header.kid);
return jwt.verify(token, key.getPublicKey(), {
algorithms: ['RS256'],
complete: true
}).payload; // contains scopes, sub, iss, exp, oid, etc.
}
Decoded JWT payload structure:
{
"client_id": "m2morg_69038819013296423",
"oid": "org_59615193906282635",
"scopes": ["deploy:applications", "read:deployments"],
"iss": "<SCALEKIT_ENVIRONMENT_URL>",
"exp": 1745305340
}
5. Enforce scopes in middleware
Flask (Python)
import functools
from flask import request, jsonify
def require_scope(scope):
def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
if not token:
return jsonify({"error": "Missing token"}), 401
try:
claims = scalekit_client.validate_access_token_and_get_claims(token=token)
except Exception:
return jsonify({"error": "Invalid token"}), 401
if scope not in claims.get("scopes", []):
return jsonify({"error": "Insufficient permissions"}), 403
return f(*args, **kwargs)
return wrapper
return decorator
# Usage:
# @app.route('/deploy', methods=['POST'])
# @require_scope('deploy:applications')
# def deploy(): ...
Express (Node.js)
function requireScope(scope) {
return async (req, res, next) => {
const token = (req.headers.authorization || '').replace('Bearer ', '');
if (!token) return res.status(401).send('Missing token');
try {
const payload = await verifyToken(token); // from step 4
if (!payload.scopes?.includes(scope))
return res.status(403).send('Insufficient permissions');
req.tokenClaims = payload;
next();
} catch {
res.status(401).send('Invalid token');
}
};
}
// Usage:
// app.post('/deploy', requireScope('deploy:applications'), handler);
Key rules
plain_secretis returned once only — instruct customers to store it immediately.- Always validate tokens server-side before trusting claims.
- Cache JWKS keys (avoid fetching on every request); rotate on
kidmismatch. - Use
resource:actionscope naming for clarity. - An
organization_idmaps to one customer; multiple API clients per org are supported.