Cambiar tema

Inicio de sesión OAuth en Next.js: guía práctica para Google, GitHub y WeChat

Easton editorial illustration: monorepo project desk

La semana pasada, en un proyecto comunitario, el product manager me dijo: «Añade inicio de sesión con Google, debería ser rápido». Pensé que el login de terceros no sería complicado: ya había leído varios tutoriales. Resultado: toda una tarde, errores de redirect_uri, fallos al obtener tokens y la consola llena de mensajes rojos. Lo peor: seguí la documentación oficial paso a paso y no funcionaba.

El problema no era el código, sino una comprensión demasiado superficial de OAuth. Código de autorización, access token, callback: cada término por sí solo tiene sentido; juntos, confusión total.

Esta vez explico OAuth con palabras sencillas — no el RFC abstracto, sino la analogía de recoger un paquete. Entenderás por qué hacen falta code y token, para qué sirve el callback y dónde suelen fallar las configuraciones. Luego configuramos tres logins: Google (estándar internacional), GitHub (amigable para desarrolladores) y WeChat (indispensable en China, el más difícil).

WeChat es la parte más molesta: documentación poco clara, requisitos empresariales, depuración local complicada. Pero en proyectos orientados al mercado chino no se puede evitar; comparto las trampas que he encontrado, incluidos túneles y proveedores personalizados. Con una sola arquitectura puedes cubrir login local e internacional.

Cómo funciona realmente OAuth

Entender OAuth con la analogía de recoger un paquete

Cuando encontré OAuth por primera vez, authorization code y access token me volvieron loco. Luego lo entendí: es exactamente como pedirle a un amigo que recoja un paquete.

Imagina que tienes un paquete en el punto de recogida (información del usuario), pero estás en la oficina y no puedes ir. Tu amigo (tu app Next.js) se ofrece a recogerlo. El punto no entrega paquetes a cualquiera: debe confirmar que tú lo autorizaste. El flujo es así:

1. Le das a tu amigo un código de recogida — es el authorization code. Al pulsar «Iniciar sesión con Google», saltas a la página de autorización de Google; tras aceptar, Google genera un code temporal y lo devuelve a tu app por la URL.

2. Tu amigo va al punto con el código — tu backend usa el code para obtener un access token. El punto también verifica que tu amigo es de confianza: por eso registraste antes su «documento» (client_secret).

3. Tras verificar, entregan el paquete — si code y secret coinciden, entregan el paquete (datos del usuario) a tu amigo, que te lo pasa. Eso es usar access_token para obtener el perfil.

Lo clave: el code es de un solo uso; aunque alguien lo vea, sin el secret el punto no entrega nada. Por eso el code puede ir en la URL del navegador, pero el secret debe quedarse en el backend.

Los cuatro pasos clave

En términos técnicos:

Paso 1: redirigir a la página de autorización OAuth

Al pulsar «Iniciar sesión con Google», el frontend construye una URL y redirige a Google:

https://accounts.google.com/o/oauth2/v2/auth?
  client_id=tu-app-id
  &redirect_uri=http://localhost:3000/api/auth/callback/google
  &response_type=code
  &scope=email profile

Parámetros importantes:

  • client_id: identificador de tu app en Google
  • redirect_uri: adónde Google devuelve al usuario tras autorizar
  • scope: qué datos del usuario quieres

Paso 2: el usuario autoriza y recibe el code

Tras pulsar «Permitir», Google redirige a tu app con una URL como:

http://localhost:3000/api/auth/callback/google?code=4/0AfJohXl...&state=random123

Ese code es el código de recogida: vale poco (5-10 minutos) y solo se usa una vez.

Paso 3: el backend intercambia code por access_token

El backend (no el frontend) envía code + client_secret a Google:

const response = await fetch('https://oauth2.googleapis.com/token', {
  method: 'POST',
  body: JSON.stringify({
    code: 'el code recibido',
    client_id: 'your_client_id',
    client_secret: 'your_secret', // nunca al frontend
    redirect_uri: 'http://localhost:3000/api/auth/callback/google',
    grant_type: 'authorization_code'
  })
})

