Authentication
Learn how authentication works in Civra and how to add it to your Web3 applications.
Civra Platform Authentication
Sign Up Methods
Google OAuth
- One-click signup with Google account
- Automatic profile creation
- Free plan with 10 message credits
GitHub OAuth
- Perfect for developers
- Connect GitHub repos (coming soon)
- Same free tier benefits
How It Works
1. Click "Sign In"
↓
2. Choose provider (Google/GitHub)
↓
3. Authorize Civra
↓
4. Auto-create:
- User profile
- Free subscription
- Initial credits (10 msg, 100 int)
↓
5. Start building!Security Features
Supabase Auth
- Industry-standard authentication
- JWT-based sessions
- Secure cookie storage
- Automatic token refresh
Row Level Security (RLS)
sql
-- Users can only access their own data
CREATE POLICY "Users can view own projects"
ON projects FOR SELECT
USING (auth.uid() = user_id);Server-Side Validation
typescript
// All API routes validate auth
export async function POST(req: Request) {
const session = await getServerSession(authOptions)
if (!session) {
return new Response('Unauthorized', { status: 401 })
}
// ... protected logic
}Adding Auth to Your dApp
Web3 Wallet Authentication
Civra makes it easy to add wallet-based auth to your applications.
Basic Setup
typescript
import { WalletAuth } from '@civra/web3'
export default function App() {
return (
<WalletAuth>
<YourApp />
</WalletAuth>
)
}Connect Wallet
typescript
import { useWalletAuth } from '@civra/web3'
export function ConnectButton() {
const { connect, disconnect, address, isConnected } = useWalletAuth()
if (isConnected) {
return (
<div>
<span>{address}</span>
<button onClick={disconnect}>Disconnect</button>
</div>
)
}
return <button onClick={connect}>Connect Wallet</button>
}Sign-In With Ethereum (SIWE)
Prove wallet ownership:
typescript
import { useSignIn } from '@civra/web3'
export function SignInButton() {
const { signIn, isLoading } = useSignIn()
const handleSignIn = async () => {
try {
const message = `Sign in to MyApp\n\nNonce: ${Date.now()}`
const signature = await signIn(message)
// Verify signature on backend
const response = await fetch('/api/auth/verify', {
method: 'POST',
body: JSON.stringify({ message, signature })
})
if (response.ok) {
console.log('Signed in!')
}
} catch (error) {
console.error('Sign in failed:', error)
}
}
return (
<button onClick={handleSignIn} disabled={isLoading}>
{isLoading ? 'Signing...' : 'Sign In'}
</button>
)
}Backend Verification
typescript
// app/api/auth/verify/route.ts
import { verifyMessage } from 'ethers'
export async function POST(req: Request) {
const { message, signature } = await req.json()
// Verify signature
const address = verifyMessage(message, signature)
// Create session
const session = await createSession(address)
return Response.json({ success: true, session })
}Authentication Patterns
1. Wallet-Only Auth
Simplest approach - no email/password needed.
typescript
// Just connect wallet
const { address } = useWallet()
// Use address as user ID
const userProfile = await fetchProfile(address)Pros:
- ✅ Truly decentralized
- ✅ No passwords to manage
- ✅ Privacy-preserving
Cons:
- ❌ Lose wallet = lose access
- ❌ No account recovery
- ❌ One device at a time
Best for: Pure dApps, DeFi, NFT platforms
2. Hybrid Auth (Wallet + Email)
Combine wallet and traditional auth.
typescript
import { useHybridAuth } from '@civra/web3'
export function AuthComponent() {
const {
signInWithEmail,
signInWithWallet,
linkWallet,
user
} = useHybridAuth()
// User can sign in via email first
await signInWithEmail('user@example.com')
// Then link their wallet
await linkWallet()
}Pros:
- ✅ Account recovery via email
- ✅ Multi-device support
- ✅ Fallback option
Cons:
- ❌ Less decentralized
- ❌ Email required
- ❌ More complex
Best for: Web3 games, social dApps, marketplaces
3. NFT-Gated Auth
Require NFT ownership for access.
typescript
import { useNFTGate } from '@civra/web3'
export function ProtectedContent() {
const { hasNFT, loading } = useNFTGate({
contract: '0x...',
chainId: 1
})
if (loading) return <div>Checking NFT...</div>
if (!hasNFT) return <div>You need the NFT to access this</div>
return <div>Welcome, NFT holder!</div>
}Use Cases:
- Exclusive communities
- Premium content
- Member-only features
- Governance participation
4. Token-Gated Auth
Require token balance for access.
typescript
import { useTokenGate } from '@civra/web3'
export function VIPSection() {
const { hasBalance, balance } = useTokenGate({
token: '0x...',
minAmount: '1000', // 1000 tokens minimum
chainId: 137
})
if (!hasBalance) {
return (
<div>
You need 1,000 tokens to access.
Current balance: {balance}
</div>
)
}
return <div>Welcome, VIP member!</div>
}Use Cases:
- Tiered access
- Governance voting
- Staking rewards
- Premium features
5. Multi-Sig Auth
Require multiple signatures.
typescript
import { useMultiSig } from '@civra/web3'
export function AdminPanel() {
const { requestSignatures, approvals } = useMultiSig({
required: 3,
signers: ['0x...', '0x...', '0x...']
})
const executeAction = async () => {
const signatures = await requestSignatures({
message: 'Approve admin action',
action: 'transfer_funds'
})
if (signatures.length >= 3) {
// Execute action
}
}
}Use Cases:
- Treasury management
- Admin actions
- High-value transactions
- DAO operations
Session Management
Persistent Sessions
Keep users logged in:
typescript
// Auto-reconnect on page load
const { autoConnect } = useWalletAuth()
useEffect(() => {
autoConnect()
}, [])Session Timeout
Add security with timeouts:
typescript
const SESSION_TIMEOUT = 30 * 60 * 1000 // 30 minutes
const { lastActivity, resetTimer } = useSession()
useEffect(() => {
const timeout = setTimeout(() => {
if (Date.now() - lastActivity > SESSION_TIMEOUT) {
disconnect()
}
}, SESSION_TIMEOUT)
return () => clearTimeout(timeout)
}, [lastActivity])Multi-Chain Sessions
Support multiple networks:
typescript
const { sessions } = useMultiChainAuth()
// Separate session per chain
const ethereumSession = sessions[1]
const polygonSession = sessions[137]Security Best Practices
1. Verify Signatures Server-Side
typescript
// ❌ Don't trust client
if (clientSignature === expectedSignature) { ... }
// ✅ Verify on server
const recoveredAddress = verifyMessage(message, signature)
if (recoveredAddress === expectedAddress) { ... }2. Use Nonces
Prevent replay attacks:
typescript
// Generate unique nonce
const nonce = crypto.randomUUID()
// Store nonce
await redis.set(`nonce:${nonce}`, true, 'EX', 300)
// Verify nonce hasn't been used
const message = `Sign in\nNonce: ${nonce}`3. Check Chain ID
Prevent cross-chain attacks:
typescript
const { chainId } = useNetwork()
if (chainId !== 1) {
throw new Error('Please switch to Ethereum Mainnet')
}4. Implement Rate Limiting
typescript
// Limit sign-in attempts
const attempts = await redis.incr(`attempts:${address}`)
if (attempts > 5) {
throw new Error('Too many attempts. Try again later.')
}5. Sanitize Inputs
typescript
import { isAddress } from 'ethers'
if (!isAddress(userInput)) {
throw new Error('Invalid address')
}OAuth Integration
Add Social Login
typescript
import { signIn } from 'next-auth/react'
// Google
await signIn('google')
// GitHub
await signIn('github')
// Discord
await signIn('discord')
// Twitter
await signIn('twitter')Link Social Accounts
typescript
// Link wallet to social account
await linkAccount({
provider: 'google',
wallet: address
})
// Sign in with either
const user = await signIn('google')
// or
const user = await signIn('wallet')
// Both access same accountAdvanced Features
Biometric Auth (Mobile)
typescript
import { useBiometric } from '@civra/web3-mobile'
const { authenticate } = useBiometric()
// Require fingerprint/face ID
await authenticate({
reason: 'Sign transaction',
fallback: 'password'
})Hardware Wallet Support
typescript
import { useLedger } from '@civra/web3'
const { connect, sign } = useLedger()
// Connect Ledger
await connect()
// Sign with hardware wallet
const signature = await sign(message)Social Recovery
typescript
// Set recovery contacts
await setRecoveryContacts([
'0x...', // Friend 1
'0x...', // Friend 2
'0x...' // Friend 3
])
// Require 2 of 3 to recover
await recoverAccount({
signatures: [sig1, sig2]
})Testing Auth
Mock Wallet Connection
typescript
// In tests
import { mockWallet } from '@civra/web3/testing'
test('user can connect wallet', async () => {
const { connect } = mockWallet({
address: '0x123...',
chainId: 1
})
await connect()
expect(getAddress()).toBe('0x123...')
})Test Networks
Use testnets for development:
typescript
const config = {
// Development
chains: [sepolia, mumbai],
// Production
// chains: [mainnet, polygon]
}