Agent77

OIDC Discovery Setup

Agent77 verifies user JWTs by fetching your public keys via standard OIDC discovery. You only need to serve two static-ish JSON endpoints — no full OIDC provider required.

1. OpenID Configuration

Serve a JSON response at https://<your-domain>/.well-known/openid-configuration:

{
  "issuer": "https://app.example.com",
  "jwks_uri": "https://app.example.com/.well-known/jwks.json",
  "id_token_signing_alg_values_supported": ["RS256"],
  "response_types_supported": ["id_token"],
  "subject_types_supported": ["public"]
}

The critical field is jwks_uri — Agent77 uses it to find your public keys. The issuer must match the iss claim in your JWTs.

2. JWKS Endpoint

Serve your public key(s) at the URL specified in jwks_uri:

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "my-key-1",
      "use": "sig",
      "alg": "RS256",
      "n": "<base64url-encoded modulus>",
      "e": "AQAB"
    }
  ]
}

The kid must match the kid header in JWTs issued by your token endpoint. You can include multiple keys to support rotation.

RSA Key Pair Generation

Python

# generate_keys.py — run once
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)

# Save private key (keep secret!)
with open("private.pem", "wb") as f:
    f.write(private_key.private_bytes(
        serialization.Encoding.PEM,
        serialization.PrivateFormat.PKCS8,
        serialization.NoEncryption(),
    ))

# Save public key
with open("public.pem", "wb") as f:
    f.write(private_key.public_key().public_bytes(
        serialization.Encoding.PEM,
        serialization.SubjectPublicKeyInfo,
    ))

print("Keys written to private.pem and public.pem")

Node.js

// generate-keys.js — run once
const crypto = require("crypto");
const fs = require("fs");

const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
  modulusLength: 2048,
  publicKeyEncoding: { type: "spki", format: "pem" },
  privateKeyEncoding: { type: "pkcs8", format: "pem" },
});

fs.writeFileSync("private.pem", privateKey);
fs.writeFileSync("public.pem", publicKey);
console.log("Keys written to private.pem and public.pem");

Django Example

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path(".well-known/openid-configuration", views.openid_configuration),
    path(".well-known/jwks.json", views.jwks),
]
# views.py
import json, base64
from django.conf import settings
from django.http import JsonResponse
from cryptography.hazmat.primitives.serialization import load_pem_public_key

def openid_configuration(request):
    return JsonResponse({
        "issuer": settings.SITE_URL,
        "jwks_uri": f"{settings.SITE_URL}/.well-known/jwks.json",
        "id_token_signing_alg_values_supported": ["RS256"],
        "response_types_supported": ["id_token"],
        "subject_types_supported": ["public"],
    })

def _b64url(n: int, length: int) -> str:
    return base64.urlsafe_b64encode(
        n.to_bytes(length, "big")
    ).rstrip(b"=").decode()

def jwks(request):
    pub = load_pem_public_key(settings.CHATBOT_PUBLIC_KEY.encode())
    numbers = pub.public_numbers()
    n_bytes = (numbers.n.bit_length() + 7) // 8
    return JsonResponse({
        "keys": [{
            "kty": "RSA",
            "kid": settings.CHATBOT_KEY_ID,
            "use": "sig",
            "alg": "RS256",
            "n": _b64url(numbers.n, n_bytes),
            "e": _b64url(numbers.e, 3),
        }]
    })

Express Example

// routes/oidc.js
const crypto = require("crypto");
const fs = require("fs");

const PUBLIC_KEY = fs.readFileSync("./keys/public.pem");
const KEY_ID = process.env.CHATBOT_KEY_ID;
const SITE_URL = process.env.SITE_URL;

function openidConfiguration(req, res) {
  res.json({
    issuer: SITE_URL,
    jwks_uri: `${SITE_URL}/.well-known/jwks.json`,
    id_token_signing_alg_values_supported: ["RS256"],
    response_types_supported: ["id_token"],
    subject_types_supported: ["public"],
  });
}

function jwks(req, res) {
  const key = crypto.createPublicKey(PUBLIC_KEY);
  const { n, e } = key.export({ format: "jwk" });
  res.json({
    keys: [{ kty: "RSA", kid: KEY_ID, use: "sig", alg: "RS256", n, e }],
  });
}

module.exports = { openidConfiguration, jwks };