const { access_token } = await response.json()

El access_token es la llave real para obtener datos del usuario.

Paso 4: obtener el perfil con access_token

Con el token llamas a la API de Google:

const userInfo = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
  headers: {
    Authorization: `Bearer ${access_token}`
  }
})

const user = await userInfo.json()
// { email: "[email protected]", name: "Juan Pérez", picture: "URL del avatar" }

Con esos datos creas o actualizas el usuario en tu base de datos, generas la sesión y completas el login.

Tabla rápida de conceptos

TérminoExplicación sencillaDónde verlo
Client IDIdentificador público de tu app.env.local, parámetros de URL
Client SecretContraseña de la app, confidencialSolo backend y variables de entorno
Authorization CodeCódigo de recogida de un solo usoParámetro code en la URL de callback
Access TokenLlave real para obtener datosBackend, no al frontend
Redirect URIURL de retorno tras autorizarConfig OAuth, parámetro de autorización
ScopePermisos solicitadosParámetro scope, p. ej. email profile
StateCadena aleatoria anti-CSRFParámetros de autorización y callback

Para qué sirve State: generas una cadena aleatoria (p. ej. abc123), la guardas en sesión y la envías al servidor OAuth. En el callback compruebas que el state devuelto coincide. Si no, puede ser un ataque — rechaza. NextAuth.js lo gestiona automáticamente.

¿Por qué no devolver el token directamente?

Si al final necesitas access_token, ¿por qué no devolverlo en la URL y evitar el paso code → token?

Por seguridad.

La barra de direcciones es visible en historial, logs y extensiones. Devolver el token ahí sería exponer la llave de los datos del usuario.

Aunque intercepten el code:

  1. Solo sirve una vez
  2. Para cambiarlo hace falta client_secret, solo en el backend
  3. El servidor OAuth valida también redirect_uri

Aunque roben el code, sin secret no hay token. Este diseño se llama Authorization Code Flow: el flujo más seguro de OAuth 2.0 para apps web con backend. El Implicit Flow (token directo en URL) ya no se recomienda.

Primeros pasos con NextAuth.js

Por qué NextAuth.js

En Next.js puedes implementar OAuth a mano o con una librería. Lo intenté yo mismo, tropecé mil veces y pasé a NextAuth.js.

Motivo 1: respaldo oficial y ecosistema maduro

NextAuth.js es la opción recomendada en la documentación de Next.js, con 70K+ estrellas y mantenimiento activo. La v5 (noviembre 2024) soporta App Router y Server Components.

Motivo 2: más de 30 proveedores OAuth

Google, GitHub, Facebook, Twitter, etc. salen con pocas líneas. Para WeChat, sin proveedor integrado, hay mecanismo de Provider personalizado.

Motivo 3: lógica compleja automatizada

Sesiones, firma JWT, almacenamiento, protección CSRF — automático. Tú te centras en el negocio, p. ej. enviar email de bienvenida en el primer login.

Estructura del archivo de configuración

El núcleo es una API route. Con App Router (Next.js 13+):

app/api/auth/[...nextauth]/route.ts

