Skip to main content

zkLogin Technical Reference

This page is the technical reference for zkLogin. For a conceptual overview, see zkLogin. For implementation steps, see the Integration Guide.

OpenID providers

The following table lists the OpenID providers that can support zkLogin or are currently being reviewed to determine whether they can support zkLogin.

ProviderCan support?DevnetTestnetMainnet
FacebookYesYesYesYes
GoogleYesYesYesYes
TwitchYesYesYesYes
AppleYesYesYesYes
SlackYesYesNoNo
KakaoYesYesNoNo
MicrosoftYesYesNoNo
AWS (Tenant)*YesYesYesYes
Karrier OneYesYesYesYes
Credenza3YesYesYesYes
RedBullUnder reviewNoNoNo
AmazonUnder reviewNoNoNo
WeChatUnder reviewNoNoNo
Auth0Under reviewNoNoNo
OktaUnder reviewNoNoNo
  • Sui supports AWS (Tenant) but the provider is enabled per tenant. Contact us for more information.

Entities

The zkLogin protocol involves three entities beyond the user:

  1. Application frontend: The wallet or frontend that stores the ephemeral private key, directs the user through the OAuth login flow, and creates and signs zkLogin transactions.

  2. Salt service: A backend service that returns a consistent, unique salt for each user. See Salt management for strategies.

  3. Proving service: A backend service that generates zero-knowledge proofs from the JWT, JWT randomness, user salt, and max epoch. The proof is submitted onchain with the ephemeral signature.

Terminology

The following terms come from the OpenID Connect specification.

OpenID provider (OP)

An OAuth 2.0 authorization server that authenticates an end-user and provides claims to a relying party. Identified by the iss field in the JWT payload. See the table of supported providers.

Relying party (RP) or client

An OAuth 2.0 client application that requires end-user authentication. Assigned by an OP when you register your application. Identified by the aud field in the JWT payload. This is your zkLogin-enabled wallet or application.

Subject identifier (sub)

A locally unique identifier for the end user within the issuer. zkLogin uses sub as the key claim to derive the user's address.

JSON Web Key (JWK)

A JSON data structure representing an OP's public keys. A public endpoint (for example, https://www.googleapis.com/oauth2/v3/certs) provides the keys corresponding to each kid. In Sui, validators call JWK endpoints independently, and the latest JWKs for all supported providers are updated during protocol upgrades. Correctness is guaranteed by quorum (2f+1) of validator stake.

JSON Web Token (JWT)

