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

# SDK Authentication (Partner JWT)

> Sign a Partner JWT from your backend to authenticate AIR Kit operations — supported algorithms, required claims, JWKS setup, and Issue on Behalf scope.

<Warning>
  **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](/airkit/usage/jwks-setup).
</Warning>

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](/airkit/usage/credential/issue-on-behalf) for concepts and [Issue on Behalf API & Examples](/airkit/usage/credential/issue-on-behalf-api) 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](https://datatracker.ietf.org/doc/html/rfc7517)). You must publish your public key at a JWKS URL and register it in the dashboard. See [JWKS endpoint setup](/airkit/usage/jwks-setup) for the full procedure.

To learn more about JWT, visit [jwt.io](https://www.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`](https://github.com/MocaNetwork/air-examples). For implementation, dashboard registration, local HTTPS tunnels, and the `kid` rule, see the [JWKS endpoint setup](/airkit/usage/jwks-setup) page.

## Issue-on-Behalf JWT specifics

For [Issue on Behalf (API)](/airkit/usage/credential/issue-on-behalf), 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](/airkit/usage/jwks-setup)) apply identically to Issue on Behalf — there is no separate JWKS configuration for the on-behalf flow.

Focused Node.js example (payload + header shape):

```js theme={null}
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:

```sh theme={null}
# 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.

<Tabs>
  <Tab title="Node.js">
    ```js theme={null}
    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);
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    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);
    ```
  </Tab>

  <Tab title="C#">
    ```csharp theme={null}
    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);
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    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.
  </Tab>
</Tabs>