[...nextauth] es catch-all: atiende /api/auth/*:

  • /api/auth/signin — página de login
  • /api/auth/callback/google — callback de Google
  • /api/auth/signout — cerrar sesión
  • /api/auth/session — sesión actual

Configuración básica:

// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import GitHubProvider from "next-auth/providers/github"

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    GitHubProvider({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
  ],
  pages: {
    signIn: '/login',
  },
  callbacks: {
    async signIn({ user, account, profile }) {
      // comprobar lista blanca, etc.
      return true // false bloquea el login
    },
    async session({ session, token }) {
      session.user.id = token.sub
      return session
    },
  },
}

const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }

La línea export { handler as GET, handler as POST } es obligatoria en App Router.

Convenciones de variables de entorno

# .env.local

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-here
# o en v5:
AUTH_SECRET=your-secret-key-here

GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# o AUTH_GOOGLE_ID / AUTH_GOOGLE_SECRET

GITHUB_ID=your-github-client-id
GITHUB_SECRET=your-github-client-secret
# o AUTH_GITHUB_ID / AUTH_GITHUB_SECRET

Puntos clave:

  1. NEXTAUTH_URL: URL completa de la app. Local: http://localhost:3000. Producción: dominio real con https.

  2. NEXTAUTH_SECRET / AUTH_SECRET: clave para firmar JWT. Genera con:

openssl rand -base64 32

No la subas a Git. Si se filtra, cámbiala de inmediato.

  1. Prefijo AUTH_: en v5, AUTH_PROVIDER_ID y AUTH_PROVIDER_SECRET se reconocen solos.

Instalación y configuración mínima

Paso 1: instalar

npm install next-auth
# o
pnpm add next-auth

Paso 2: crear la API route

Crea app/api/auth/[...nextauth]/route.ts con el código anterior.

Paso 3: crear .env.local

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=cadena generada con openssl rand -base64 32

Paso 4: botón de login en el frontend

// app/login/page.tsx
'use client'

import { signIn } from 'next-auth/react'

export default function LoginPage() {
  return (
    <div>
      <button onClick={() => signIn('google')}>
        Iniciar sesión con Google
      </button>
      <button onClick={() => signIn('github')}>
        Iniciar sesión con GitHub
      </button>
    </div>
  )
}

Paso 5: envolver la app con Provider

// app/layout.tsx
import { SessionProvider } from 'next-auth/react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <SessionProvider>
          {children}
        </SessionProvider>
      </body>
    </html>
  )
}

Con esto tienes el esqueleto. El botón fallará hasta configurar las apps OAuth de Google y GitHub.

Configuración de login con Google

Google Cloud Console

Paso 1: entrar en Google Cloud Console

Visita https://console.cloud.google.com. Si es la primera vez, crea un proyecto, p. ej. «Mi blog».

Paso 2: habilitar Google+ API

Menú «APIs & Services» → «Library», busca «Google+ API» y pulsa «Enable».

Paso 3: crear credenciales OAuth

  • «Credentials» → «Create Credentials» → «OAuth client ID»
  • Si es la primera vez, configura «OAuth consent screen» (nombre, email de soporte)
  • Application type: «Web application»

Paso 4: Redirect URIs (crítico)

En Authorized redirect URIs:

Desarrollo:

http://localhost:3000/api/auth/callback/google

Producción (tras desplegar):

https://yourdomain.com/api/auth/callback/google

Detalles:

  • Local: http; producción: https obligatorio
  • Si usas puerto 3001, inclúyelo
  • La ruta /api/auth/callback/google debe ser exacta
  • No añadas query params; Google añade ?code=xxx

Copia Client ID y Client Secret.

Paso 5: .env.local

GOOGLE_CLIENT_ID=tu-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxx

Integración en Next.js

// app/api/auth/[...nextauth]/route.ts
import GoogleProvider from "next-auth/providers/google"

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      authorization: {
        params: {
          prompt: "consent",
          access_type: "offline",
          response_type: "code"
        }
      }
    }),
  ],
}
  • prompt: "consent": muestra la pantalla de consentimiento cada vez (útil en pruebas)
  • access_type: "offline": refresh token si necesitas acceso prolongado
  • response_type: "code": Authorization Code Flow explícito

Probar el flujo

npm run dev

Visita http://localhost:3000/login, inicia sesión con Google.

Obtener sesión en servidor:

// app/page.tsx
import { getServerSession } from "next-auth"
import { authOptions } from "./api/auth/[...nextauth]/route"

export default async function Home() {
  const session = await getServerSession(authOptions)

  if (session) {
    return <div>Hola, {session.user?.name}</div>
  }

  return <div>No has iniciado sesión</div>
}

En cliente:

'use client'
import { useSession } from "next-auth/react"

export default function Profile() {
  const { data: session, status } = useSession()

  if (status === "loading") return <div>Cargando...</div>
  if (!session) return <div>No has iniciado sesión</div>

  return (
    <div>
      <img src={session.user?.image} alt="Avatar" />
      <p>{session.user?.name}</p>
      <p>{session.user?.email}</p>
    </div>
  )
}

Errores frecuentes

Error 1: redirect_uri_mismatch

Error 400: redirect_uri_mismatch
The redirect URI in the request, http://localhost:3000/api/auth/callback/google,
does not match the ones authorized for the OAuth client.

La URI en Google Console no coincide con la real.

Pasos:

  1. DevTools → Network
  2. Al pulsar login, copia el parámetro redirect_uri
  3. Pégalo tal cual en Authorized redirect URIs
  4. Revisa protocolo, puerto, barras

Error 2: Access blocked: This app’s request is invalid

Falta OAuth consent screen o usuarios de prueba.

  • Google Console → OAuth consent screen
  • User Type: External o Internal
  • Si External sin publicar, añade emails en Test users

Error 3: puerto distinto

Local en 3001 pero URI en 3000 → error. Usa siempre 3000 o añade varios URIs.

Error 4: HTTPS en producción

Google exige https en producción. Vercel y Netlify lo dan por defecto.

Configuración de login con GitHub

GitHub OAuth App

Paso 1: Developer settings

GitHub → avatar → Settings → Developer settings → OAuth Apps → New OAuth App.

Paso 2: datos de la app

  • Application name: cualquiera
  • Homepage URL: http://localhost:3000 (local) o tu dominio
  • Authorization callback URL: http://localhost:3000/api/auth/callback/github

Register application → Client ID. Generate client secret (solo se muestra una vez).

Paso 3: .env.local

GITHUB_ID=tu-client-id
GITHUB_SECRET=tu-client-secret

Integración Next.js

import GitHubProvider from "next-auth/providers/github"

export const authOptions = {
  providers: [
    GitHubProvider({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
  ],
}

GitHub vs Google

AspectoGoogleGitHub
DificultadMedia, hay que habilitar APISimple
Redirect URICoincidencia estrictaMás flexible
HTTPSObligatorio en producciónlocalhost puede ser http
ScopeHay que definirPor defecto suele bastar
Datosemail, name, picturelogin, email, avatar_url

El email de GitHub puede ser null si el usuario oculta el correo:

const userEmail = session.user?.email || 'Correo no proporcionado'

Login con WeChat (escenario China)

Tres formas de login WeChat

TipoEscenarioRequisitosExperiencia
Open Platform — webSitio independienteEmpresa, dominio registrado, HTTPSQR en PC
Autorización web de cuenta oficialH5 dentro de WeChatCuenta oficial verificadaSolo navegador WeChat
WeChat WorkSistemas internosCuenta WeChat WorkSolo empleados

Aquí cubrimos Open Platform — web app, adecuada para un sitio Next.js independiente.

WeChat Open Platform (barrera alta)

Requisitos:

  • Licencia de empresa (no vale desarrollador individual)
  • Dominio registrado (ICP)
  • Certificado HTTPS

Paso 1: registrar cuenta de desarrollador

https://open.weixin.qq.com → Register → Website App Developer → subir licencia (1-2 días hábiles).

Paso 2: crear web app

Centro de gestión → Web Apps → Create:

  • Nombre, descripción
  • Sitio oficial: tu dominio registrado
  • Dominio de callback: solo dominio, sin protocolo ni ruta, p. ej. yourdomain.com

A diferencia de Google/GitHub, WeChat pide dominio, no URL completa.

Tras aprobación (1-7 días): AppID y AppSecret.

Paso 3: variables

WECHAT_APP_ID=tu-app-id
WECHAT_APP_SECRET=tu-app-secret

Provider personalizado en NextAuth.js

// app/api/auth/[...nextauth]/route.ts

const WeChatProvider = {
  id: "wechat",
  name: "WeChat",
  type: "oauth",
  authorization: {
    url: "https://open.weixin.qq.com/connect/qrconnect",
    params: {
      appid: process.env.WECHAT_APP_ID,
      scope: "snsapi_login",
      response_type: "code",
    },
  },
  token: {
    url: "https://api.weixin.qq.com/sns/oauth2/access_token",
    async request({ params, provider }) {
      const response = await fetch(
        `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${process.env.WECHAT_APP_ID}&secret=${process.env.WECHAT_APP_SECRET}&code=${params.code}&grant_type=authorization_code`
      )
      const tokens = await response.json()
      return { tokens }
    },
  },
  userinfo: {
    url: "https://api.weixin.qq.com/sns/userinfo",
    async request({ tokens }) {
      const response = await fetch(
        `https://api.weixin.qq.com/sns/userinfo?access_token=${tokens.access_token}&openid=${tokens.openid}`
      )
      return await response.json()
    },
  },
  profile(profile) {
    return {
      id: profile.unionid || profile.openid,
      name: profile.nickname,
      email: null,
      image: profile.headimgurl,
    }
  },
}

export const authOptions = {
  providers: [
    WeChatProvider,
  ],
}

Depuración en local

WeChat exige HTTPS y dominio registrado. Opciones:

Opción 1: túnel (recomendado)

ngrok http 3000
# o en China:
cpolar http 3000

Dominio temporal, p. ej. https://abc123.ngrok.io → configúralo como dominio de callback en WeChat.

Opción 2: cuenta de prueba

Solo tú puedes probar; otros verán «cuenta no seguida».

Particularidades de WeChat

openid y unionid

  • openid: identificador en la app actual
  • unionid: mismo usuario en apps bajo la misma cuenta Open Platform

Prefiere unionid; si no hay, openid.

Sin correo: email es null; pide email aparte si lo necesitas.

Token corto: ~2 h y refrescos limitados (~10/día). Gestiona la renovación.

Resumen

Desde OAuth hasta Google, GitHub y WeChat: flujo completo de login de terceros.

Puntos clave:

  • Code + token en dos pasos = seguridad; el code puede ir en el navegador, el secret solo en backend
  • NextAuth.js gestiona sesión y CSRF; tú el negocio
  • Google es estricto con redirect URI; GitHub el más sencillo; WeChat el más exigente pero indispensable en China
  • WeChat: empresa + dominio registrado; túnel + cuenta de prueba en desarrollo

Consejos:

  • openssl rand -base64 32 para NEXTAUTH_SECRET
  • Varios puertos en Google Console
  • Email de GitHub nullable: compruébalo
  • unionid > openid entre apps

Lo más difícil no es el código, sino el modelo mental de OAuth y las diferencias por plataforma. Si esto te evita algunos obstáculos, misión cumplida.

La próxima vez que te pidan «añade login con Google», no debería costarte toda una tarde.

Flujo completo de configuración OAuth en Next.js

Desde entender OAuth hasta configurar Google, GitHub y WeChat

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: Entender OAuth (analogía del paquete)

    Idea central de OAuth: no entregas tu contraseña a una app de terceros; autorizas un pase temporal con el proveedor.

    Analogía del paquete:
    • Tú (usuario) → quien quiere acceder
    • Amigo (app Next.js) → quien recoge el paquete
    • Punto de recogida (proveedor OAuth) → Google, GitHub, WeChat
    • Tarjeta permanente (contraseña) → no se comparte
    • Pase temporal (access_token) → limitado en tiempo y permisos

    Cuatro pasos:
    1. Das el código de recogida (authorization code)
    2. Tu amigo va al punto con código + documento (code + client_secret → access_token)
    3. El punto verifica y entrega el paquete (información del usuario)
    4. Tu amigo te lo pasa (login exitoso)

    Puntos clave:
    • El code es de un solo uso, ~10 minutos
    • client_secret solo en el servidor
    • access_token limitado en duración y alcance
  2. 2

    Step 2: Configurar Google

    1. Crear cliente OAuth en Google Cloud Console:
    • https://console.cloud.google.com
    • Proyecto → APIs y servicios → Credenciales → ID de cliente OAuth
    • Tipo: aplicación web
    • URI de redirección: http://localhost:3000/api/auth/callback/google

    2. Obtener client_id y client_secret

    3. Configurar NextAuth.js:
    ```ts
    // app/api/auth/[...nextauth]/route.ts
    import NextAuth from 'next-auth'
    import GoogleProvider from 'next-auth/providers/google'

    export const authOptions = {
    providers: [
    GoogleProvider({
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    })
    ],
    }

    const handler = NextAuth(authOptions)
    export { handler as GET, handler as POST }
    ```

    4. Variables de entorno:
    ```
    GOOGLE_CLIENT_ID=tu_client_id
    GOOGLE_CLIENT_SECRET=tu_client_secret
    NEXTAUTH_URL=http://localhost:3000
    NEXTAUTH_SECRET=cadena_aleatoria
    ```

    5. En la página:
    ```tsx
    import { signIn } from 'next-auth/react'

    <button onClick={() => signIn('google')}>
    Iniciar sesión con Google
    </button>
    ```
  3. 3

    Step 3: Configurar GitHub

    1. Crear OAuth App en GitHub:
    • https://github.com/settings/developers
    • New OAuth App
    • Authorization callback URL: http://localhost:3000/api/auth/callback/github

    2. Obtener Client ID y Client Secret

    3. NextAuth.js:
    ```ts
    import GitHubProvider from 'next-auth/providers/github'

    providers: [
    GitHubProvider({
    clientId: process.env.GITHUB_CLIENT_ID!,
    clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    })
    ]
    ```

    4. Variables:
    ```
    GITHUB_CLIENT_ID=tu_client_id
    GITHUB_CLIENT_SECRET=tu_client_secret
    ```

    Misma lógica que Google, proveedor distinto.
  4. 4

    Step 4: Configurar WeChat (caso especial)

    1. Registrar app en WeChat Open Platform:
    • https://open.weixin.qq.com
    • Crear aplicación web
    • AppID y AppSecret
    • Requiere calificación empresarial

    2. Dominio de callback:
    • Formato: tudominio.com (sin http:// ni https://)
    • Dominio con registro ICP

    3. Provider personalizado:
    ```ts
    import WeChatProvider from 'next-auth/providers/wechat'

    providers: [
    WeChatProvider({
    clientId: process.env.WECHAT_CLIENT_ID!,
    clientSecret: process.env.WECHAT_CLIENT_SECRET!,
    })
    ]
    ```

    4. Depuración local con túnel:
    • ngrok o frp
    • Callback = dirección del túnel
    • En producción: dirección real

    Puntos clave:
    • Calificación empresarial
    • Túnel en local
    • unionid preferible a openid
  5. 5

    Step 5: Resolver errores comunes

    Error 1: redirect_uri_mismatch
    • Causa: URI de callback incorrecta
    • Solución: configurar redirect_uri en el proveedor
    • Declara local y producción

    Error 2: faltan variables de entorno
    • Comprueba .env.local
    • Nombres correctos
    • También en Vercel Dashboard

    Error 3: OK en local, falla en producción
    • URI de callback distinta
    • Añade https://yourdomain.com/api/auth/callback/google

    Seguridad:
    • client_secret solo en servidor
    • Parámetro state anti-CSRF
    • Verificar state en el callback

FAQ

¿Cómo funciona realmente OAuth?
Analogía de recoger un paquete:

Escenario: tienes un paquete (tus datos) en el punto de recogida, estás en la oficina. Un amigo (app Next.js) se ofrece a recogerlo.

Flujo:
1. Das un código de recogida (authorization code)
• Pulsas «Iniciar sesión con Google» → página de autorización
• Tras consentir, Google devuelve un code temporal en la URL

2. Tu amigo va al punto (code + client_secret → access_token)
• El backend intercambia code + secret en Google
• Google verifica que la app es de confianza (client_secret)

3. Tras verificar, entregan el paquete (información del usuario)
• Google confirma → perfil transmitido → login exitoso

Puntos clave:
• Code de un solo uso, ~10 minutos
• client_secret solo en servidor
• access_token limitado en duración y permisos

Ventaja: no entregas tu contraseña a la app de terceros.
¿Qué es redirect_uri_mismatch?
Causa: la URI de callback no coincide con la configuración.

El proveedor OAuth verifica la dirección de retorno; si no coincide, aparece este error.

Solución:
1. Configura redirect_uri en el panel del proveedor
2. Local: http://localhost:3000/api/auth/callback/google
3. Producción: https://tudominio.com/api/auth/callback/google
4. Declara ambos entornos

Errores frecuentes:
• Solo local, sin producción
• Barra de más o de menos
• http vs https

Comprobar:
• Ruta por defecto de NextAuth: /api/auth/callback/[provider]
• Coincidencia exacta con el panel del proveedor

La propagación puede tardar unos minutos.
¿Por qué WeChat es tan complicado?
Problemas:

1. Calificación empresarial
• Desarrolladores individuales excluidos
• Licencia y documentos
• Aprobación 1-3 días hábiles

2. Documentación
• Poco clara
• Mensajes de error vagos
• Depuración difícil

3. Depuración local
• Túnel obligatorio (ngrok, frp)
• Callback complejo
• Límites del entorno de prueba

4. Configuración
• Provider personalizado
• openid vs unionid
• Dominio de callback con registro ICP

Soluciones:
• Túnel en desarrollo
• Provider personalizado
• unionid como identificador único
• Paciencia con la aprobación

Consejo: prioriza Google o GitHub; WeChat como complemento si hace falta.
¿Cómo configurar un Provider personalizado?
WeChat requiere un Provider personalizado:

```ts
import type { OAuthConfig, OAuthUserConfig } from 'next-auth/providers'

function WeChatProvider(options: OAuthUserConfig<WeChatProfile>): OAuthConfig<WeChatProfile> {
return {
id: 'wechat',
name: 'WeChat',
type: 'oauth',
authorization: {
url: 'https://open.weixin.qq.com/connect/qrconnect',
params: {
appid: options.clientId,
redirect_uri: options.callbackUrl,
response_type: 'code',
scope: 'snsapi_login',
state: 'state',
},
},
token: {
url: 'https://api.weixin.qq.com/sns/oauth2/access_token',
},
userinfo: {
url: 'https://api.weixin.qq.com/sns/userinfo',
},
profile(profile) {
return {
id: profile.openid,
name: profile.nickname,
email: null,
image: profile.headimgurl,
}
},
...options,
}
}
```

Puntos clave:
• URLs de authorization, token y userinfo
• Función profile

WeChat es complejo — consulta la documentación oficial o un Provider existente.
¿Qué diferencia hay entre unionid y openid?
openid:
• Identificador único en la app actual
• Distinto por aplicación
• Adecuado para una sola app

unionid:
• Identificador único bajo la misma cuenta Open Platform
• Igual para el mismo usuario en varias apps
• Adecuado para varias apps

Consejo:
• Una app → openid
• Varias apps → unionid

Ejemplo:
```ts
const response = await fetch(
`https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openid}`
)
const data = await response.json()
const unionid = data.unionid
```

unionid es mejor identificador estable; requiere autorización y Open Platform.
¿Cómo depurar WeChat en local?
Problema: WeChat exige dominio de callback; localhost no sirve.

Solución: túnel

1. ngrok:
```bash
ngrok http 3000
```

2. Dirección pública:
```
https://xxxxx.ngrok.io
```

3. Configuración:
• Open Platform: https://xxxxx.ngrok.io/api/auth/callback/wechat
• .env.local: NEXTAUTH_URL=https://xxxxx.ngrok.io

4. Prueba:
• Abre https://xxxxx.ngrok.io
• Login con WeChat

Notas:
• ngrok gratuito cambia la URL al reiniciar
• No uses ngrok en producción
• Tras pruebas: dirección de producción

Alternativa: frp propio, URL más estable.

12 min de lectura · Publicado el: 19 dic 2025 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog