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';
const accountManager = new AccountManager({
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 Google
const authUrl = accountManager.getAuthUrl();
window.location.href = authUrl;
// Step 2: Handle callback (in your callback route)
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const authResult = await accountManager.exchangeCode(code!);// Check if account exists
const existingAddress = await accountManager.accountExistsForIdentity(authResult);
if (!existingAddress) {
// Create new account
const accountInfo = await accountManager.createAccount(authResult);
console.log('Account created:', accountInfo.address);
console.log('Session owner:', accountInfo.sessionOwner);
} else {
// Recover existing account
const accountInfo = await accountManager.recoverAccount(authResult, existingAddress);
console.log('Account recovered:', accountInfo.address);
}import { AztecAddress } from '@aztec/aztec.js';
// Get session owner (ephemeral key) from account info
const sessionOwner = 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.ts
import { BaseProvider, ProviderConfig, AuthResult, JWTToken } from './base.js';
export class AppleProvider extends BaseProvider {
private static readonly APPLE_AUTH_URL = 'https://appleid.apple.com/auth/authorize';
private static readonly APPLE_TOKEN_URL = 'https://appleid.apple.com/auth/token';
private static readonly APPLE_CERTS_URL = 'https://appleid.apple.com/auth/keys';
constructor(config: ProviderConfig) {
super(config);
}
getAuthUrl(state?: string): string {
const params = new URLSearchParams({
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()}`;
}
async exchangeCode(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()
}
async getPublicKey(): Promise<Uint8Array> {
// Fetch Apple public keys from Apple's JWKS endpoint
// Similar to GoogleProvider.getPublicKey()
}
}// packages/sdk/src/account/manager.ts
import { AppleProvider } from '../providers/apple.js';
export class AccountManager {
// Add provider type parameter
constructor(
config: AccountConfig,
provider: 'google' | 'apple' = 'google'
) {
if (provider === 'apple') {
this.provider = new AppleProvider({ /* config */ });
} else {
this.provider = new GoogleProvider({ /* 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 provider
const accountManager = new AccountManager(config, 'apple');
const authResult = await accountManager.exchangeCode(code);
const accountInfo = await accountManager.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
# Required
GOOGLE_CLIENT_ID=your_google_client_id
AZTEC_RPC_URL=http://localhost:8080
# Optional
GOOGLE_CLIENT_SECRET=your_google_client_secret
NODE_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.