Documentation Index
Fetch the complete documentation index at: https://docs.moca.network/llms.txt
Use this file to discover all available pages before exploring further.
BLOCKING — Partner JWKS endpoint required. issueCredential, verifyCredential, and Issue on Behalf all fail until you (1) host a public HTTPS JWKS URL, (2) register it in Dashboard → Account → General → JWKS URL, and (3) sign your Partner JWT with a kid that matches a key in that JWKS. Localhost is not reachable from AIR servers — use an HTTPS tunnel (ngrok, cloudflared) or deploy. See JWKS endpoint setup.
You need to generate and use the JWT when:
- Authenticating a User
- Performing credentials-related operations such as issuing or verifying credentials
For Issue on Behalf (server-side credential issuance without user presence), the JWT must include scope: "issue on-behalf", typ: "JWT", and the target user’s email claim. See Issue on Behalf for concepts and Issue on Behalf API & Examples for endpoint usage.
JWT Details
- Signing algorithms supported: ES256, RS256
- Expiry: 5 min (recommended)
- Claims: varies depending on the operation. You would always need to include your
partnerId as one of the claims
- Header: You must include a
kid (Key ID) header to indicate which key was used to sign the JWT. AIR Kit uses kid to select the matching key from your JWKS endpoint.
- JWKS URL: AIR Kit validates your JWT using JWK standards (RFC 7517). You must publish your public key at a JWKS URL and register it in the dashboard. See JWKS endpoint setup for the full procedure.
To learn more about JWT, visit jwt.io.
JWKS endpoint (required for all credential SDK operations)
issueCredential, verifyCredential, and Issue on Behalf all rely on the same Partner JWT trust model — AIR Kit fetches your registered JWKS URL and validates every JWT against it. The endpoint must be:
- A public HTTPS URL reachable from AIR servers (localhost is not enough).
- Registered in Dashboard → Account → General Settings → JWKS URL as the full URL your app actually serves.
- Returning a JSON document whose
keys[].kid matches the kid you set in your JWT header.
The canonical Next.js route is app/api/.well-known/jwks/route.ts, used by every issuer and verifier in air-examples. For implementation, dashboard registration, local HTTPS tunnels, and the kid rule, see the JWKS endpoint setup page.
Issue-on-Behalf JWT specifics
For Issue on Behalf (API), use a Partner JWT with these fields:
| Category | Required fields | Notes |
|---|
| Claims | partnerId, scope: "issue on-behalf", email | email must be the target user’s AIR Account email |
| Header | kid, typ: "JWT" | kid identifies which key in your JWKS is used |
| Algorithm | RS256 or ES256 | Keep consistent with your JWKS key type |
| Expiry | exp (recommended 5 minutes) | Short-lived token recommended |
JWKS rules (endpoint reachability, registration, kid matching) apply identically to Issue on Behalf — there is no separate JWKS configuration for the on-behalf flow.
Focused Node.js example (payload + header shape):
const jwt = require("jsonwebtoken");
const fs = require("fs");
const privateKey = fs.readFileSync("path/to/private.key");
const now = Math.floor(Date.now() / 1000);
const payload = {
partnerId: "your-partner-id",
scope: "issue on-behalf",
email: "user@example.com",
iat: now,
exp: now + 5 * 60
};
const token = jwt.sign(payload, privateKey, {
algorithm: "RS256",
header: {
kid: "your-partner-id",
typ: "JWT"
}
});
Generating Partner JWTs
Generating an RS256 Key Pair
To generate a private/public key pair, you may use OpenSSL:
# Generate a 2048-bit RSA private key
openssl genpkey -algorithm RSA -out private.key -pkeyopt rsa_keygen_bits:2048
# Extract the public key in PEM format
openssl rsa -pubout -in private.key -out public.key
private.key: Use this file as your signing key in backend code.
public.key: Use this to configure your JWKS endpoint for JWT verification.
Tip: Keep your private key secure and never share it publicly.
Examples
Below are backend code examples for generating a JWT using ES256 or RS256 algorithms, including the kid (Key ID) header.
const jwt = require("jsonwebtoken");
const fs = require("fs");
const privateKey = fs.readFileSync("path/to/private.key");
const payload = {
partnerId: "your-partner-id",
// other claims as needed
exp: Math.floor(Date.now() / 1000) + 5 * 60 // 5 minutes expiry
};
const token = jwt.sign(payload, privateKey, {
algorithm: "RS256",
header: {
kid: "your-key-id"
}
});
console.log(token);
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.HashMap;
import java.util.Map;
Algorithm algorithm = Algorithm.RSA256(null, privateKey); // Use your private key
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("kid", "your-key-id");
String token = JWT.create()
.withHeader(headerClaims)
.withClaim("partnerId", "your-partner-id")
.withExpiresAt(new Date(System.currentTimeMillis() + 5 * 60 * 1000))
.sign(algorithm);
System.out.println(token);
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Collections.Generic;
// Load your private key and create signing credentials
var securityKey = new RsaSecurityKey(yourPrivateRsa);
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256);
var claims = new[] {
new Claim("partnerId", "your-partner-id"),
// other claims
};
var header = new JwtHeader(credentials);
header["kid"] = "your-key-id";
var token = new JwtSecurityToken(
header,
new JwtPayload(
claims: claims,
expires: DateTime.UtcNow.AddMinutes(5),
notBefore: null,
issuedAt: null,
audience: null,
issuer: null
)
);
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
Console.WriteLine(jwt);
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
func main() {
privateKey := []byte("your-private-key") // Use PEM for RS256/ES256
claims := jwt.MapClaims{
"partnerId": "your-partner-id",
"exp": time.Now().Add(5 * time.Minute).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
token.Header["kid"] = "your-key-id"
signedToken, err := token.SignedString(privateKey)
if err != nil {
panic(err)
}
fmt.Println(signedToken)
}
Note: Replace "your-partner-id", "your-key-id", and private key paths with your actual values. For ES256, use the appropriate signing method and key type.