ENOKI HTTP API Request on React Native

We are developing a mobile app using Expo (React Native). Since the @mysten/enoki package throws an error in React Native, we want to use the Enoki HTTP API instead. However, I received an error during the /v1/zklogin/nonce request.

I sent the ephemeral key as Base64 using the following code:

export function makeEphemeralPublicKey() {
const kp = Ed25519Keypair.generate();
const publicKey = kp.getPublicKey().toBase64();
return publicKey;
}
But I received this error:
{“errors”:[{“code”:“custom”,“message”:“Public key is not a valid Ed25519 or Secp256r1 public key”,“path”:[“ephemeralPublicKey”]}]}

For reference, an example ephemeralPublicKey returned by the makeEphemeralPublicKe function is:
uBkVkSG71lcWXiCJO5/+0NzPdyLu9yYubzckl42I/kA=

How can I fix this error?

Can you post an example of the public key you post to the api?
Two common catches are:

  1. accidentally sending Sui’s flag-prefixed public key (aka “SuiPublicKey”, 33 bytes), or

  2. sending something that isn’t exactly 32 bytes after base64-decoding.

Here’s an example ephemeralPublicKey I send to the API:

zZQ6r425fk/bgxz3kTjtlOQ67cMzzZTZv0szIIlzjVc=

This key is created by generating a new Ed25519 keypair and converting the public key to Base64:
const kp = Ed25519Keypair.generate();
const publicKey = kp.getPublicKey().toBase64();

Thanks for the sample. The issue seems to be that getPublicKey().toBase64() returns Sui’s flag-prefixed public key (“SuiPublicKey”), while Enoki expects the raw public key bytes.

Try the following and send the raw 32-byte key, base64-encoded:

import { toB64 } from ‘@mysten/bcs’;
import { Ed25519Keypair } from ‘@mysten/sui.js/cryptography’;

const kp = Ed25519Keypair.generate();
// ❌ Sui format (flag + key): kp.getPublicKey().toBase64()
// ✅ Raw 32B:
const ephemPkB64 = toB64(kp.getPublicKey().toRawBytes());

const res = await fetch(‘https://api.enoki.mystenlabs.com/v1/zklogin/nonce’, {
  method: ‘POST’,
  headers: {
    ‘Content-Type’: ‘application/json’,
    ‘Authorization’: Bearer ${ENOKI_API_KEY}, // if required
  },
  body: JSON.stringify({
    network: ‘testnet’,              // optional
    ephemeralPublicKey: ephemPkB64,  // raw key in base64
  }),
});

To make sure you send the correct format base64-decode before sending and ensure length === 32 for Ed25519

Thanks for the feedback.
toRawBytes() didn’t work for me for some reason, but when I changed it as below, it worked:

const publicKey = toB64(kp.getPublicKey().toSuiBytes());

I’m developing a dApp on mobile. I realized that passing the nonce to googleSignin is a bit problematic compared to the web app. So I tried using the zkLogin HTTP request instead:

fetch("https://api.enoki.mystenlabs.com/v1/zklogin", {
  method: "GET",
  headers: {
    "zklogin-jwt": "string",
    "Authorization": "Bearer "
  }
})

This returns:

{
  "data": {
    "salt": "string",
    "address": "string",
    "publicKey": "string"
  }
}

The issue is that, when I want to request a zero-knowledge proof, I’m providing randomness but not requesting a salt. How should I handle creating the zero-knowledge proof in this case?

const body = JSON.stringify({
  "ephemeralPublicKey": "string",
  "maxEpoch": 0,
  "randomness": "string"
})

fetch("https://api.enoki.mystenlabs.com/v1/zklogin/zkp", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "zklogin-jwt": "string",
    "Authorization": "Bearer "
  },
  body
})

Let me write the flow step by step, it might help clarify that part. So,

  1. Create a nonce (Enoki server)
    • You send your raw 32-byte Ed25519 ephemeral public key (base64).
    • Enoki returns { nonce, randomness, epoch, maxEpoch }.
curl -X POST https://api.enoki.mystenlabs.com/v1/zklogin/nonce \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_ENOKI_API_KEY>' \
  -d '{"ephemeralPublicKey":"<base64 32B>"}'
  1. Do OAuth with your provider
  • Pass the nonce from step 1 into the provider’s login.
  • You receive a JWT from the provider.
  1. (Optional) Get salt/address (Enoki server)
  • Useful to confirm the derived Sui address before proving
curl -X GET https://api.enoki.mystenlabs.com/v1/zklogin \
  -H 'Authorization: Bearer <YOUR_ENOKI_API_KEY>' \
  -H 'zklogin-jwt: <OIDC_JWT_FROM_PROVIDER>'
  1. Request the ZK proof (Enoki server)
  • You do not send salt here.
  • Send the same ephemeralPublicKey, the maxEpoch, and the exact randomness from step 1, plus the JWT in a header.
curl -X POST https://api.enoki.mystenlabs.com/v1/zklogin/zkp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <YOUR_ENOKI_API_KEY>' \
  -H 'zklogin-jwt: <OIDC_JWT_FROM_PROVIDER>' \
  -d '{
    "ephemeralPublicKey":"<base64 32B>",
    "maxEpoch": <from step 1>,
    "randomness":"<from step 1>"
  }'

Let me know if something does not work