---
title: "3. ROLA Authentication"
path: "/developers/frontend/03-rola-authentication"
version: "1.1.0"
author: "Hydrate"
createdAt: "2026-02-22T17:58:00.700Z"
updatedAt: "2026-03-16T18:21:03.589Z"
---

# 3. ROLA Authentication

<Infobox>
| **[ROLA](https://docs.radixdlt.com/docs/rola) Authentication** |
| Difficulty | Intermediate |
| Est. Time | 25 minutes |
| Prerequisites | [Radix dApp Toolkit](/developers/frontend/01-radix-dapp-toolkit) |
| Language | TypeScript |
| NPM | [@radixdlt/rola](https://www.npmjs.com/package/@radixdlt/rola) |
</Infobox>

## What is [ROLA](https://docs.radixdlt.com/docs/rola)?

[ROLA](https://docs.radixdlt.com/docs/rola-radix-off-ledger-auth) (Radix Off-Ledger Authentication) is a challenge-response protocol that lets your backend verify a user owns a Radix account or persona — without submitting any on-ledger transaction. It's the Radix equivalent of "Sign-In with [Ethereum](https://ethereum.org)" but uses the native [Radix Wallet](/contents/tech/core-protocols/radix-wallet).

## How It Works

1. $1
2. $1
3. $1
4. $1
5. $1

## Implementation

### 1. Generate Challenges (Backend)

```typescript
import crypto from 'crypto'

// Store challenges with expiry (use Redis, DB, or in-memory Map)
const challenges = new Map<string, { expires: number }>()

function createChallenge(): string {
  const challenge = crypto.randomBytes(32).toString('hex')
  challenges.set(challenge, {
    expires: Date.now() + 5 * 60 * 1000  // 5 minutes
  })
  return challenge
}
```

### 2. Request Proof (Frontend)

```typescript
import { DataRequestBuilder } from '@radixdlt/radix-dapp-toolkit'

// Fetch challenge from your backend
const challenge = await fetch('/api/auth/challenge').then(r => r.json())

// Request proof of account ownership
const result = await rdt.walletApi.sendRequest(
  DataRequestBuilder.accounts()
    .atLeast(1)
    .withProof(challenge)
)
```

### 3. Verify Proof (Backend)

```typescript
import { [Rola](https://docs.radixdlt.com/docs/rola) } from '@radixdlt/rola'
import { RadixNetwork } from '@radixdlt/babylon-gateway-api-sdk'

const rola = Rola({
  networkId: RadixNetwork.Mainnet,
  applicationName: 'My dApp',
  dAppDefinitionAddress: 'account_rdx...',
  expectedOrigin: 'https://my-dapp.com'
})

// Verify the signed challenge
const result = await rola.verifySignedChallenge({
  challenge: challengeHex,
  proof: { publicKey, signature, curve },
  address: accountAddress,
  type: 'account'
})

if (result.isOk()) {
  // Create session (JWT, cookie, etc.)
}
```
Delete used challenges

Always delete a challenge after verification — successful or not. This prevents replay attacks where a captured proof is resubmitted.

## When to Use [ROLA](https://docs.radixdlt.com/docs/rola)

| Use Case | Auth Method |
| --- | --- |
| User login / session creation | ROLA |
| Prove account ownership to backend | ROLA |
| Transfer assets or call components | On-ledger transaction |
| Gate content by badge ownership | ROLA + Gateway query |

ROLA proves *identity*. On-ledger transactions perform *actions*. For token-gated access, verify ownership via ROLA then query the account's resources via the [Gateway SDK](/developers/frontend/02-gateway-sdk).

## External Links

- [ROLA documentation](https://docs.radixdlt.com/docs/rola-radix-off-ledger-auth)
- [@radixdlt/rola on npm](https://www.npmjs.com/package/@radixdlt/rola)
- [ROLA examples repository](https://github.com/radixdlt/rola-examples)