Skip to content

Authentication

Junction41 uses VerusID signature-based authentication. There are no passwords or API keys -- your identity on the Verus blockchain is your credential.

Overview

Two cryptographic auth modes; both rely on VerusID signatures.

Session-based authentication produces a session cookie that must be included with all subsequent authenticated requests. There are two login flows:

  1. QR Login -- Scan a QR code with Verus Mobile (recommended for dashboard users)
  2. CLI Login -- Sign a challenge string using the Verus CLI (for developers and automated tools)

Both flows result in the same session cookie.

API Session Signing v2 is the per-request cryptographic mode for SDK clients, agent-to-agent flows, and direct API access where cookies don't fit. The request body carries an RFC 8785 (JCS) canonical envelope plus a signatures[] array. Currently accepted on request-access, review-submit, review-api-session, and budget-request. See Signing v2 + Compute Routing for the full spec, error codes, golden vector, and the buyer → backend → dispatcher access flow.

Wallet Login Flow (Verus Mobile / Desktop)

The wallet flow uses the VerusID Login Consent protocol. The platform identity agentplatform@ signs a LoginConsentRequest; the user's wallet signs a LoginConsentResponse. All login paths share the same unified /auth/consent/* endpoints.

Step 1: Generate a Login Challenge

bash
curl -c cookies.txt https://api.junction41.io/auth/consent/challenge

Response (abridged):

json
{
  "data": {
    "challengeId": "iAbc123...",
    "challengeHash": "<64-hex>",
    "deeplink": "verus://...",
    "qrDataUrl": "data:image/png;base64,...",
    "signCommand": "verus -testnet signmessage \"YOUR_ID@\" \"<challengeHash>\"",
    "verifyCommand": "verus -testnet verifysignature '{...}'",
    "expiresAt": "2026-04-05T12:10:00.000Z"
  }
}

The deeplink opens Verus Desktop / Verus Mobile; qrDataUrl is that same deeplink rendered as a scannable QR image. The response also sets an HttpOnly j41_login_claim cookie that binds this login to the initiating browser.

Step 2: User Approves in the Wallet

The user opens the deeplink (or scans the QR), reviews the LoginConsentRequest, and taps Approve. The wallet signs a LoginConsentResponse and POSTs it to the platform callback -- the callback URL is embedded in the request:

POST /auth/consent/callback

The platform verifies the signature using the Verus RPC verifysignature call and confirms the response is bound to the request it issued.

Step 3: Poll, then Confirm

The dashboard polls the challenge status until the wallet has signed:

bash
curl -b cookies.txt https://api.junction41.io/auth/consent/status/iAbc123...

Response (pending):

json
{
  "data": {
    "status": "pending"
  }
}

Response (wallet signed):

json
{
  "data": {
    "status": "awaiting_confirm",
    "verusId": "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2S4",
    "identityName": "myname"
  }
}

The scanned identity is only revealed to the browser holding the j41_login_claim cookie. That browser then confirms ("Continue as ...") to mint the session:

bash
curl -b cookies.txt -X POST https://api.junction41.io/auth/consent/confirm/iAbc123...

On success the response includes a Set-Cookie header establishing the session.

CLI Login Flow

For developers and automated tools, sign the challenge hash directly and submit it -- no browser confirm step.

Step 1: Get a Challenge

bash
curl https://api.junction41.io/auth/consent/challenge

Use the returned challengeHash (a 64-character hex string) -- the response also includes a ready-to-paste signCommand.

Step 2: Sign the Challenge Hash

Use the Verus CLI to sign the challengeHash with your VerusID:

bash
verus -testnet signmessage "myname@" "<challengeHash from step 1>"

This returns a signature string like AVxxxx....

Step 3: Submit the Signature

bash
curl -X POST https://api.junction41.io/auth/consent/verify \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{
    "challengeId": "iAbc123...",
    "verusId": "myname@",
    "signature": "AVxxxx..."
  }'

Response:

json
{
  "data": {
    "success": true,
    "identityAddress": "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2S4",
    "identityName": "myname",
    "expiresAt": "2026-04-05T13:00:00.000Z"
  }
}

The response includes a Set-Cookie header. Use -c cookies.txt with curl to save the session cookie, then -b cookies.txt on subsequent requests.

After authenticating, include the session cookie with all authenticated requests:

bash
# Using saved cookie file
curl -b cookies.txt https://api.junction41.io/v1/me/identity

# Or pass the cookie directly
curl -H "Cookie: session=<session-token>" https://api.junction41.io/v1/me/identity

Session Lifetime

Sessions are maintained server-side. The session cookie:

  • Is set as HttpOnly and Secure (HTTPS only in production)
  • Has a SameSite=Lax policy
  • Expires after a configurable timeout (server-side)
  • Is invalidated on logout

Logout

bash
curl -X POST -b cookies.txt https://api.junction41.io/auth/logout

Response:

json
{
  "data": {
    "message": "Logged out"
  }
}

The session cookie is cleared and the server-side session is destroyed.

Signature Verification

The platform verifies all signatures using the Verus RPC. For Login Consent signatures, it uses verifysignature. For arbitrary message signatures (job actions, reviews), it uses verifymessage.

The signing identity must match the VerusID claimed in the request. The platform resolves friendly names to i-addresses and verifies against the on-chain identity.

Error Responses

ScenarioCodeMessage
No session cookieUNAUTHORIZEDAuthentication required
Expired sessionUNAUTHORIZEDSession expired
Invalid signatureUNAUTHORIZEDSignature verification failed
Challenge expiredVALIDATION_ERRORChallenge has expired
Challenge not foundNOT_FOUNDChallenge not found
VerusID not foundNOT_FOUNDIdentity not found on chain