---
title: "JWT authentication (Stock Provider)"
canonical_url: "https://apidocs.sportradar.com/resources/widgets/docs/custom-bet/integration/jwt"
markdown_url: "https://apidocs.sportradar.com/resources/widgets/docs/custom-bet/integration/jwt.md"
last_updated: "2026-10-22T13:00:29Z"
---

# JWT authentication (Stock Provider)

The **stock** data provider (`dataProvider: 'stock'`) requires a **`jwt`** widget prop. For widget setup, see [Stock Provider](https://apidocs.sportradar.com/resources/widgets/docs/custom-bet/integration/stock.md).

## What a JWT is

A **JSON Web Token (JWT)** is a compact, signed string ([RFC 7519](https://tools.ietf.org/html/rfc7519)) made of three parts:

| Part          | Contains                                                                                  |
| ------------- | ----------------------------------------------------------------------------------------- |
| **Header**    | Algorithm (`RS256`) and type (`JWT`)                                                      |
| **Payload**   | Signed JSON data (your JWT library and Sportradar onboarding define what goes here)       |
| **Signature** | Proof that the payload was signed with your **RSA private key** and was not tampered with |

Sportradar holds your matching **public key**. When the widget calls Sportradar **custom bet services** for offering or odds calculation, those services verify the signature on the token you provide. That replaces exposing a long-lived UOF API key in the browser while still authenticating traffic from your integration.

For Custom Bet stock you:

1. **Sign** a fresh token on your **backend** after the user is authenticated (never in client-side JavaScript).
2. Pass the resulting string to the widget as the **`jwt`** prop.
3. The widget sends it on each custom bet request as a bearer token:

```http
Authorization: Bearer <your-jwt>
```

The token is **not** a static password you embed in the page. Issue **short-lived** tokens and fetch a new one when validation fails or your session policy requires a refresh.

> **Signing algorithm**
>
> Tokens must be signed with **RS256** (RSA + SHA-256). Other algorithms are not supported for this integration.

> **Warning**
>
> Never sign JWTs in the browser, commit private keys, or hard-code long-lived secrets in public pages. Issue tokens only from your server after the user is authenticated.

### Why stock uses a JWT (instead of UOF Proxy)

With **`uofProxy`**, your page implemented callbacks that called **your** proxy with a **UOF API key** and returned XML to the widget.

With **`stock`**, the widget calls **Sportradar-hosted** custom bet APIs directly. The **`jwt`** replaces that server-side UOF key for custom bet traffic: it authorizes the widget runtime to load markets and calculate odds for the current user session.

## How the token reaches the widget

```mermaid
sequenceDiagram
    participant User as Logged-in user
    participant FE as Your frontend
    participant BE as Your backend
    participant CB as Custom Bet widget
    participant SR as Sportradar custom bet services

    User->>FE: Opens event page
    FE->>BE: Request Custom Bet JWT
    BE->>BE: Sign RS256 JWT for user/session
    BE-->>FE: jwt string
    FE->>CB: addWidget (jwt, matchId, stock, addToBetSlip)
    CB->>SR: Authorization Bearer jwt
    SR-->>CB: Offering / calculation
```

Typical integration pattern:

1. User logs in to your site as usual.
2. Your frontend calls a **same-origin** endpoint (for example `GET /api/custom-bet/jwt`) that only returns a token for authenticated users.
3. Your backend signs a token with **RS256** using the private key that matches your published public key, and returns `{ "jwt": "…" }`.
4. Pass the string to `SIR('addWidget', …, 'customBet', { jwt, dataProvider: 'stock', … })`.
5. Refresh the token when it expires or when custom bet requests start failing with 401/403.

## Prerequisites (one-time setup)

Work with Sportradar **client setup** before production:

| Step             | Action                                                                                                                                                                                                                                                      |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Keys             | Generate a 2048-bit RSA key pair (OpenSSL or your platform). Keep the private key on your backend. Step-by-step key commands: [Generate RSA key pair](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/jwt.md#step-1-generate-rsa-key-pair). |
| Share public key | Send **`rsa-public.pem`** to your Sportradar sales or client setup contact                                                                                                                                                                                  |
| Publish          | Confirm client setup has **published** your public key so Sportradar can validate tokens you sign (do not issue production `jwt` values until this is done)                                                                                                 |

If you already use widget JWT signing for other Sportradar products, you can usually reuse the same key pair and signing code; confirm with client setup that the published key covers Custom Bet stock.

Non-production environments: follow [Non-Production Environments](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/jwt.md#step-1-generate-rsa-key-pair) in the shared JWT tutorial when you use separate dev or staging keys.

## Sign tokens on your backend

Use the same RS256 signing flow as other Sportradar widget JWT integrations. Your JWT library builds the payload and signature; follow client setup guidance for payload fields and token lifetime.

Example (Node.js) — adjust the payload to match your Sportradar onboarding:

```javascript
import jwt from "jsonwebtoken";
import fs from "fs";

const privateKey = fs.readFileSync("rsa-private.pem", "utf8");

const token = jwt.sign(
  {
    /* payload per your Sportradar JWT onboarding */
  },
  privateKey,
  { algorithm: "RS256", expiresIn: "1h" },
);
```

More examples and key rotation: [JSON Web Token (JWT) tutorial](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/jwt.md#step-3-sign-the-token).

## Pass the JWT to Custom Bet

```javascript
SIR("addWidget", "#sr-widget", "customBet", {
  matchId: 50852263,
  dataProvider: "stock",
  jwt: customBetJwt, // from your backend endpoint
  dataProviderConfig: {
    addToBetSlip,
  },
});
```

The `jwt` prop is **only required** when `dataProvider` is `'stock'`. Legacy `uofProxy` integrations do not use it.

## Compared to UOF Proxy authentication

| Topic                     | UOF Proxy                          | Stock + JWT                                          |
| ------------------------- | ---------------------------------- | ---------------------------------------------------- |
| Who calls custom bet APIs | Your proxy (browser callbacks)     | Sportradar (widget runtime)                          |
| Credential                | UOF application key on your server | Per-user/session JWT signed by you                   |
| Browser exposure          | Proxy URLs; key stays on server    | JWT string in the page (short-lived)                 |
| User binding              | Implicit in your session           | Tied to your logged-in user via backend-issued token |

## Troubleshooting

| Symptom                               | Likely cause                                                               |
| ------------------------------------- | -------------------------------------------------------------------------- |
| Widget errors immediately on load     | Missing `jwt`, or `dataProvider` not set to `'stock'`                      |
| HTTP 401 / 403 on custom bet requests | Expired or invalid JWT, or public key not yet published for your bookmaker |
| Worked in dev, fails in prod          | Wrong signing key or environment for the published public key              |
| Token works once then fails           | Token expired — issue a new JWT and update the widget                      |

See also [Stock Provider troubleshooting](https://apidocs.sportradar.com/resources/widgets/docs/custom-bet/integration/stock.md#troubleshooting).

## Related documentation

- [Stock Provider](https://apidocs.sportradar.com/resources/widgets/docs/custom-bet/integration/stock.md) — full stock integration
- [Migrating from UOF Proxy to Stock](https://apidocs.sportradar.com/resources/widgets/docs/custom-bet/integration/migrate-uof-proxy-to-stock.md) — moving off `uofProxy`
- [JSON Web Token (JWT)](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/jwt.md) — key generation, signing examples for other widgets, key rotation
