se añadio jwt en el backend
Some checks failed
Deploy Spring Boot App / build-and-deploy (push) Has been cancelled
Some checks failed
Deploy Spring Boot App / build-and-deploy (push) Has been cancelled
This commit is contained in:
167
FRONTEND_JWT_MIGRATION.txt
Normal file
167
FRONTEND_JWT_MIGRATION.txt
Normal file
@@ -0,0 +1,167 @@
|
||||
================================================================================
|
||||
MIGRACIÓN REACT NATIVE: AUTENTICACIÓN CON JWT (backend Fercogan)
|
||||
================================================================================
|
||||
Documento para adaptar el frontend (React Native) a los nuevos endpoints y al
|
||||
uso del token Bearer. Base URL de ejemplo: https://testapp.digitaltelecom.net
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
1. RESUMEN DEL CAMBIO
|
||||
--------------------------------------------------------------------------------
|
||||
- Antes: el "login" usaba GET /api/usuarios/confirmado/{email} sin contraseña y
|
||||
guardaba solo email en AsyncStorage (inseguro).
|
||||
- Ahora: el login real es POST /api/usuarios/login con email (username) y
|
||||
contraseña. El servidor devuelve un JWT que debe enviarse en las peticiones
|
||||
protegidas: header Authorization: Bearer <token>.
|
||||
- La mayoría de rutas /api/* (remates, lotes, pujas, etc.) requieren JWT salvo
|
||||
las excepciones listadas abajo.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
2. ENDPOINTS PÚBLICOS (sin Authorization)
|
||||
--------------------------------------------------------------------------------
|
||||
POST /api/usuarios/login → login (body JSON)
|
||||
POST /api/usuarios/registrar → registro
|
||||
GET /api/usuarios/confirmado/{username} → solo verificación registro/aprobado
|
||||
(útil en flujo signup; no sustituye login)
|
||||
GET /auth/verificar/{username} → mismo concepto que confirmado (signup)
|
||||
GET /auth/existe/{username} → según implementación actual
|
||||
OPTIONS /** → CORS preflight
|
||||
/ws/** , /contador/** → según necesidad (actualmente públicos)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
3. LOGIN (SIGN IN) — REEMPLAZA confirmado + AsyncStorage sin token
|
||||
--------------------------------------------------------------------------------
|
||||
Método: POST
|
||||
URL: {BASE_URL}/api/usuarios/login
|
||||
Headers:
|
||||
Content-Type: application/json
|
||||
|
||||
Body (JSON):
|
||||
{
|
||||
"username": "<email del usuario>",
|
||||
"password": "<contraseña en texto plano; va por HTTPS>"
|
||||
}
|
||||
|
||||
Respuesta 200 (JSON) — guardar estos valores en AsyncStorage:
|
||||
{
|
||||
"token": "<JWT>",
|
||||
"tokenType": "Bearer",
|
||||
"expiresInMs": 86400000,
|
||||
"username": "...",
|
||||
"userId": 1,
|
||||
"rolId": 1,
|
||||
"authenticated": true,
|
||||
"confirmed": true
|
||||
}
|
||||
|
||||
Errores:
|
||||
- 401 + { "error": "Credenciales inválidas" } → usuario inexistente o contraseña incorrecta
|
||||
- 403 + { "error": "Usuario pendiente de aprobación" } → existe pero aún no aprobado
|
||||
|
||||
Acciones en el cliente tras 200:
|
||||
1) AsyncStorage.setItem("authToken", data.token);
|
||||
2) AsyncStorage.setItem("usuario", data.username);
|
||||
3) AsyncStorage.setItem("rol", String(data.rolId)); // ya no hace falta GET /rol/{email}
|
||||
4) AsyncStorage.setItem("isLoggedIn", "true");
|
||||
5) Opcional: guardar userId para depuración.
|
||||
|
||||
NO guardar la contraseña en AsyncStorage.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
4. SESIÓN ACTUAL / USUARIO LOGUEADO
|
||||
--------------------------------------------------------------------------------
|
||||
GET {BASE_URL}/api/usuarios/me
|
||||
Headers:
|
||||
Authorization: Bearer <authToken>
|
||||
Accept: application/json
|
||||
|
||||
Respuesta 200 ejemplo:
|
||||
{
|
||||
"username": "...",
|
||||
"userId": 1,
|
||||
"rolId": 1,
|
||||
"confirmed": true
|
||||
}
|
||||
|
||||
Usar al abrir la app para validar que el token sigue siendo válido; si 401,
|
||||
limpiar AsyncStorage y volver a pantalla de login.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
5. PETICIONES PROTEGIDAS (remates, pujas, lotes, cabañas, etc.)
|
||||
--------------------------------------------------------------------------------
|
||||
En TODAS las llamadas fetch/axios a /api/... (excepto login, registrar,
|
||||
confirmado y auth/verificar según corresponda), añadir:
|
||||
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer " + (await AsyncStorage.getItem("authToken"))
|
||||
}
|
||||
|
||||
Si el servidor responde 401, el token expiró o es inválido: cerrar sesión en
|
||||
cliente (borrar authToken, isLoggedIn) y redirigir a login.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
6. SIGN UP (REGISTRO) — CAMBIOS MÍNIMOS
|
||||
--------------------------------------------------------------------------------
|
||||
- GET /auth/verificar/{email} puede seguir usándose sin token para decidir si
|
||||
el usuario ya existe o está aprobado (flujo actual).
|
||||
- POST /api/usuarios/registrar sigue siendo público; body igual que antes
|
||||
(username=email, password, celular, ci, nombre, rol).
|
||||
|
||||
Tras registro exitoso, el usuario sigue pendiente de aprobación: NO recibirá
|
||||
JWT válido para app hasta que un admin apruebe; el login con POST /login
|
||||
devolverá 403 mientras tanto.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
7. EJEMPLO handleSignIn (lógica sugerida, pseudocódigo)
|
||||
--------------------------------------------------------------------------------
|
||||
async function handleSignIn() {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
if (!validateEmail(email)) { setError("Email inválido"); setLoading(false); return; }
|
||||
|
||||
const res = await fetch(BASE + "/api/usuarios/login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ username: email, password: password }),
|
||||
});
|
||||
|
||||
const data = await res.json().catch(() => ({}));
|
||||
|
||||
if (res.status === 401) {
|
||||
setError(data.error || "Credenciales inválidas");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (res.status === 403) {
|
||||
setError(data.error || "Cuenta pendiente de aprobación");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (!res.ok) {
|
||||
setError("Error de servidor");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await AsyncStorage.setItem("authToken", data.token);
|
||||
await AsyncStorage.setItem("usuario", data.username);
|
||||
await AsyncStorage.setItem("rol", String(data.rolId));
|
||||
await AsyncStorage.setItem("isLoggedIn", "true");
|
||||
navigation.replace("RematesList");
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
Eliminar: GET confirmado/{email} como sustituto del login, y la llamada
|
||||
separada a GET /api/usuarios/rol/{email} tras login (los datos vienen en la
|
||||
respuesta del login).
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
8. VARIABLES DE ENTORNO BACKEND (operaciones)
|
||||
--------------------------------------------------------------------------------
|
||||
En el servidor, definir JWT_SECRET (cadena larga y aleatoria, mínimo 32 bytes
|
||||
en UTF-8) y opcionalmente JWT_EXPIRATION_MS. El valor por defecto en
|
||||
application.properties es solo para desarrollo.
|
||||
|
||||
================================================================================
|
||||
FIN DEL DOCUMENTO
|
||||
================================================================================
|
||||
Reference in New Issue
Block a user