# Authentication

Executive summary
**Authentication uses a two-layer model: JWT tokens for session identity and digital signatures for authorizing changes.**

- Every API call requires a JWT token proving the caller's identity.
- State-changing operations (intents) additionally require cryptographic signatures.
- Public keys are registered via genesis block or governance intents.
- Challenges are one-time-use to prevent replay attacks.


Why this matters
This dual-layer model ensures that even if a session token is compromised, an attacker cannot make state changes without access to the user's private key. Every modification creates a non-repudiable audit trail—the signer cannot deny they authorized the action. This is essential for regulatory compliance and internal controls.

**For architects and operators**: Integrate authentication with your existing identity provider via OIDC, but understand that the cryptographic signing layer is separate and non-negotiable. Plan for key ceremony procedures and secure key storage for your users.

Prerequisites
Before reading this page, you should understand:

- [Security architecture](/products/custody/v1.34/concepts/security) - The zero-trust security model
- [Governance framework](/products/custody/v1.34/overview/governance) - How intents and policies work


## Overview

The Ripple Custody API requires two types of authentication:

| Authentication type | Purpose | When required |
|  --- | --- | --- |
| **JSON Web Token (JWT)** | Proves your identity for the current session | All API calls |
| **Digital signature** | Authorizes a specific action and creates an audit trail | State-changing operations (intents) |


This dual-layer approach ensures that:

- Every request comes from an authenticated user
- Every change to the system is explicitly authorized and signed
- A complete audit trail exists for all modifications


## How authentication works

The authentication flow is built on **asymmetric cryptography** (public/private key pairs):

- **Private key**: A secret key that only you possess. Used to sign data. Never share this key.
- **Public key**: Shared with Ripple Custody. Used to verify signatures created by the corresponding private key.


### The authentication flow


```mermaid
sequenceDiagram
    participant User
    participant AuthServer as Auth Server
    participant API as Custody API

    User->>User: 1. Generate random challenge
    User->>User: 2. Sign challenge with private key
    User->>AuthServer: 3. Send public key + challenge + signature
    AuthServer->>AuthServer: 4. Verify signature
    AuthServer->>AuthServer: 5. Check public key is trusted
    AuthServer-->>User: 6. Issue JWT token
    User->>API: 7. Make API calls with JWT
```

1. **Generate challenge**: Create a random string (e.g., a UUID, or 64 bytes of random data).
2. **Sign challenge**: Use your private key to create a cryptographic signature.
3. **Authenticate**: Send your public key, the challenge, and the signature to the auth server.
4. **Verification**: The server verifies the signature matches the challenge and public key.
5. **Trust check**: The server confirms your public key is in its list of trusted keys (from the genesis block or added via intent).
6. **Token issued**: If both checks pass, you receive a JWT token.
7. **API access**: Use the JWT as a Bearer token in the `Authorization` header for all subsequent API calls.


For security reasons, you can only use a challenge once. For every new authentication request, you must generate a new challenge and create a new signature.

## Dual control enforcement

Ripple Custody enforces **dual control** for intent approval workflows. This means:

- The user who proposes an intent **cannot also approve** that same intent
- Even if the proposer has the required role for approval, they must wait for a different user to approve
- This ensures that no single user can unilaterally execute state-changing operations


Self-approval is blocked
If a user attempts to approve an intent they proposed, the system returns: `InvalidIntentError: This user has already signed`

This dual control mechanism is a fundamental security feature that enforces separation of duties and prevents insider threats.

## JWT and intent author consistency

The JWT token and the intent author must identify the same user:

- **JWT token**: Identifies who is making the API call (session authentication)
- **Intent author**: Identifies who is proposing or approving the intent (specified in `author.id`)


The system enforces that these match — you cannot use one user's JWT to submit intents on behalf of another user. For more details, see [JWT and intent author identity](/products/custody/v1.34/concepts/authenticate-api-requests#jwt-and-intent-author-identity).

## Genesis users vs. regular users

Ripple Custody distinguishes between two types of users based on how they were added to the system:

| User type | How they're created | Trust establishment |
|  --- | --- | --- |
| **Genesis users** | Defined in the genesis block during initial platform setup | Public key is trusted from system initialization |
| **Regular users** | Created via an intent after the system is running | Public key is added through the governance approval process |


### Genesis users

Genesis users are the initial administrators defined in the [Genesis block](/products/custody/v1.34/concepts/genesis-state). Their public keys are included in the cryptographically-signed configuration that establishes the platform's root of trust.

**Key characteristics:**

- Created during first-time platform initialization
- Cannot be modified or removed (the genesis block is immutable)
- Typically have administrative roles to bootstrap the system
- Their trust is established at the cryptographic foundation of the platform


For detailed information about how genesis users are defined and the genesis block structure, see [Genesis block](/products/custody/v1.34/concepts/genesis-state).

### Regular users

Regular users are created after the platform is running, through the standard intent workflow:

1. A genesis user (or authorized user) proposes a `v0_CreateUser` intent.
2. The intent goes through governance approval based on configured policies.
3. Once approved, the new user's public key is added to the trusted key list.
4. The new user can then authenticate using the standard flow.


## Next steps

For regular users
The following guides walk you through setting up authentication and making API calls. Genesis users are pre-configured during platform initialization — see [Genesis block](/products/custody/v1.34/concepts/genesis-state) for details.

| Task | Description |
|  --- | --- |
| [Generate keys and register](/products/custody/v1.34/concepts/generate-keys-register) | One-time setup: generate your key pair and register with the platform |
| [Authenticate API requests](/products/custody/v1.34/concepts/authenticate-api-requests) | Ongoing usage: obtain JWT tokens and sign intents for API calls |


## Related topics

- [Genesis state](/products/custody/v1.34/concepts/genesis-state) - Platform initialization and genesis user configuration
- [Security architecture](/products/custody/v1.34/concepts/security) - Zero-trust security model and cryptographic verification
- [Verifying Custody signatures](/products/custody/v1.34/concepts/data-integrity#verifying-custody-signatures) - How to verify signatures on data returned by the platform