{
  "success": false,
  "data": "Wallet address is required"
}

Authentication

Rise uses Sign-In with Ethereum (SIWE) for secure, blockchain-based authentication. This ensures that only users with the correct wallet permissions can access the API.

Overview

The authentication flow consists of two main steps:
  1. Get SIWE Message: Request a message to sign from the user’s wallet.
  2. Verify Signature: Submit the signed message, signature, and nonce to obtain a JWT token.

Authentication Flow

1

Request SIWE Message

Call the /v2/auth/siwe endpoint with the user’s wallet address and RiseID to get a message to sign.
2

User Signs Message

The user signs the SIWE message using their wallet (MetaMask, WalletConnect, etc.).
3

Verify Signature

Submit the signed message, signature, and nonce to /v2/auth/verify to obtain a JWT token.
4

Use JWT Token

Include the JWT token in the Authorization header for subsequent API calls.

Step 1: Get SIWE Message

Request a SIWE message for the user to sign:
curl -X GET "${this.baseUrl}/v2/auth/siwe?wallet=0x1234...&riseid=rise_123456789"
Response:
{
  "success": true,
  "data": {
    "siwe": "rise.works wants you to sign in with your Ethereum account:\n0x1234567890abcdef...\n...Nonce: abc123def456..."
  }
}

Step 2: User Signs Message

The user signs the SIWE message using their wallet.
You must extract the nonce from the SIWE message (look for the Nonce: line).
Important: The nonce is automatically generated by the server and included in the SIWE message. You need to extract this nonce to use in Step 3. Example (JavaScript/ethers - Node.js):
// Extract nonce from the SIWE message
const nonceMatch = siweMessage.match(/Nonce: (.+)/);
const nonce = nonceMatch ? nonceMatch[1] : '';

// Sign the message using private key (Node.js)
const wallet = new ethers.Wallet(walletPrivateKey);
const signature = await wallet.signMessage(siweMessage);

Step 3: Verify Signature

Submit the signed message, signature, and nonce to obtain a JWT:
curl -X POST "${this.baseUrl}/v2/auth/verify" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "<the SIWE message>",
    "sig": "<the signature>",
    "nonce": "<the nonce from SIWE message>"
  }'
Response:
{
  "success": true,
  "data": {
    "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Step 4: Use JWT Token

Include the JWT in the Authorization header for all subsequent API calls:
Authorization: Bearer <jwt>

Notes & Best Practices

  • Nonce: Always extract and use the nonce from the SIWE message.
  • JWT Expiry: JWT tokens expire after 24 hours.
  • Security: Never store private keys or signatures in client-side code. Always use wallet providers for signing.
  • Error Handling: Handle errors such as invalid wallet, invalid signature, expired nonce, or unauthorized access.

Code Examples

The following code samples show how to implement the authentication flow in different languages and frameworks.

Complete Authentication Example

Here’s a complete example of the authentication flow:
import { ethers } from 'ethers';

class RiseAuth {
  constructor(baseUrl = 'https://b2b-api.riseworks.io') {
    this.baseUrl = baseUrl;
  }

  async authenticate(walletAddress, riseId, walletPrivateKey) {
    try {
      // Step 1: Get SIWE message
      const siweResponse = await fetch(
        `${this.baseUrl}/v2/auth/siwe?wallet=${walletAddress}&riseid=${riseId}`
      );
      const { data: { siwe } } = await siweResponse.json();

      // Step 2: Sign the message using private key (Node.js)
      const wallet = new ethers.Wallet(walletPrivateKey);
      const signature = await wallet.signMessage(siwe);

      // Extract nonce from the message
      const nonceMatch = siwe.match(/Nonce: (.+)/);
      const nonce = nonceMatch ? nonceMatch[1] : '';

      // Step 3: Verify signature and get JWT
      const verifyResponse = await fetch(`${this.baseUrl}/v2/auth/verify`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          message: siwe,
          sig: signature,
          nonce: nonce
        })
      });

      const { data: { jwt } } = await verifyResponse.json();
      return jwt;
    } catch (error) {
      console.error('Authentication failed:', error);
      throw error;
    }
  }
}

Error Handling

Common authentication errors and how to handle them:
{
  "success": false,
  "data": "Wallet address is required"
}
HTTP Status CodeDescriptionSolution
400Bad Request - Missing or invalid parametersCheck that wallet address and RiseID are provided and valid
401Unauthorized - Invalid signature or expired nonceRe-authenticate with a new SIWE message
403Forbidden - User lacks permission for the RiseIDVerify the wallet has proper permissions for the specified RiseID
404Not Found - RiseID doesn’t existVerify the RiseID exists in the system
500Internal Server ErrorRetry the request or contact support

Common Error Scenarios

Invalid Wallet Address (400):
{
  "success": false,
  "data": "Wallet address is required"
}
Invalid RiseID (404):
{
  "success": false,
  "data": "No entity found with riseid rise_invalid"
}
Permission Denied (403):
{
  "success": false,
  "data": "Invalid Rise account 0x1234..."
}
SIWE Verification Failed (401):
{
  "success": false,
  "data": "SIWE error: Invalid signature"
}

Security Considerations

Never store private keys or signatures in client-side code. Always use wallet providers like MetaMask or WalletConnect for signing in browser environments.
Node.js Applications: For server-side applications, you can use private keys directly with ethers.Wallet(). However, ensure the private key is stored securely in environment variables and never committed to version control.
  • Token Expiration: JWT tokens expire after 24 hours - implement automatic refresh logic
  • Nonce Validation: Each SIWE message has a unique nonce to prevent replay attacks - never reuse nonces
  • Chain ID for SIWE: SIWE authentication uses Ethereum mainnet (Chain ID 1) for production and Goerli testnet (Chain ID 5) for development
  • Rise Operations: While SIWE uses Ethereum, Rise’s payment and smart contract operations are on Arbitrum mainnet (Chain ID 42161)
  • Domain Verification: The SIWE message includes the domain to prevent phishing attacks - always verify the domain
  • Signature Verification: All signatures are verified on-chain to ensure authenticity
  • Private Key Security: When using private keys in Node.js, store them in environment variables and use secure key management services in production