Login con Google en la App Móvil — Finnova
Finnova ofrece "Continuar con Google" como método de autenticación en la app móvil (React Native / Expo), además del login con email y contraseña. Esta página define cómo se integra el flujo de Google Sign-In con el esquema de sesiones de doble token de Finnova (ver Control de Sesiones).
1. Principio clave
Google Sign-In solo se usa para autenticar la identidad del usuario, no para emitir las sesiones de Finnova. El flujo es:
- La app obtiene un ID Token de Google (JWT firmado por Google) usando el SDK nativo.
- La app envía ese ID Token al backend de Finnova.
- El backend verifica el token contra Google, identifica o crea al usuario, y emite sus propios access + refresh tokens.
Las sesiones siguen siendo de Finnova (mismo esquema JWT de 15 min + refresh opaco de 30 días). Google solo prueba "este usuario es quien dice ser".
2. Configuración previa en Google Cloud
En Google Cloud Console → APIs & Services → Credentials, crear OAuth 2.0 Client IDs (uno por plataforma):
| Client ID | Para qué |
|---|---|
| iOS | App nativa en iPhone/iPad (requiere el Bundle ID) |
| Android | App nativa en Android (requiere package name + SHA-1 de la firma) |
| Web | El backend lo usa como audience para verificar los ID tokens |
El Web Client ID es el que el backend valida como
audience. Los client IDs de iOS/Android se configuran en el SDK del cliente.
3. Librería recomendada
| Stack | Librería |
|---|---|
| Expo (managed) | @react-native-google-signin/google-signin con development build, o expo-auth-session como alternativa sin módulo nativo |
| React Native (bare) | @react-native-google-signin/google-signin |
expo-auth-sessionfunciona en Expo Go pero usa un flujo de navegador.@react-native-google-signinofrece la UI nativa de Google (mejor UX) pero requiere development/EAS build. Para Finnova se recomienda@react-native-google-signin/google-signinpor la experiencia nativa.
4. Flujo de autenticación
5. Verificación del ID Token en el backend
El backend nunca confía en el token sin verificarlo. Usar la librería oficial google-auth-library:
import { OAuth2Client } from 'google-auth-library';
const client = new OAuth2Client();
async function verifyGoogleIdToken(idToken: string) {
const ticket = await client.verifyIdToken({
idToken,
audience: process.env.GOOGLE_WEB_CLIENT_ID, // Web Client ID
});
const payload = ticket.getPayload();
if (!payload) throw new Error('Token inválido');
// Validaciones adicionales
if (!payload.email_verified) {
throw new Error('Email de Google no verificado');
}
return {
googleSub: payload.sub, // ID estable y único del usuario en Google
email: payload.email!,
name: payload.name,
picture: payload.picture,
};
}
La verificación comprueba automáticamente: la firma (con las claves públicas de Google), el audience (que el token sea para nuestra app), el issuer (accounts.google.com) y la expiración.
Identificar al usuario por
sub, no por email. Elsubes el identificador único e inmutable de Google; el email podría cambiar o reasignarse. El email se usa solo para vincular cuentas existentes.
6. Vinculación de cuentas (account linking)
Un usuario podría registrarse primero con email/contraseña y luego usar "Continuar con Google" (o viceversa) con el mismo correo. Estrategia:
| Caso | Acción |
|---|---|
google_sub ya existe | Login directo a esa cuenta |
google_sub nuevo, pero el email ya existe en una cuenta | Vincular: añadir google_sub a la cuenta existente |
google_sub y email ambos nuevos | Crear cuenta nueva (sin contraseña) |
Vincular por email solo es seguro porque Google ya verificó
email_verified: true. Si en el futuro se añaden proveedores que no verifican el email, no se debe vincular automáticamente.
Cambios al modelo de datos
ALTER TABLE users
ADD COLUMN google_sub TEXT UNIQUE, -- NULL si nunca usó Google
ALTER COLUMN password_hash DROP NOT NULL; -- cuentas solo-Google no tienen contraseña
CREATE INDEX idx_users_google_sub ON users(google_sub);
7. A partir de aquí: sesiones de Finnova
Una vez verificado el usuario, el flujo es idéntico al login con email/contraseña descrito en Control de Sesiones:
- Se crea una fila en
sessionscon la info del dispositivo. - Se emite el par access token (JWT 15 min) + refresh token (opaco 30 días).
- El refresh token se guarda en
SecureStore; el access token en memoria. - Aplican los mismos límites de sesiones por plan, revocación y detección de anomalías.
El
POST /auth/googlereemplaza alPOST /auth/loginsolo en la etapa de verificación de identidad. Todo lo demás (refresh, logout, multidispositivo) no cambia.
8. Consideraciones de seguridad
- Verificar siempre el token en el backend — nunca confiar en datos de usuario que llegan del cliente sin validar el ID Token con Google.
- Validar el
audiencepara evitar que un ID Token emitido para otra app sea aceptado. - Exigir
email_verifiedantes de vincular por email. - No almacenar el ID Token de Google — se usa una sola vez para autenticar y se descarta; lo que persiste son los tokens de Finnova.
- Logout: cerrar sesión en Finnova revoca la sesión propia; opcionalmente llamar
GoogleSignin.signOut()en el cliente para limpiar el estado local de Google.
9. Variables de entorno
GOOGLE_WEB_CLIENT_ID=xxxxxxxx.apps.googleusercontent.com
GOOGLE_IOS_CLIENT_ID=xxxxxxxx.apps.googleusercontent.com
GOOGLE_ANDROID_CLIENT_ID=xxxxxxxx.apps.googleusercontent.com
Solo los client IDs son públicos; Google Sign-In en móvil no requiere un client secret en el cliente.