Authentication
Oproto uses OAuth 2.0 for secure API authentication. This guide covers the authentication flows available for different integration scenarios.
Overview
Oproto supports two primary authentication patterns:
| Flow | Use Case | Grant Type |
|---|---|---|
| User Authentication | Web/mobile apps with user login | Authorization Code + PKCE |
| Server-to-Server | Backend services, automation | Client Credentials |
User Authentication
For applications where end users log in directly. This flow uses the OAuth 2.0 Authorization Code grant with PKCE (Proof Key for Code Exchange) for enhanced security.
Flow Overview
Step 1: Generate PKCE Values
Before initiating the flow, generate a code verifier and code challenge:
// Generate a random code verifier (43-128 characters)
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
// Create the code challenge from the verifier
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const digest = await crypto.subtle.digest('SHA-256', data);
return base64UrlEncode(new Uint8Array(digest));
}
function base64UrlEncode(buffer) {
return btoa(String.fromCharCode(...buffer))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Step 2: Redirect to Authorization Endpoint
Redirect the user to the authorization endpoint:
GET /oauth2/authorize
?response_type=code
&client_id={your_client_id}
&redirect_uri={your_redirect_uri}
&scope=openid profile email
&state={random_state_value}
&code_challenge={code_challenge}
&code_challenge_method=S256
| Parameter | Description |
|---|---|
response_type | Must be code |
client_id | Your application's client ID |
redirect_uri | URL to redirect after authentication |
scope | Space-separated list of requested scopes |
state | Random value to prevent CSRF attacks |
code_challenge | PKCE code challenge |
code_challenge_method | Must be S256 |
Step 3: Exchange Authorization Code for Tokens
After the user authenticates, they're redirected back with an authorization code:
curl -X POST https://auth.oproto.io/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id={your_client_id}" \
-d "code={authorization_code}" \
-d "redirect_uri={your_redirect_uri}" \
-d "code_verifier={code_verifier}"
Response:
{
"access_token": "eyJraWQiOiJ...",
"id_token": "eyJraWQiOiJ...",
"refresh_token": "eyJjdHkiOiJ...",
"token_type": "Bearer",
"expires_in": 3600
}
Server-to-Server Authentication
For backend services and automated integrations that don't involve user interaction. This flow uses the OAuth 2.0 Client Credentials grant.
Obtaining Tokens
curl -X POST https://auth.oproto.io/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic {base64(client_id:client_secret)}" \
-d "grant_type=client_credentials" \
-d "scope=api/read api/write"
Or with credentials in the body:
curl -X POST https://auth.oproto.io/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id={your_client_id}" \
-d "client_secret={your_client_secret}" \
-d "scope=api/read api/write"
Response:
{
"access_token": "eyJraWQiOiJ...",
"token_type": "Bearer",
"expires_in": 3600
}
Never expose your client secret in client-side code, version control, or logs. Use environment variables or a secrets manager.
Token Management
Using Access Tokens
Include the access token in the Authorization header for all API requests:
curl -X GET https://api.oproto.io/v1/companies \
-H "Authorization: Bearer {access_token}" \
-H "Content-Type: application/json"
Token Lifetimes
| Token Type | Typical Lifetime | Purpose |
|---|---|---|
| Access Token | 1 hour | API authorization |
| Refresh Token | 30 days | Obtain new access tokens |
| ID Token | 1 hour | User identity claims |
Refreshing Access Tokens
When an access token expires, use the refresh token to obtain a new one without requiring user interaction:
curl -X POST https://auth.oproto.io/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id={your_client_id}" \
-d "refresh_token={refresh_token}"
Response:
{
"access_token": "eyJraWQiOiJ...",
"id_token": "eyJraWQiOiJ...",
"token_type": "Bearer",
"expires_in": 3600
}
Token Validation
Access tokens are JWTs that can be validated locally or via introspection:
Local Validation (recommended for performance):
- Verify the JWT signature using the public keys from the JWKS endpoint
- Check the
expclaim to ensure the token hasn't expired - Verify the
iss(issuer) matches the expected authorization server - Verify the
aud(audience) includes your API identifier
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const client = jwksClient({
jwksUri: 'https://auth.oproto.io/.well-known/jwks.json',
cache: true,
rateLimit: true
});
async function validateToken(token) {
const decoded = jwt.decode(token, { complete: true });
const key = await client.getSigningKey(decoded.header.kid);
return jwt.verify(token, key.getPublicKey(), {
issuer: 'https://auth.oproto.io',
algorithms: ['RS256']
});
}
Handling Token Errors
| HTTP Status | Error | Action |
|---|---|---|
| 401 | invalid_token | Token expired or invalid — refresh or re-authenticate |
| 401 | insufficient_scope | Token lacks required permissions |
| 400 | invalid_grant | Refresh token expired — re-authenticate |
Example error response:
{
"error": "invalid_token",
"error_description": "The access token has expired"
}
Best Practices
Security Recommendations
- Always use HTTPS for all authentication requests
- Use PKCE for all user-facing applications (web, mobile, desktop)
- Store tokens securely — use secure storage mechanisms appropriate for your platform
- Implement token refresh proactively before expiration to avoid interruptions
- Validate tokens on every API request
- Use short-lived access tokens and refresh them as needed
Implementation Tips
- Cache the JWKS keys to avoid fetching them on every request
- Implement automatic token refresh when tokens are close to expiration
- Handle token errors gracefully with appropriate retry logic
- Log authentication failures for security monitoring (but never log tokens)
Quick Links
- API Reference — Explore the API endpoints
- OpenID Configuration — Discovery document