🔧JAR Complete Implementation Guide
📖 Getting started? Check the Integration Flow Overview to understand the complete integration process.
IMPORTANT: JAR for Confidential Clients Only
Oten Identity Provider requires JAR (JWT-Secured Authorization Request) for CONFIDENTIAL CLIENTS only. Public clients (SPAs/Mobile) must use PKCE instead.
Client Type Requirements:
Confidential Clients: JAR is REQUIRED
Public Clients: JAR is FORBIDDEN, use PKCE Implementation Guide
Supported JAR Algorithms
Oten IDP supports exactly two algorithms:
HS256
Symmetric (client_secret)
Good
Development, Internal Apps
EdDSA
Asymmetric (Ed25519 key pair)
Better
Production, Public Apps
JAR Structure
Every JAR must include these JWT claims:
Required JWT Claims
{
"iss": "your_client_id", // Issuer (your client ID)
"aud": "https://account.oten.com", // Audience (Oten)
"iat": 1640995200, // Issued at (current timestamp)
"exp": 1640995500, // Expires (iat + 300 seconds max)
"jti": "unique-request-id", // JWT ID (unique for each request)
// OAuth parameters
"client_id": "your_client_id",
"redirect_uri": "https://yourapp.com/callback",
"response_type": "code",
"scope": "openid profile email",
"state": "random_state_string",
"code_challenge": "base64url_encoded_challenge",
"code_challenge_method": "S256"
}Optional Claims
{
"nonce": "random_nonce_string", // For OIDC
"max_age": 3600, // Max authentication age
"prompt": "login", // Force re-authentication
"ui_locales": "en-US" // Preferred language
}Method 1: HS256 Implementation (Simpler)
When to Use HS256
✅ Development and testing
✅ Internal applications
✅ When you want simple setup
❌ Not recommended for public applications
Complete HS256 Example
JavaScript/Node.js
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
async function createJARWithHS256(authParams, clientSecret) {
const now = Math.floor(Date.now() / 1000);
const jarPayload = {
// Required JWT claims
iss: authParams.client_id,
aud: 'https://account.oten.com',
iat: now,
exp: now + 300, // 5 minutes max
jti: crypto.randomUUID(),
// OAuth parameters
...authParams
};
return jwt.sign(jarPayload, clientSecret, {
algorithm: 'HS256',
header: {
alg: 'HS256',
typ: 'JWT'
}
});
}
// Usage example
const authParams = {
client_id: 'your_client_id',
redirect_uri: 'https://yourapp.com/callback',
response_type: 'code',
scope: 'openid profile email',
state: crypto.randomUUID(),
code_challenge: 'your_code_challenge',
code_challenge_method: 'S256'
};
const requestJWT = await createJARWithHS256(authParams, process.env.CLIENT_SECRET);
const authURL = `https://account.oten.com/v1/oauth/authorize?client_id=${authParams.client_id}&request=${requestJWT}`;Python
import jwt
import time
import uuid
import os
def create_jar_with_hs256(auth_params, client_secret):
now = int(time.time())
jar_payload = {
# Required JWT claims
'iss': auth_params['client_id'],
'aud': 'https://account.oten.com',
'iat': now,
'exp': now + 300, # 5 minutes max
'jti': str(uuid.uuid4()),
# OAuth parameters
**auth_params
}
return jwt.encode(
jar_payload,
client_secret,
algorithm='HS256',
headers={'alg': 'HS256', 'typ': 'JWT'}
)
# Usage example
auth_params = {
'client_id': 'your_client_id',
'redirect_uri': 'https://yourapp.com/callback',
'response_type': 'code',
'scope': 'openid profile email',
'state': str(uuid.uuid4()),
'code_challenge': 'your_code_challenge',
'code_challenge_method': 'S256'
}
request_jwt = create_jar_with_hs256(auth_params, os.getenv('CLIENT_SECRET'))
auth_url = f"https://account.oten.com/v1/oauth/authorize?client_id={auth_params['client_id']}&request={request_jwt}"Go
package main
import (
"crypto/rand"
"encoding/hex"
"time"
"github.com/golang-jwt/jwt/v5"
)
func createJARWithHS256(authParams map[string]interface{}, clientSecret string) (string, error) {
now := time.Now().Unix()
// Generate unique JTI
jtiBytes := make([]byte, 16)
rand.Read(jtiBytes)
jti := hex.EncodeToString(jtiBytes)
claims := jwt.MapClaims{
// Required JWT claims
"iss": authParams["client_id"],
"aud": "https://account.oten.com",
"iat": now,
"exp": now + 300, // 5 minutes max
"jti": jti,
}
// Add OAuth parameters
for k, v := range authParams {
claims[k] = v
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(clientSecret))
}
// Usage example
authParams := map[string]interface{}{
"client_id": "your_client_id",
"redirect_uri": "https://yourapp.com/callback",
"response_type": "code",
"scope": "openid profile email",
"state": generateRandomString(32),
"code_challenge": "your_code_challenge",
"code_challenge_method": "S256",
}
requestJWT, err := createJARWithHS256(authParams, os.Getenv("CLIENT_SECRET"))
if err != nil {
log.Fatal(err)
}
authURL := fmt.Sprintf("https://account.oten.com/v1/oauth/authorize?client_id=%s&request=%s",
authParams["client_id"], requestJWT)🔐 Method 2: EdDSA Implementation (More Secure)
When to Use EdDSA
✅ Production applications
✅ Public applications
✅ When you need maximum security
✅ When you want to follow best practices
Step 1: Generate Ed25519 Key Pair
Using OpenSSL
# Generate private key
openssl genpkey -algorithm Ed25519 -out jar-private-key.pem
# Extract public key
openssl pkey -in jar-private-key.pem -pubout -out jar-public-key.pem
# View keys (for verification)
openssl pkey -in jar-private-key.pem -text -noout
openssl pkey -in jar-public-key.pem -pubin -text -nooutUsing Node.js
const crypto = require('crypto');
const fs = require('fs');
// Generate key pair
const { publicKey, privateKey } = crypto.generateKeyPairSync('ed25519', {
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// Save keys
fs.writeFileSync('jar-private-key.pem', privateKey);
fs.writeFileSync('jar-public-key.pem', publicKey);Step 2: Register Public Key with Oten
You have two options to register your public key:
Option A: Through Developer Portal
Login to https://developer.oten.com (Coming Soon)
Navigate to your application settings
Upload your public key file (
jar-public-key.pem)Note the Key ID assigned by the system
Option B: Through Support Request
Email your public key to [email protected]
Include your client_id and application name
Wait for confirmation and Key ID
Step 3: Complete EdDSA Implementation
JavaScript/Node.js
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const fs = require('fs');
async function createJARWithEdDSA(authParams, privateKeyPath, keyId) {
const now = Math.floor(Date.now() / 1000);
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
const jarPayload = {
// Required JWT claims
iss: authParams.client_id,
aud: 'https://account.oten.com',
iat: now,
exp: now + 300, // 5 minutes max
jti: crypto.randomUUID(),
// OAuth parameters
...authParams
};
return jwt.sign(jarPayload, privateKey, {
algorithm: 'EdDSA',
header: {
alg: 'EdDSA',
typ: 'JWT',
kid: keyId // Must match registered key ID
}
});
}
// Usage example
const authParams = {
client_id: 'your_client_id',
redirect_uri: 'https://yourapp.com/callback',
response_type: 'code',
scope: 'openid profile email',
state: crypto.randomUUID(),
code_challenge: 'your_code_challenge',
code_challenge_method: 'S256'
};
const requestJWT = await createJARWithEdDSA(
authParams,
'./jar-private-key.pem',
'your-key-id'
);
const authURL = `https://account.oten.com/v1/oauth/authorize?client_id=${authParams.client_id}&request=${requestJWT}`;Python
import jwt
import time
import uuid
from cryptography.hazmat.primitives import serialization
def create_jar_with_eddsa(auth_params, private_key_path, key_id):
now = int(time.time())
# Load private key
with open(private_key_path, 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None
)
jar_payload = {
# Required JWT claims
'iss': auth_params['client_id'],
'aud': 'https://account.oten.com',
'iat': now,
'exp': now + 300, # 5 minutes max
'jti': str(uuid.uuid4()),
# OAuth parameters
**auth_params
}
return jwt.encode(
jar_payload,
private_key,
algorithm='EdDSA',
headers={'alg': 'EdDSA', 'typ': 'JWT', 'kid': key_id}
)
# Usage example
auth_params = {
'client_id': 'your_client_id',
'redirect_uri': 'https://yourapp.com/callback',
'response_type': 'code',
'scope': 'openid profile email',
'state': str(uuid.uuid4()),
'code_challenge': 'your_code_challenge',
'code_challenge_method': 'S256'
}
request_jwt = create_jar_with_eddsa(auth_params, './jar-private-key.pem', 'your-key-id')
auth_url = f"https://account.oten.com/v1/oauth/authorize?client_id={auth_params['client_id']}&request={request_jwt}"Go
package main
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"io/ioutil"
"time"
"github.com/golang-jwt/jwt/v5"
)
func createJARWithEdDSA(authParams map[string]interface{}, privateKeyPath, keyID string) (string, error) {
now := time.Now().Unix()
// Load private key
keyData, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
return "", err
}
block, _ := pem.Decode(keyData)
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", err
}
ed25519Key := privateKey.(ed25519.PrivateKey)
// Generate unique JTI
jtiBytes := make([]byte, 16)
rand.Read(jtiBytes)
jti := hex.EncodeToString(jtiBytes)
claims := jwt.MapClaims{
// Required JWT claims
"iss": authParams["client_id"],
"aud": "https://account.oten.com",
"iat": now,
"exp": now + 300, // 5 minutes max
"jti": jti,
}
// Add OAuth parameters
for k, v := range authParams {
claims[k] = v
}
token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims)
token.Header["kid"] = keyID
return token.SignedString(ed25519Key)
}
// Usage example
authParams := map[string]interface{}{
"client_id": "your_client_id",
"redirect_uri": "https://yourapp.com/callback",
"response_type": "code",
"scope": "openid profile email",
"state": generateRandomString(32),
"code_challenge": "your_code_challenge",
"code_challenge_method": "S256",
}
requestJWT, err := createJARWithEdDSA(authParams, "./jar-private-key.pem", "your-key-id")
if err != nil {
log.Fatal(err)
}
authURL := fmt.Sprintf("https://account.oten.com/v1/oauth/authorize?client_id=%s&request=%s",
authParams["client_id"], requestJWT)🚨 Common Errors and Solutions
Error: "invalid_request" (Missing request parameter)
Cause: Missing request parameter in authorization URL Solution: Always include JAR in request parameter
Error: "invalid_request_object" (Invalid JAR signature)
Cause:
Wrong signing algorithm (must be HS256 or EdDSA)
Wrong private key or client secret
Missing or incorrect
kidin JWT header (for EdDSA)
Solution:
Verify algorithm is HS256 or EdDSA
Check private key matches registered public key
Ensure
kidmatches registered Key ID
Error: "invalid_request" (JAR expired)
Cause: JAR exp claim is too old Solution: Set exp to current time + 5 minutes maximum
Error: "invalid_request" (Invalid audience)
Cause: Wrong aud claim in JAR Solution: Use exact audience: https://account.oten.com
Error: "invalid_request" (Invalid issuer)
Cause: iss claim doesn't match client_id Solution: Set iss to your exact client_id
JAR Validation Checklist
Before testing with Oten IDP:
For HS256:
For EdDSA:
Testing Your JAR Implementation
Test JAR Structure
// Decode JAR to verify structure (for debugging only)
const jwt = require('jsonwebtoken');
function debugJAR(jarToken) {
const decoded = jwt.decode(jarToken, { complete: true });
console.log('Header:', decoded.header);
console.log('Payload:', decoded.payload);
// Verify required claims
const required = ['iss', 'aud', 'iat', 'exp', 'jti', 'client_id', 'redirect_uri', 'response_type'];
const missing = required.filter(claim => !decoded.payload[claim]);
if (missing.length > 0) {
console.error('Missing required claims:', missing);
} else {
console.log('✅ All required claims present');
}
}Test with curl
# Test authorization endpoint with JAR
curl -v "https://account.oten.com/v1/oauth/authorize?client_id=your_client_id&request=your_jar_token"🔗 Related Documentation
Prerequisites - JAR setup requirements
Step 3: Authorization Flow - Complete JAR implementation
Configuration Reference - Endpoints and settings
Common Errors - JAR-related error troubleshooting
🧭 Navigation
← Previous: Prerequisites - Environment setup
↑ Overview: Integration Flow Overview - See the big picture
→ Next: Step 1: Choose OAuth Library - Select JAR-compatible library
Remember: JAR is required for confidential clients only. Public clients must use PKCE instead.
Need Help with JAR Implementation?
If your application cannot implement JAR due to technical constraints, please contact our support team to discuss enabling traditional OAuth flow as a temporary solution:
📧 Contact Support: [email protected]
Include in your request:
Application details and technical constraints
Reason why JAR cannot be implemented
Security measures you have in place
Timeline for potential JAR migration
Security Notice: Traditional OAuth flow has lower security compared to JAR and should only be used temporarily while planning JAR implementation.
Last updated