# JAR Complete Implementation Guide

> 📖 **Getting started?** Check the [Integration Flow Overview](broken://pages/iMMrTD1lQOMbxxvheKAz) 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](https://gitlab.silvertiger.tech/documents/idp/-/blob/main/developer-guide/pkce-implementation-guide.md)

## Supported JAR Algorithms

Oten IDP supports exactly two algorithms:

| Algorithm | Key Type                      | Security Level | Recommended For            |
| --------- | ----------------------------- | -------------- | -------------------------- |
| **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

```json
{
  "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

```json
{
  "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

```javascript
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

```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

```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

```bash
# 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 -noout
```

#### Using Node.js

```javascript
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

1. Login to <https://developer.oten.com> *(Coming Soon)*
2. Navigate to your application settings
3. Upload your public key file (`jar-public-key.pem`)
4. Note the Key ID assigned by the system

#### Option B: Through Support Request

1. Email your public key to <support@oten.dev>
2. Include your client\_id and application name
3. Wait for confirmation and Key ID

### Step 3: Complete EdDSA Implementation

#### JavaScript/Node.js

```javascript
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

```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

```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 `kid` in JWT header (for EdDSA)

**Solution**:

* Verify algorithm is HS256 or EdDSA
* Check private key matches registered public key
* Ensure `kid` matches 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:

* [ ] Client secret available and correct
* [ ] JAR includes all required JWT claims (`iss`, `aud`, `iat`, `exp`, `jti`)
* [ ] JAR includes all OAuth parameters
* [ ] JAR signed with HS256 algorithm
* [ ] JAR expiration set to reasonable time (≤ 5 minutes)
* [ ] Authorization URL only contains `client_id` and `request` parameters

### For EdDSA:

* [ ] Ed25519 key pair generated
* [ ] Public key registered with Oten
* [ ] Key ID obtained from registration
* [ ] JAR includes all required JWT claims (`iss`, `aud`, `iat`, `exp`, `jti`)
* [ ] JAR includes all OAuth parameters
* [ ] JAR signed with EdDSA algorithm
* [ ] JAR `kid` header matches registered Key ID
* [ ] JAR expiration set to reasonable time (≤ 5 minutes)
* [ ] Authorization URL only contains `client_id` and `request` parameters

## Testing Your JAR Implementation

### Test JAR Structure

```javascript
// 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

```bash
# 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](broken://pages/qErS2kM8hpRocnYPaDQW) - JAR setup requirements
* [Step 3: Authorization Flow](broken://pages/1zKgOda9hrWwNX8K6Zea) - Complete JAR implementation
* [Configuration Reference](broken://pages/XsyPoCRDPUpz0vcP6KkD) - Endpoints and settings
* [Common Errors](broken://pages/ChQqcK8cqX9LLgyQtBJT) - JAR-related error troubleshooting

***

## 🧭 Navigation

* **← Previous**: [Prerequisites](broken://pages/qErS2kM8hpRocnYPaDQW) - Environment setup
* **↑ Overview**: [Integration Flow Overview](broken://pages/iMMrTD1lQOMbxxvheKAz) - See the big picture
* **→ Next**: [Step 1: Choose OAuth Library](broken://pages/CuJNdfiNnGHuqh7gDIK2) - 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**: <support@oten.dev>

**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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://oten.gitbook.io/identity-support/integration/prerequisites/jar-complete-implementation-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
