RADIX WikiRADIX Wiki

What is ROLA?

ROLA (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" but uses the native Radix Wallet.

How It Works

  1. Backend generates a challenge – a random 32-byte hex string stored with a 5-minute expiry
  2. Frontend requests proof – asks the Radix Wallet for account data with proof, and the toolkit attaches the challenge it gets from the generator you registered
  3. User approves in wallet – wallet signs the challenge with the account's private key (Ed25519 or secp256k1)
  4. Frontend sends proof to backend – contains public key, signature, and curve type
  5. Backend verifies – checks the signature, confirms the public key matches the account's owner_keys metadata on ledger, and deletes the challenge

Implementation

1. Generate Challenges (Backend)

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)

The challenge is never passed to the request builder. You give the Radix dApp Toolkit a generator function, and the toolkit calls that function itself each time it sends a request. withProof() is a flag on the builder, not a place to put the challenge.

import { DataRequestBuilder } from '@radixdlt/radix-dapp-toolkit'
 
// RDT calls this once per request and attaches the result to it
rdt.walletApi.provideChallengeGenerator(async () => {
  const res = await fetch('/api/auth/challenge')
  return (await res.json()).challenge
})
 
// withProof() takes an optional boolean, never the challenge string
rdt.walletApi.setRequestData(
  DataRequestBuilder.accounts().atLeast(1).withProof(),
)
 
const result = await rdt.walletApi.sendRequest()   // takes no arguments
 
if (result.isOk()) {
  const { proofs } = result.value   // one signed challenge per shared account
}

Two things here are easy to get wrong. walletApi.sendRequest() accepts no arguments: the builders go to setRequestData() first, or to sendOneTimeRequest(...) if you do not want the request stored for later connections. And every entry in result.value.proofs already has the shape { address, type, challenge, proof }, which is exactly the argument verifySignedChallenge expects in the next step, so you can pass one straight through without rebuilding it. The curve field is the string 'curve25519' or 'secp256k1', so do not test it against 'ed25519' even though Ed25519 is the signature scheme.

3. Verify Proof (Backend)

import { 

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

Use CaseAuth Method
User login / session creationROLA
Prove account ownership to backendROLA
Transfer assets or call componentsOn-ledger transaction
Gate content by badge ownershipROLA + 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.

Reference Implementation and Maintenance Status (checked August 2026)

ROLA works, and it is still the right way to log a user in. But every official piece around it has stopped receiving updates, and one of them is archived, so check the source before you copy code out of a Radix repository.

  • The examples repository is archived. radixdlt/rola-examples is marked archived on GitHub and its most recent commit is dated 8 September 2023. It pins @radixdlt/radix-dapp-toolkit at 0.5.1 and sets networkId: 13, an id the Gateway SDK's own RadixNetwork map does not contain at all, since it goes from 12 straight to 14. The example also predates the @radixdlt/rola package: its server verifies proofs with a hand-written implementation under apps/server/src/rola/ rather than the package this page recommends.
  • The package is frozen, and widely installed anyway. @radixdlt/rola is at version 2.1.0, published on 20 November 2024, and npm latest has not moved since. It was still downloaded 3,564 times in the month to 24 August 2026.
  • The toolkit is in the same position. The dated version of that story is on Radix dApp Toolkit: bug fixes only, on a volunteer basis, pending a community decision on who takes the libraries on.

The practical rule for all three: read the archived example to see how the parts connect, then check every method name against the type definitions inside the version you actually installed, because the two do not agree.

Next Steps

HydrateLast updated 6h agov2.0.07 revisionsVerified Aug 25, 2026