aZKey is a privacy-preserving authentication system that enables users to create and manage Aztec Network accounts using familiar Web2 identity providers (starting with Google) without exposing personal information on-chain. The system leverages zero-knowledge proofs to cryptographically verify JWT validity while keeping sensitive data completely private.
- 🔐 Privacy-Preserving: Google identity (
jwt.sub,jwt.aud) never revealed on-chain - 🛡️ Zero-Knowledge Proofs: Cryptographic proofs verify authentication without data exposure
- 🔑 Self-Custody: Users control accounts through Web2 identity, no seed phrases required
- 🚀 Modular Architecture: Extensible design for easy integration with other Web2 providers (Apple, Facebook, etc.)
- ⚡ Optimized Circuits: Minimized constraint count for efficient proving in Noir
- User Interface (SvelteKit): Web application handling OAuth flows and user interactions
- SDK (TypeScript): Client-side library for proof generation and Aztec network interaction
- Noir Circuits: Zero-knowledge circuits for JWT verification and account management
- Aztec Account Contract: Smart contract storing account state and verifying ZK proofs
- Identity Provider (Google): OAuth provider issuing JWTs for authentication
The following sequence diagram illustrates the complete flow from user authentication to account creation/recovery:
sequenceDiagram
participant User
participant UI as SvelteKit UI
participant SDK as TypeScript SDK
participant Google as Google OAuth
participant ProofGen as Proof Generator
participant Noir as Noir Circuit
participant Aztec as Aztec Network
participant Contract as JwtAccount Contract
Note over User,Contract: Account Creation Flow
User->>UI: Click "Sign in with Google"
UI->>SDK: getAuthUrl
SDK->>Google: Redirect to OAuth
Google->>User: Show login page
User->>Google: Authenticate
Google->>UI: Redirect with auth code
UI->>SDK: exchangeCode with code
SDK->>Google: Exchange code for JWT
Google->>SDK: Return JWT id_token
Note over SDK,Noir: Proof Generation
SDK->>ProofGen: generateProof with authResult and sessionOwnerNonce
ProofGen->>ProofGen: Extract sub, aud from JWT
ProofGen->>ProofGen: Derive accountId = hash(sub, aud)
ProofGen->>ProofGen: Generate ephemeral sessionOwnerNonce
ProofGen->>Google: Fetch Google public key
Google->>ProofGen: Return public key
ProofGen->>Noir: Prepare inputs with JWT and public key as private, accountId and nonce as public
Noir->>Noir: Parse JWT - header, payload, signature
Noir->>Noir: Verify JWT signature with Google public key
Noir->>Noir: Verify accountId matches hash(sub, aud)
Noir->>ProofGen: Return ZK proof + public inputs
Note over SDK,Contract: Contract Deployment
SDK->>Aztec: Deploy JwtAccount contract
Aztec->>Contract: Constructor with accountId
Contract->>Contract: Store accountId in storage
Aztec->>SDK: Return contract address
SDK->>Aztec: set_owner with proof, publicInputs, sessionOwner
Aztec->>Contract: set_owner with proof, accountId, nonce, sessionOwner
Contract->>Contract: Verify proof via embedded verifier
Contract->>Contract: Assert publicInputs index 0 equals stored accountId
Contract->>Contract: Store sessionOwner in storage
Contract->>Aztec: Transaction confirmed
Aztec->>SDK: Success
SDK->>UI: Account created
UI->>User: Show success + account info
Note over User,Contract: Account Recovery Flow
User->>UI: Click "Recover Account"
UI->>SDK: recoverAccount with authResult and contractAddress
SDK->>Aztec: Check contract exists
Aztec->>SDK: Contract found
SDK->>ProofGen: generateProof with authResult and newSessionOwnerNonce
ProofGen->>Noir: Generate new ZK proof
Noir->>ProofGen: Return proof + public inputs
SDK->>Aztec: set_owner with proof, publicInputs, newSessionOwner
Aztec->>Contract: set_owner with proof, accountId, newNonce, newSessionOwner
Contract->>Contract: Verify proof - same accountId
Contract->>Contract: Update sessionOwner
Contract->>Aztec: Transaction confirmed
Aztec->>SDK: Recovery successful
SDK->>UI: Account recovered
UI->>User: Show success + new session key
- Authentication: User authenticates with Google OAuth, receiving a JWT token
- Proof Generation: SDK extracts JWT claims, derives
accountId, and generates a ZK proof using the Noir circuit - Circuit Verification: Noir circuit privately verifies JWT signature and validates
accountIdmatcheshash(sub, aud) - Contract Deployment: New account contract is deployed with the
accountIdstored - Session Owner Setup: ZK proof is submitted to set the temporary
sessionOwner(ephemeral EOA) - Recovery: Same flow with existing contract, updating to a new
sessionOwnerwhen keys are lost
This guide shows how to integrate aZKey into your application or add support for new identity providers.
npm install @azkey/sdk
# or
pnpm add @azkey/sdkimport{AccountManager}from'@azkey/sdk';constaccountManager=newAccountManager({googleClientId: process.env.GOOGLE_CLIENT_ID!,googleClientSecret: process.env.GOOGLE_CLIENT_SECRET,aztecRpcUrl: process.env.AZTEC_RPC_URL||'http://localhost:8080',redirectUri: 'https://yourapp.com/oauth/callback'});// Step 1: Redirect user to GoogleconstauthUrl=accountManager.getAuthUrl();window.location.href=authUrl;// Step 2: Handle callback (in your callback route)consturlParams=newURLSearchParams(window.location.search);constcode=urlParams.get('code');constauthResult=awaitaccountManager.exchangeCode(code!);// Check if account existsconstexistingAddress=awaitaccountManager.accountExistsForIdentity(authResult);if(!existingAddress){// Create new accountconstaccountInfo=awaitaccountManager.createAccount(authResult);console.log('Account created:',accountInfo.address);console.log('Session owner:',accountInfo.sessionOwner);}else{// Recover existing accountconstaccountInfo=awaitaccountManager.recoverAccount(authResult,existingAddress);console.log('Account recovered:',accountInfo.address);}import{AztecAddress}from'@aztec/aztec.js';// Get session owner (ephemeral key) from account infoconstsessionOwner=accountInfo.sessionOwner;// Use sessionOwner to sign and send transactions// (Implementation depends on your Aztec client setup)The SDK is designed with modularity in mind. To add support for a new provider (e.g., Apple, Facebook):
// packages/sdk/src/providers/apple.tsimport{BaseProvider,ProviderConfig,AuthResult,JWTToken}from'./base.js';exportclassAppleProviderextendsBaseProvider{privatestaticreadonlyAPPLE_AUTH_URL='https://appleid.apple.com/auth/authorize';privatestaticreadonlyAPPLE_TOKEN_URL='https://appleid.apple.com/auth/token';privatestaticreadonlyAPPLE_CERTS_URL='https://appleid.apple.com/auth/keys';constructor(config: ProviderConfig){super(config);}getAuthUrl(state?: string): string{constparams=newURLSearchParams({client_id: this.config.clientId,redirect_uri: this.config.redirectUri||'',response_type: 'code',scope: 'openid email',state: state||'',response_mode: 'form_post'});return`${AppleProvider.APPLE_AUTH_URL}?${params.toString()}`;}asyncexchangeCode(code: string): Promise<AuthResult>{// Implement Apple-specific token exchange// Similar to GoogleProvider.exchangeCode()}parseJWT(token: string): JWTToken{// Parse Apple JWT format// Similar to GoogleProvider.parseJWT()}asyncgetPublicKey(): Promise<Uint8Array>{// Fetch Apple public keys from Apple's JWKS endpoint// Similar to GoogleProvider.getPublicKey()}}// packages/sdk/src/account/manager.tsimport{AppleProvider}from'../providers/apple.js';exportclassAccountManager{// Add provider type parameterconstructor(config: AccountConfig,provider: 'google'|'apple'='google'){if(provider==='apple'){this.provider=newAppleProvider({/* config */});}else{this.provider=newGoogleProvider({/* config */});}// ... rest of initialization}}If the new provider uses different JWT structure or signature algorithm, you may need to update the Noir circuit:
// packages/contracts/noir/jwt_account/src/main.nr// Add provider-specific verification logic if needed// Most providers follow standard JWT format, so minimal changes required// Test the new providerconstaccountManager=newAccountManager(config,'apple');constauthResult=awaitaccountManager.exchangeCode(code);constaccountInfo=awaitaccountManager.createAccount(authResult);- Install
@azkey/sdkpackage - Configure OAuth credentials (Google/Apple/etc.)
- Set up Aztec RPC endpoint
- Implement OAuth callback handler
- Handle account creation/recovery flow
- Store account address and session owner securely
- Implement transaction signing with session owner
- Add error handling for proof generation failures
- Test account recovery flow
# RequiredGOOGLE_CLIENT_ID=your_google_client_idAZTEC_RPC_URL=http://localhost:8080# OptionalGOOGLE_CLIENT_SECRET=your_google_client_secretNODE_ENV=development- Secure Storage: Store session owner keys securely (browser storage, encrypted, or hardware wallet)
- Error Handling: Implement robust error handling for OAuth failures, proof generation errors, and network issues
- Proof Caching: Consider caching proofs for a short period to avoid regenerating on every request
- Session Management: Implement proper session management for the ephemeral session owner keys
- Provider Abstraction: Use the provider abstraction layer to easily switch between providers
MIT License - see LICENSE file for details.