Finish a passkey login
curl --request POST \
--url https://api.next.orenda.finance/v1/auth/passkey/verify \
--header 'Content-Type: application/json' \
--header 'x-program-id: <x-program-id>' \
--data '
{
"session_token": "3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b",
"assertion": {
"credentialIdB64": "AaIWjR2k8Qm3sV7tZ1pYxC",
"userHandleB64": "dXNlci1jdXN0XzNmOWEyYjdl",
"clientDataJSON_B64": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0",
"authenticatorDataB64": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M",
"signatureB64": "MEUCIQDx9z...sig...redacted"
}
}
'import requests
url = "https://api.next.orenda.finance/v1/auth/passkey/verify"
payload = {
"session_token": "3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b",
"assertion": {
"credentialIdB64": "AaIWjR2k8Qm3sV7tZ1pYxC",
"userHandleB64": "dXNlci1jdXN0XzNmOWEyYjdl",
"clientDataJSON_B64": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0",
"authenticatorDataB64": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M",
"signatureB64": "MEUCIQDx9z...sig...redacted"
}
}
headers = {
"x-program-id": "<x-program-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-program-id': '<x-program-id>', 'Content-Type': 'application/json'},
body: JSON.stringify({
session_token: '3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b',
assertion: {
credentialIdB64: 'AaIWjR2k8Qm3sV7tZ1pYxC',
userHandleB64: 'dXNlci1jdXN0XzNmOWEyYjdl',
clientDataJSON_B64: 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0',
authenticatorDataB64: 'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M',
signatureB64: 'MEUCIQDx9z...sig...redacted'
}
})
};
fetch('https://api.next.orenda.finance/v1/auth/passkey/verify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.next.orenda.finance/v1/auth/passkey/verify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'session_token' => '3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b',
'assertion' => [
'credentialIdB64' => 'AaIWjR2k8Qm3sV7tZ1pYxC',
'userHandleB64' => 'dXNlci1jdXN0XzNmOWEyYjdl',
'clientDataJSON_B64' => 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0',
'authenticatorDataB64' => 'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M',
'signatureB64' => 'MEUCIQDx9z...sig...redacted'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-program-id: <x-program-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.next.orenda.finance/v1/auth/passkey/verify"
payload := strings.NewReader("{\n \"session_token\": \"3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b\",\n \"assertion\": {\n \"credentialIdB64\": \"AaIWjR2k8Qm3sV7tZ1pYxC\",\n \"userHandleB64\": \"dXNlci1jdXN0XzNmOWEyYjdl\",\n \"clientDataJSON_B64\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0\",\n \"authenticatorDataB64\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M\",\n \"signatureB64\": \"MEUCIQDx9z...sig...redacted\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-program-id", "<x-program-id>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.next.orenda.finance/v1/auth/passkey/verify")
.header("x-program-id", "<x-program-id>")
.header("Content-Type", "application/json")
.body("{\n \"session_token\": \"3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b\",\n \"assertion\": {\n \"credentialIdB64\": \"AaIWjR2k8Qm3sV7tZ1pYxC\",\n \"userHandleB64\": \"dXNlci1jdXN0XzNmOWEyYjdl\",\n \"clientDataJSON_B64\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0\",\n \"authenticatorDataB64\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M\",\n \"signatureB64\": \"MEUCIQDx9z...sig...redacted\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.next.orenda.finance/v1/auth/passkey/verify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-program-id"] = '<x-program-id>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"session_token\": \"3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b\",\n \"assertion\": {\n \"credentialIdB64\": \"AaIWjR2k8Qm3sV7tZ1pYxC\",\n \"userHandleB64\": \"dXNlci1jdXN0XzNmOWEyYjdl\",\n \"clientDataJSON_B64\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0\",\n \"authenticatorDataB64\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M\",\n \"signatureB64\": \"MEUCIQDx9z...sig...redacted\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"authenticated": true,
"access_token": "eyJhbGciOi...JWT...access...redacted",
"id_token": "eyJhbGciOi...JWT...id...redacted",
"refresh_token": "eyJjdHkiOi...JWT...refresh...redacted",
"token_type": "Bearer",
"expires_in": 3600,
"hasPasskey": true,
"hasMfa": true
}
}{
"success": false,
"code": "INVALID_JSON",
"message": "Invalid or malformed JSON in request body"
}{
"success": false,
"code": "UNAUTHORIZED",
"message": "Passkey authentication failed"
}{
"success": false,
"code": "RESOURCE_NOT_FOUND",
"message": "Program issuer not found"
}{
"success": false,
"code": "SESSION_EXPIRED",
"message": "Challenge session has expired"
}{
"success": false,
"code": "VALIDATION_ERROR",
"message": "email is required"
}{
"success": false,
"code": "INTERNAL_SERVER_ERROR",
"message": "Internal Server Error"
}Passkey sign-in (recommended)
Passkey sign-in (finish)
Send the device’s assertion and receive the tokens.
POST
/
v1
/
auth
/
passkey
/
verify
Finish a passkey login
curl --request POST \
--url https://api.next.orenda.finance/v1/auth/passkey/verify \
--header 'Content-Type: application/json' \
--header 'x-program-id: <x-program-id>' \
--data '
{
"session_token": "3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b",
"assertion": {
"credentialIdB64": "AaIWjR2k8Qm3sV7tZ1pYxC",
"userHandleB64": "dXNlci1jdXN0XzNmOWEyYjdl",
"clientDataJSON_B64": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0",
"authenticatorDataB64": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M",
"signatureB64": "MEUCIQDx9z...sig...redacted"
}
}
'import requests
url = "https://api.next.orenda.finance/v1/auth/passkey/verify"
payload = {
"session_token": "3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b",
"assertion": {
"credentialIdB64": "AaIWjR2k8Qm3sV7tZ1pYxC",
"userHandleB64": "dXNlci1jdXN0XzNmOWEyYjdl",
"clientDataJSON_B64": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0",
"authenticatorDataB64": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M",
"signatureB64": "MEUCIQDx9z...sig...redacted"
}
}
headers = {
"x-program-id": "<x-program-id>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-program-id': '<x-program-id>', 'Content-Type': 'application/json'},
body: JSON.stringify({
session_token: '3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b',
assertion: {
credentialIdB64: 'AaIWjR2k8Qm3sV7tZ1pYxC',
userHandleB64: 'dXNlci1jdXN0XzNmOWEyYjdl',
clientDataJSON_B64: 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0',
authenticatorDataB64: 'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M',
signatureB64: 'MEUCIQDx9z...sig...redacted'
}
})
};
fetch('https://api.next.orenda.finance/v1/auth/passkey/verify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.next.orenda.finance/v1/auth/passkey/verify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'session_token' => '3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b',
'assertion' => [
'credentialIdB64' => 'AaIWjR2k8Qm3sV7tZ1pYxC',
'userHandleB64' => 'dXNlci1jdXN0XzNmOWEyYjdl',
'clientDataJSON_B64' => 'eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0',
'authenticatorDataB64' => 'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M',
'signatureB64' => 'MEUCIQDx9z...sig...redacted'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-program-id: <x-program-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.next.orenda.finance/v1/auth/passkey/verify"
payload := strings.NewReader("{\n \"session_token\": \"3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b\",\n \"assertion\": {\n \"credentialIdB64\": \"AaIWjR2k8Qm3sV7tZ1pYxC\",\n \"userHandleB64\": \"dXNlci1jdXN0XzNmOWEyYjdl\",\n \"clientDataJSON_B64\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0\",\n \"authenticatorDataB64\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M\",\n \"signatureB64\": \"MEUCIQDx9z...sig...redacted\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-program-id", "<x-program-id>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.next.orenda.finance/v1/auth/passkey/verify")
.header("x-program-id", "<x-program-id>")
.header("Content-Type", "application/json")
.body("{\n \"session_token\": \"3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b\",\n \"assertion\": {\n \"credentialIdB64\": \"AaIWjR2k8Qm3sV7tZ1pYxC\",\n \"userHandleB64\": \"dXNlci1jdXN0XzNmOWEyYjdl\",\n \"clientDataJSON_B64\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0\",\n \"authenticatorDataB64\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M\",\n \"signatureB64\": \"MEUCIQDx9z...sig...redacted\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.next.orenda.finance/v1/auth/passkey/verify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-program-id"] = '<x-program-id>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"session_token\": \"3f1c2e8a-9b4d-4e6f-8a1b-2c3d4e5f6a7b\",\n \"assertion\": {\n \"credentialIdB64\": \"AaIWjR2k8Qm3sV7tZ1pYxC\",\n \"userHandleB64\": \"dXNlci1jdXN0XzNmOWEyYjdl\",\n \"clientDataJSON_B64\": \"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0In0\",\n \"authenticatorDataB64\": \"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M\",\n \"signatureB64\": \"MEUCIQDx9z...sig...redacted\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"authenticated": true,
"access_token": "eyJhbGciOi...JWT...access...redacted",
"id_token": "eyJhbGciOi...JWT...id...redacted",
"refresh_token": "eyJjdHkiOi...JWT...refresh...redacted",
"token_type": "Bearer",
"expires_in": 3600,
"hasPasskey": true,
"hasMfa": true
}
}{
"success": false,
"code": "INVALID_JSON",
"message": "Invalid or malformed JSON in request body"
}{
"success": false,
"code": "UNAUTHORIZED",
"message": "Passkey authentication failed"
}{
"success": false,
"code": "RESOURCE_NOT_FOUND",
"message": "Program issuer not found"
}{
"success": false,
"code": "SESSION_EXPIRED",
"message": "Challenge session has expired"
}{
"success": false,
"code": "VALIDATION_ERROR",
"message": "email is required"
}{
"success": false,
"code": "INTERNAL_SERVER_ERROR",
"message": "Internal Server Error"
}Step 2 of signing in with a passkey. Send the
session_token from
the start call together with the assertion the device
produced. On success you receive your tokens. Public —
no bearer token.
Encode the device result into these fields (all base64url strings):
| Field | From the credential |
|---|---|
credentialIdB64 | the raw credential id |
userHandleB64 | the user handle |
clientDataJSON_B64 | the client data JSON |
authenticatorDataB64 | the authenticator data |
signatureB64 | the signature |
The whole-flow example on the start page shows
this call in context, in JavaScript, Kotlin, Swift, and Flutter.
Headers
Identifies the program. Can also be sent as the programId query parameter.
Body
application/json
⌘I