---
id: web--jwt-issuer
title: "JWT issuer"
summary: "Creates a signed JSON Web Token for a user, and optionally a refresh token. Use it in a login endpoint after the user's credentials have been checked; the **JWT validator** with the matching key accepts the token later."
audience: user
keywords: "input, output, uselifetime, tokenlifetime, issuer, audience, algorithm, signingkey, keyencoding, keyid, refreshtokenlifetime"
---
# JWT issuer

Creates a signed JSON Web Token for a user, and optionally a refresh token. Use it in a login endpoint after the user's credentials have been checked; the **JWT validator** with the matching key accepts the token later.

## Pins

| Pin | Kind | Types | Description |
|---|---|---|---|
| Input | input | `Viaducta.Plugins.JWTIssuer.JwtIssuerUserData` | User data: `UserName`, `UserId`, `Email`, `Claims` (dictionary). |
| Output | output | `Viaducta.Plugins.JWTIssuer.JwtData` | `Token`, `TokenType`, `ExpiresIn`, `Username`, `UserId`, `Email`, `ValidFrom`, `ValidTo`, `RefreshToken`, `RefreshTokenValidTo`. |

## Configuration

| Field | Type | Default | Description |
|---|---|---|---|
| `UseLifetime` | boolean | `true` | `true`: the token gets an expiry (`exp`). |
| `TokenLifetime` | string | `24:00:00` | Validity when `UseLifetime` is `true`: `hh:mm:ss` where the hours may exceed 23 (`24:00:00` is one day, `36:00:00` a day and a half), or `d.hh:mm:ss` (`7.00:00:00` is a week). |
| `Issuer` | string | | Required. `iss` claim. |
| `Audience` | string | | `aud` claim, comma separated for several audiences (omitted when empty). |
| `Algorithm` | int | `0` | `0` HS256, `1` HS384, `2` HS512 (shared secret); `3` RS256, `4` RS384, `5` RS512 (RSA key); `6` ES256, `7` ES384, `8` ES512 (EC key); `9` PS256, `10` PS384, `11` PS512 (RSA-PSS). |
| `SigningKey` | string | | Required. HS*: the shared secret, at least 32 bytes for HS256, 48 for HS384, 64 for HS512. RS*, ES*, PS*: the private key in PEM format (PKCS#8 or PKCS#1, unencrypted; RSA at least 2048 bits, the EC curve must match the algorithm). |
| `KeyEncoding` | int | `0` | Shared secret only: `0` text (its UTF-8 bytes), `1` Base64. |
| `KeyId` | string | | Optional `kid` header, lets validators pick the right key from a JWKS while keys are rotated. |
| `RefreshTokenLifetime` | string | | Optional, same format as `TokenLifetime` and longer than it. When set a refresh token is issued too. |

## Behaviour

- For every input record a token is created with `iss`, `aud`, `iat`, `nbf`, `exp`, a unique `jti`, `sub` = `UserName`, `email` = `Email`, `userId` = `UserId` (left out when empty) and one claim per `Claims` entry.
- A `Claims` value written as a JSON array (`["admin","editor"]`) becomes an array in the token. Entries named `iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti` or `token_use` are skipped (a warning is shown once); entries with an empty key are skipped.
- `ValidFrom` (`nbf`), `ValidTo` (`exp`) and `RefreshTokenValidTo` are UTC. `ValidTo` is `0001-01-01` and `ExpiresIn` `0` when `UseLifetime` is `false`.
- The refresh token carries the same claims plus `token_use` = `refresh`. A JWT validator with `TokenUse` Access (the default) rejects it, so it cannot be used as an access token.
- A wrong configuration (missing `Issuer`, short or unreadable key, invalid lifetime) stops the flow at start.

## Notes

- Older versions read `TokenLifetime` with the .NET time span parser, which takes `24:00:00` as **24 days**. Flows saved with that value now issue tokens valid for 24 hours.
- Without `UseLifetime` tokens never expire; avoid that for user logins.
- Keep `SigningKey` out of the flow: use an expression setting (`=Secrets.JwtKey`). With HS* the validator needs the same secret; with RS*/ES*/PS* it needs only the public key, so other services can check tokens without being able to issue them.
- To return the token to the caller, serialize `JwtData` to JSON and send it with **Http response**.

## Example

```json
{ "UseLifetime": true, "TokenLifetime": "00:15:00", "RefreshTokenLifetime": "7.00:00:00", "Issuer": "my-api", "Audience": "my-clients", "Algorithm": 0, "SigningKey": "=Secrets.JwtKey" }
```

Login: Http listener → Rest handler (`login`) → Script (check password, build `JwtIssuerUserData`) → **JWT issuer** → Json serializer → Http response

Refresh: Rest handler (`refresh`) → JWT validator (`TokenUse` Refresh, token as `TextRecord`) → Script (`JwtClaimsData` to `JwtIssuerUserData`, check the user is still active) → **JWT issuer** → Json serializer → Http response
