Skip to content

eth-ecuador/aZKey

Repository files navigation

aZKey - Aztec zkLogin

License

Project Summary

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.

Key Features

  • 🔐 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

Core Components

  • 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

System Flow Sequence Diagram

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
Loading

Flow Explanation

  1. Authentication: User authenticates with Google OAuth, receiving a JWT token
  2. Proof Generation: SDK extracts JWT claims, derives accountId, and generates a ZK proof using the Noir circuit
  3. Circuit Verification: Noir circuit privately verifies JWT signature and validates accountId matches hash(sub, aud)
  4. Contract Deployment: New account contract is deployed with the accountId stored
  5. Session Owner Setup: ZK proof is submitted to set the temporary sessionOwner (ephemeral EOA)
  6. Recovery: Same flow with existing contract, updating to a new sessionOwner when keys are lost

Integration Guide

This guide shows how to integrate aZKey into your application or add support for new identity providers.

Integrating aZKey into Your Application

1. Install Dependencies

npm install @azkey/sdk
# or
pnpm add @azkey/sdk

2. Initialize Account Manager

import { 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'
});

3. Handle OAuth Flow

// 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!);

4. Create Account

// 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);
}

5. Execute Transactions

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)

Adding Support for New Identity Providers

The SDK is designed with modularity in mind. To add support for a new provider (e.g., Apple, Facebook):

1. Create Provider Class

// 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()
  }
}

2. Update Account Manager

// 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
  }
}

3. Update Noir Circuit (if needed)

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

4. Test Integration

// Test the new provider
const accountManager = new AccountManager(config, 'apple');
const authResult = await accountManager.exchangeCode(code);
const accountInfo = await accountManager.createAccount(authResult);

Integration Checklist

  • Install @azkey/sdk package
  • 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

Environment Variables

# 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

Best Practices

  1. Secure Storage: Store session owner keys securely (browser storage, encrypted, or hardware wallet)
  2. Error Handling: Implement robust error handling for OAuth failures, proof generation errors, and network issues
  3. Proof Caching: Consider caching proofs for a short period to avoid regenerating on every request
  4. Session Management: Implement proper session management for the ephemeral session owner keys
  5. Provider Abstraction: Use the provider abstraction layer to easily switch between providers

License

MIT License - see LICENSE file for details.

About

Authenticate using web2 accounts as Google on Aztec preserving privacy.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published