技能 编程开发 跨平台认证系统迁移指南

跨平台认证系统迁移指南

v20260423
clerk-migration-deep-dive
本指南详细介绍了如何将从Auth0、Firebase、Supabase或NextAuth等现有认证系统迁移到Clerk。内容涵盖用户数据导出、批量导入、更新数据库引用和重写前端/后端认证代码,确保平稳、分阶段地切换用户认证服务。
获取技能
342 次下载
概览

Clerk Migration Deep Dive

Current State

!npm list @auth0/nextjs-auth0 next-auth @supabase/auth-helpers-nextjs firebase 2>/dev/null | grep -E "auth0|next-auth|supabase|firebase" || echo 'No auth providers detected'

Overview

Comprehensive guide to migrating from Auth0, Firebase Auth, Supabase Auth, or NextAuth to Clerk. Covers user data export, bulk import, parallel running, and phased migration.

Prerequisites

  • Current auth provider access with admin/export permissions
  • Clerk account with API keys
  • Git repository with clean working state
  • Migration timeline planned (recommend 2-4 weeks)

Instructions

Step 1: Export Users from Current Provider

Auth0 Export:

# Export users via Auth0 Management API
curl -s -H "Authorization: Bearer $AUTH0_TOKEN" \
  "https://$AUTH0_DOMAIN/api/v2/users?per_page=100&page=0" \
  | jq '[.[] | {email: .email, name: .name, picture: .picture, created_at: .created_at}]' \
  > auth0-users.json

NextAuth (Prisma) Export:

// scripts/export-nextauth-users.ts
const users = await prisma.user.findMany({
  include: { accounts: true },
})
const exported = users.map((u) => ({
  email: u.email,
  name: u.name,
  image: u.image,
  provider: u.accounts[0]?.provider,
  createdAt: u.createdAt,
}))
await fs.writeFile('nextauth-users.json', JSON.stringify(exported, null, 2))

Step 2: Import Users to Clerk

// scripts/import-to-clerk.ts
import { createClerkClient } from '@clerk/backend'
import users from './auth0-users.json'

const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })

interface MigrationResult {
  email: string
  status: 'created' | 'exists' | 'error'
  clerkId?: string
  error?: string
}

async function importUsers(): Promise<MigrationResult[]> {
  const results: MigrationResult[] = []

  for (const user of users) {
    try {
      const created = await clerk.users.createUser({
        emailAddress: [user.email],
        firstName: user.name?.split(' ')[0],
        lastName: user.name?.split(' ').slice(1).join(' '),
        skipPasswordRequirement: true, // User will set password on first sign-in
      })
      results.push({ email: user.email, status: 'created', clerkId: created.id })
      console.log(`Created: ${user.email} -> ${created.id}`)
    } catch (err: any) {
      if (err.status === 422) {
        results.push({ email: user.email, status: 'exists' })
      } else {
        results.push({ email: user.email, status: 'error', error: err.message })
      }
    }

    // Respect rate limits
    await new Promise((resolve) => setTimeout(resolve, 100))
  }

  return results
}

importUsers().then((results) => {
  const summary = {
    total: results.length,
    created: results.filter((r) => r.status === 'created').length,
    exists: results.filter((r) => r.status === 'exists').length,
    errors: results.filter((r) => r.status === 'error').length,
  }
  console.log('Migration summary:', summary)
  fs.writeFileSync('migration-results.json', JSON.stringify(results, null, 2))
})

Step 3: Update Database References

// scripts/update-db-references.ts
import { createClerkClient } from '@clerk/backend'

const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })

async function updateDatabaseReferences() {
  // Get all users from your database
  const dbUsers = await db.user.findMany()

  for (const dbUser of dbUsers) {
    // Find corresponding Clerk user by email
    const clerkUsers = await clerk.users.getUserList({
      emailAddress: [dbUser.email],
    })

    if (clerkUsers.totalCount > 0) {
      const clerkUser = clerkUsers.data[0]
      await db.user.update({
        where: { id: dbUser.id },
        data: {
          clerkId: clerkUser.id,
          // Keep old auth ID for rollback
          legacyAuthId: dbUser.authProviderId,
        },
      })
      console.log(`Mapped: ${dbUser.email} -> ${clerkUser.id}`)
    }
  }
}

Step 4: Replace Auth Code (NextAuth to Clerk Example)

// BEFORE: NextAuth
// import { getServerSession } from 'next-auth'
// import { authOptions } from '@/lib/auth'
// const session = await getServerSession(authOptions)
// const userId = session?.user?.id

// AFTER: Clerk
import { auth } from '@clerk/nextjs/server'
const { userId } = await auth()
// BEFORE: NextAuth client hook
// import { useSession } from 'next-auth/react'
// const { data: session } = useSession()

// AFTER: Clerk client hook
import { useUser } from '@clerk/nextjs'
const { user, isLoaded } = useUser()
// BEFORE: NextAuth middleware
// export { default } from 'next-auth/middleware'
// export const config = { matcher: ['/dashboard(.*)'] }

// AFTER: Clerk middleware
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isProtected = createRouteMatcher(['/dashboard(.*)'])
export default clerkMiddleware(async (auth, req) => {
  if (isProtected(req)) await auth.protect()
})

Step 5: Parallel Running (Optional Safety Net)

// lib/auth-bridge.ts — run both auth systems during transition
import { auth as clerkAuth } from '@clerk/nextjs/server'

export async function getAuthUser() {
  // Try Clerk first (new system)
  const { userId: clerkUserId } = await clerkAuth()
  if (clerkUserId) {
    return { provider: 'clerk', userId: clerkUserId }
  }

  // Fall back to legacy system during migration window
  // const legacySession = await getLegacySession()
  // if (legacySession) return { provider: 'legacy', userId: legacySession.userId }

  return null
}

Step 6: Cleanup After Migration

# After migration is verified (2+ weeks in production):
npm uninstall next-auth @auth0/nextjs-auth0  # Remove old auth packages
# Delete old auth files: pages/api/auth/[...nextauth].ts, lib/auth.ts
# Remove legacy database columns after confirming all users migrated

Output

  • User export from current auth provider (Auth0, NextAuth, Firebase)
  • Bulk import script with rate limiting and error handling
  • Database reference mapping (old auth IDs to Clerk IDs)
  • Code migration examples (NextAuth to Clerk)
  • Parallel running bridge for safe transition
  • Cleanup checklist for removing old auth code

Error Handling

Error Cause Solution
Duplicate email on import User already exists in Clerk Skip (status: 'exists') or merge
Invalid email format Dirty data from export Clean/validate before import
Rate limited during import Too many API calls Add 100ms delay between creates
Password can't be migrated Passwords are hashed Use skipPasswordRequirement, user sets new password
OAuth accounts Social login tokens non-transferable Users re-link OAuth accounts on first Clerk sign-in

Examples

Migration Verification Script

// scripts/verify-migration.ts
async function verifyMigration() {
  const dbUsers = await db.user.findMany({ where: { clerkId: { not: null } } })
  const unmapped = await db.user.findMany({ where: { clerkId: null } })

  console.log(`Mapped: ${dbUsers.length}, Unmapped: ${unmapped.length}`)

  if (unmapped.length > 0) {
    console.log('Unmapped users:', unmapped.map((u) => u.email))
  }
}

Resources

Next Steps

After migration, review clerk-prod-checklist for production readiness.

信息
Category 编程开发
Name clerk-migration-deep-dive
版本 v20260423
大小 6.52KB
更新时间 2026-04-27
语言