JK
JustKalm
OAuth 2.0

OAuth Authentication

Secure delegated access for your applications. Support for Authorization Code with PKCE, Client Credentials, and Device Authorization flows.

PKCE required
Token rotation
Granular scopes

Supported Flows

Authorization Code + PKCE

Recommended

SPAs, Mobile apps, CLI tools

high security

Client Credentials

Server-to-server, Machine accounts

high security

Device Authorization

Smart TVs, IoT devices

medium security

Available Scopes

ScopeDescriptionPermissions
valuate:readRead valuation results
Get valuationsList history
valuate:writeCreate valuations
Create valuationsBatch processing
webhooks:manageManage webhooks
Create webhooksDelete webhooks
organization:readRead org info
View membersView settings
organization:adminAdmin organization
Invite membersChange settings
billing:readView billing
View invoicesView usage

Implementation Guide

Step 1-2: Generate PKCE & Authorize
// Step 1: Generate PKCE challenge
import crypto from 'crypto';

function generateCodeVerifier() {
  return crypto.randomBytes(32).toString('base64url');
}

function generateCodeChallenge(verifier: string) {
  return crypto
    .createHash('sha256')
    .update(verifier)
    .digest('base64url');
}

const codeVerifier = generateCodeVerifier();
const codeChallenge = generateCodeChallenge(codeVerifier);

// Step 2: Redirect to authorization
const authUrl = new URL('https://auth.justkalm.com/authorize');
authUrl.searchParams.set('client_id', 'YOUR_CLIENT_ID');
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'valuate:read valuate:write');
authUrl.searchParams.set('code_challenge', codeChallenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
authUrl.searchParams.set('state', crypto.randomBytes(16).toString('hex'));

// Redirect user to authUrl.toString()
Step 3: Exchange Code for Tokens
// Step 3: Exchange code for tokens
const response = await fetch('https://auth.justkalm.com/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    client_id: 'YOUR_CLIENT_ID',
    code: authorizationCode, // From callback URL
    redirect_uri: 'https://yourapp.com/callback',
    code_verifier: codeVerifier, // From step 1
  }),
});

const tokens = await response.json();
// {
//   access_token: "eyJhbGci...",
//   refresh_token: "dGhpcyBpcyBh...",
//   token_type: "Bearer",
//   expires_in: 3600,
//   scope: "valuate:read valuate:write"
// }
Refresh Tokens
// Refresh expired access tokens
const response = await fetch('https://auth.justkalm.com/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'refresh_token',
    client_id: 'YOUR_CLIENT_ID',
    refresh_token: storedRefreshToken,
  }),
});

const newTokens = await response.json();
// Store the new access_token and refresh_token

Security Best Practices

Always Use PKCE

PKCE is required for all public clients (SPAs, mobile apps). It prevents authorization code interception attacks.

Validate State Parameter

Always verify the state parameter matches to prevent CSRF attacks. Generate cryptographically random state values.

Secure Token Storage

Store tokens securely. Use httpOnly cookies or secure storage. Never expose tokens in URLs or localStorage in production.

Minimal Scopes

Request only the scopes you need. Users can see requested permissions and are more likely to approve minimal access.

Create OAuth Application

Register your application to get client credentials.

© 2025 JustKalm. Secure by design.