The token returned in the redirect URI after the OAuth login flow (https://redirect.com?id_token=$JWT_TOKEN). A JWT contains a header, payload, and signature. The signature is an RSA signature verified against jwt_message = header + . + payload using the JWK identified by kid.

Header fields:

NameExample ValueUsage
algRS256zkLogin only supports RS256 (RSA + SHA-256).
kidc3afe7a9bda46bae6ef97e46c95cda48912e5979Identifies the JWK that should be used to verify the JWT.
typJWTzkLogin only supports JWT.

Payload fields:

NameExample ValueUsage
isshttps://accounts.google.comUnique identifier for the OAuth provider.
aud575519200000-msop9ep45u2uo98hapqmngv8d8000000.apps.googleusercontent.comUnique identifier for the relying party (your app), assigned by the OAuth provider.
noncehTPpgF7XAKbW37rEUS6pEVZqmoISet by the relying party. zkLogin requires this to be the hash of the ephemeral public key, expiry, and randomness.
sub110463452167303000000Unique identifier for the user.

For zkLogin transactions, the iat and exp JWT timestamp claims are not used. Expiry is controlled by the nonce, which embeds max_epoch.

Key claim

The claim used to derive the address. zkLogin currently supports only sub because the OpenID specification mandates that providers do not change this identifier.

Notations

SymbolDescription
(eph_sk, eph_pk)Ephemeral private and public key pair. Same signing mechanism as traditional transactions, but short-lived (one session). The ephemeral public key is used to compute the nonce.
nonceToBase64URL(Poseidon_BN254([ext_eph_pk_bigint / 2^128, ext_eph_pk_bigint % 2^128, max_epoch, jwt_randomness]).to_bytes()[len - 20..]) where ext_eph_pk_bigint is the BigInt representation of ext_eph_pk.
ext_eph_pkByte representation of the ephemeral public key: flag || eph_pk. Size varies by signature scheme (see Signatures).
user_saltA value that unlinks the OAuth identifier from the onchain address.
max_epochThe epoch at which the ephemeral key expires (u64).
kc_nameKey claim name, for example sub.
kc_valueKey claim value, for example 110463452167303000000.
hashBytesToField(str, maxLen)Hashes an ASCII string to a BN254 field element using Poseidon hash.

How zkLogin works

The protocol works in three stages:

  1. A JWT is a signed payload from an OAuth provider that includes a user-defined nonce. zkLogin uses the OpenID Connect flow by setting the nonce to a hash of the ephemeral public key, a randomness value, and an expiry epoch.

  2. The wallet stores an ephemeral key pair. The ephemeral private key signs transactions for a short session. A Groth16 zero-knowledge proof generated from the JWT conceals sensitive fields.

  3. The transaction is submitted onchain with the ephemeral signature and zero-knowledge proof. Validators verify both before executing.

  4. The zkLogin address is derived from sub (user identifier), iss (provider), aud (application), and user_salt, not from a public key.

zkLogin flow diagram

Step 0: The Groth16 zkSNARK requires a common reference string (CRS). A ceremony generates the CRS, producing the proving key (for the proving service) and verifying key (for validators).

Steps 1–3: An ephemeral key pair (eph_sk, eph_pk) is generated. The public key, expiry (max_epoch), and randomness (jwt_randomness) are embedded in the nonce. The user logs in with an OpenID provider, and the JWT appears in the redirect URL.

Steps 4–5: The application sends the JWT to a salt service, which returns the unique user_salt based on iss, aud, and sub.

Steps 6–7: The JWT, user salt, ephemeral public key, randomness, and key claim name (sub) are sent to the proving service. The proof confirms:

  • The nonce is derived correctly.
  • The key claim value matches the JWT.
  • The RSA signature from the provider is valid.
  • The address is consistent with the key claim and salt.

zkLogin authority

Step 8: The application computes the user's address from iss, aud, sub, and user_salt.

Steps 9–10: The transaction is signed with the ephemeral private key and submitted with the ephemeral signature, zero-knowledge proof, and other inputs. Validators verify the proof against the provider's JWKs (stored by consensus) and the ephemeral signature.

Address definition

The address is computed from:

  1. zk_login_flag = 0x05: domain separator for zkLogin addresses.
  2. kc_name_F = hashBytesToField(kc_name, maxKCNameLen): key claim name (for example, sub) mapped to a BN254 field element.
  3. kc_value_F = hashBytesToField(kc_value, maxKCValueLen): key claim value mapped to a field element.
  4. aud_F = hashBytesToField(aud, maxAudValueLen): relying party identifier.
  5. iss: OpenID provider identifier.
  6. user_salt: value that unlinks the OAuth identifier from the address.

The final address:

addr_seed = Poseidon_BN254(kc_name_F, kc_value_F, aud_F, Poseidon_BN254(user_salt))
zk_login_address = Blake2b_256(zk_login_flag, iss_L, iss, addr_seed)

Address and session lifetime

The address is permanent. It does not change or expire as long as sub, iss, aud, and user_salt remain the same.

The ephemeral key pair and max_epoch control how long a single login session can authorize transactions. When the epoch passes max_epoch, the user logs in again, generates a new ephemeral key pair and zero-knowledge proof, and continues using the same address.

Logging in with a different OAuth provider or application produces a different address (different iss or aud). Using a different user_salt also produces a different address.

Ceremony

zkLogin uses Groth16 zkSNARKs for proof generation. Groth16 requires a computation-specific common reference string (CRS) generated through a trusted setup ceremony. The security model requires that at least one participant in the ceremony acted honestly, generated strong entropy, and discarded it.

The Sui zkLogin ceremony was a cryptographic multi-party computation (MPC) following the MMORPG protocol by Bowe, Gabizon, and Miers. It ran September 12–18, 2023, with 111 contributions (82 browser, 29 Docker) from participants with diverse backgrounds: Sui validators, cryptographers, Web3 experts, academics, and business leaders.

Click to open
Ceremony details

The protocol proceeds in two phases:

  • Phase 1 (circuit-agnostic): Adopted from the community-contributed perpetual powers of tau, specifically challenge #0081 (80 community contributions). The Drand random beacon at epoch #3298000 was applied to remove bias.

  • Phase 2 (zkLogin-circuit-specific): 111 contributions. Participants could contribute through a browser (snarkjs) or Docker (Kobi's implementation), providing software diversity. The Drand random beacon at epoch #3320606 was applied to remove bias.

The zkLogin circuit and ceremony client code were open-sourced before the ceremony. An audit report on the circuit from zkSecurity was also published.

All intermediate files can be reproduced following instructions for phase 1 and phase 2. The final CRS and all transcripts are available in a public repository.

The resulting proving key is stored with the proving service. The verifying key was deployed as part of the validator software (protocol version 25 in release 1.10.1).

Security and privacy

The following analysis covers each zkLogin artifact and the consequences of loss or exposure.

JWT

The JWT's validity is scoped to the client ID (aud), preventing phishing attacks. A JWT obtained for a malicious application cannot be used for your app's zkLogin. The JWT is sent directly to the application frontend through the redirect URL.

A leaked JWT can compromise user privacy (JWTs often contain usernames and emails) and, if a backend salt server uses JWTs for authentication, could be used to retrieve the user's salt.

A JWT leak does not mean loss of funds as long as the ephemeral private key is safe.

User salt

The salt is required for both zero-knowledge proof generation and address derivation. A leaked salt does not enable fund theft, but it allows an attacker to link the user's sub to their Sui address. The impact depends on whether the provider uses pairwise identifiers (for example, Facebook, which is unique per app, low risk) or public reusable identifiers (for example, Google, Twitch, which have a globally unique sub, higher privacy risk).

Losing the salt means permanent loss of access to that zkLogin address.

Ephemeral private key

The ephemeral key's lifespan is tied to max_epoch. If lost, generate a new key pair, complete a new OAuth login, and get a new zero-knowledge proof. If compromised, an attacker would also need the user salt and a valid zero-knowledge proof to move funds.

Zero-knowledge proof

The proof alone cannot create a valid transaction. An ephemeral signature over the transaction is also required.

Privacy

By default, there is no link between the OAuth sub and the Sui address (this is the purpose of the salt). The JWT is not published onchain. The values revealed onchain are iss, aud, and kid (needed to compute the public input hash). Sensitive fields like sub are private inputs to the proof.

The proving service and salt service can link user identity (they see both the JWT and salt), but both services are stateless by design.

Offchain signature verification

You can verify a zkLogin signature over transaction data or a personal message using the following methods:

  1. Sui TypeScript SDK: Use the SDK, which initializes a GraphQL client under the hood.

  2. GraphQL endpoint: Call https://sui-[network].mystenlabs.com/graphql directly. See the GraphQL documentation. Recommended if you do not want to run servers or handle JWK rotations.

  3. Sui Keytool CLI (debug use):

    sui keytool zk-login-sig-verify --sig $ZKLOGIN_SIG --bytes $BYTES --intent-scope 3 --network devnet --curr-epoch 3
  4. Self-hosted verifier: Use the zklogin-verifier for custom logic.

FAQ

What providers is zkLogin compatible with?

zkLogin supports providers that implement OpenID Connect on top of OAuth 2.0. See the supported providers table. Additional providers are enabled through protocol upgrades.

What are the circuit constraints?

Groth16 imposes length restrictions on several JWT fields, including aud (max 120 characters), iss, the JWT header, and payload. These limits were chosen after surveying JWTs from all supported providers.

Does zkLogin work on mobile?

Yes. zkLogin is a protocol-level primitive, not a feature of a specific application or wallet.

Is a new proof required for every transaction?

No. A proof is valid until the ephemeral key pair expires (when the current epoch exceeds max_epoch). Cache and reuse the proof across the session.

Can I convert a traditional wallet to a zkLogin wallet?

No. zkLogin addresses are derived differently from private-key-based addresses.

If an OAuth account is compromised, are funds at risk?

No. zkLogin is a two-factor system. An attacker who compromises the OAuth account still needs the salt to derive the address and sign transactions.

If I lose access to my OAuth account, do I lose access to the address?

Yes. You must produce a current JWT to use zkLogin. If recovery from a lost OAuth account is a concern, use a Sui multisig wallet with a backup signer, even another zkLogin signer from a different provider (for example, a 1-of-2 multisig with Google and Facebook).

How is zkLogin different from Web3Auth, Magic, or Privy?

Other social login solutions typically require one or more of: trusting a separate network to verify credentials (JWK oracle), trusting parties to manage persistent private keys (MPC, secure enclaves), or verifying JWTs or zero-knowledge proofs onchain (expensive). zkLogin avoids all three: validators agree on JWKs by consensus, no persistent private key exists, and zero-knowledge proofs are verified natively at the protocol level.