Skip to main content

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:

FlowUse CaseGrant Type
User AuthenticationWeb/mobile apps with user loginAuthorization Code + PKCE
Server-to-ServerBackend services, automationClient 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
ParameterDescription
response_typeMust be code
client_idYour application's client ID
redirect_uriURL to redirect after authentication
scopeSpace-separated list of requested scopes
stateRandom value to prevent CSRF attacks
code_challengePKCE code challenge
code_challenge_methodMust 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
}
Keep Secrets Secure

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 TypeTypical LifetimePurpose
Access Token1 hourAPI authorization
Refresh Token30 daysObtain new access tokens
ID Token1 hourUser 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):

  1. Verify the JWT signature using the public keys from the JWKS endpoint
  2. Check the exp claim to ensure the token hasn't expired
  3. Verify the iss (issuer) matches the expected authorization server
  4. 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 StatusErrorAction
401invalid_tokenToken expired or invalid — refresh or re-authenticate
401insufficient_scopeToken lacks required permissions
400invalid_grantRefresh 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)