Servicio de autenticación JWT para el ecosistema LifeSync-Games (LSG), basado en FastAPI, MySQL y JWT.
Versión: 1.2.0.3 | Swagger: https://lsg.diinf.usach.cl/lsg-auth/docs
Provee:
- Gestión de jugadores con contraseña hasheada con bcrypt.
- Inicio de sesión con token JWT (claim
rolescomo lista). - Sistema de roles múltiples por jugador (
player_roles). - Endpoint
GET /token/remainingpara consultar segundos restantes del token. - Endpoints
PATCHyGET /admin/players/{id}/rolespara gestionar roles (solo admin). - Healthcheck de la API y de la conexión a la base de datos.
lsg-auth/
│
├── app/
│ ├── tools/
│ │ └── generate_jwt_secret.py # genera AUTH_JWT_SECRET (ignorado por git)
│ ├── __init__.py
│ ├── auth.py # bcrypt + JWT + get_token_remaining()
│ ├── cli_create_user.py # CLI para bootstrap de usuarios (primer admin)
│ ├── db.py # conexión SQLAlchemy a MySQL
│ ├── main.py # FastAPI: endpoints + require_roles()
│ ├── models.py # ORM: Player, PlayerRole
│ └── schemas.py # Pydantic v2: PlayerCreate/Out, Token, RoleAssign
│
├── .env.example
├── .gitignore
├── docker-compose.yml
├── Dockerfile
├── README.md
└── requirements.txt
| Método | Ruta | Rol requerido | Descripción |
|---|---|---|---|
GET |
/health |
- | Healthcheck + SELECT 1 en BD |
POST |
/login |
- | Login OAuth2 → JWT (120 min) |
GET |
/whoami |
cualquiera | Perfil + roles del token activo |
GET |
/token/remaining |
cualquiera | Segundos restantes del JWT activo |
POST |
/token/refresh |
cualquiera | Renovar token sin re-login (roles actualizados) |
POST |
/players |
admin |
Crear jugador con rol inicial |
POST |
/admin/players/batch-temp |
admin |
Crear lote de cuentas temporales (1-50) |
PATCH |
/admin/players/{id}/roles |
admin |
Asignar (grant) o revocar (revoke) rol |
GET |
/admin/players/{id}/roles |
admin |
Historial de roles (activos + revocados) |
PATCH |
/admin/players/{id}/password |
admin |
Cambiar contraseña de cualquier jugador |
Nota: La creación del primer usuario admin debe hacerse desde el CLI del contenedor (ver sección Bootstrap). El endpoint
POST /playersrequiere un token admin activo.
Un jugador puede tener N roles activos simultáneamente (tabla player_roles).
El JWT emite el claim "roles": ["player", "researcher"] (lista, no string singular).
| Rol | Descripción |
|---|---|
player |
Visualización y uso de servicios propios únicamente |
teacher |
Lectura de todos los jugadores y analíticas |
researcher |
Todo lo de teacher + edición, exportación e IC² ajeno |
developer |
Integración de mods: crear juegos, mecánicas y vincularlas |
admin |
Acceso completo incluyendo configuración del sistema |
-- Tabla players (v1.1: sin columna 'role')
CREATE TABLE players (
id_players INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
password_hash CHAR(95) DEFAULT NULL,
email VARCHAR(128) NOT NULL UNIQUE,
age INT,
external_type VARCHAR(16),
external_id VARCHAR(128),
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
temp_expires_at TIMESTAMP NULL DEFAULT NULL -- NULL = permanente; fecha = cuenta temporal
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Tabla player_roles (multi-rol, historial con revocación)
CREATE TABLE player_roles (
id_player_role INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
id_players INT NOT NULL,
role VARCHAR(32) NOT NULL,
assigned_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
assigned_by INT NULL, -- id_players del admin asignador (NULL = CLI bootstrap)
revoked_at TIMESTAMP NULL, -- NULL = rol activo; timestamp = rol revocado
CONSTRAINT chk_pr_role CHECK (role IN ('player','researcher','admin','teacher','developer')),
FOREIGN KEY (id_players) REFERENCES players(id_players)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;# ── Base de datos ──────────────────────────────────────────────────────────────
# DB_HOST y DB_PORT son inyectados por docker-compose (lsg_db:3306)
DB_NAME=db_lsg
DB_USER=lsg_user
DB_PASSWORD=PASSWORD
# ── JWT ────────────────────────────────────────────────────────────────────────
# Debe coincidir exactamente con lsg-core-api-prod
AUTH_JWT_SECRET=VALOR_LARGO_Y_ALEATORIO_GENERADO
AUTH_JWT_ALGORITHM=HS256
AUTH_JWT_ISSUER=lsg-auth
AUTH_JWT_AUDIENCE=lsg-core-api
JWT_EXPIRE_MINUTES=120
# ── Guards ─────────────────────────────────────────────────────────────────────
# false en producción SIEMPRE
AUTH_DISABLED=falsepython -m app.tools.generate_jwt_secret
# Copiar el valor generado y pegarlo en .env de AMBOS repos (auth y core-api)# Crear solo una vez por VM (si no existe)
docker network create lsg_sharedcp .env.example .env
# Editar .env con los valores reales
docker compose up -d --build
docker ps
docker logs -n 100 lsg_authLa creación de usuarios desde el endpoint POST /players requiere un token admin.
Para el bootstrap inicial usar el CLI directamente en el contenedor:
docker compose exec app \
python -m app.cli_create_user \
--name "Admin LSG" \
--email admin@lsg.cl \
--password "contraseña_segura" \
--role admin# Health
curl https://lsg.diinf.usach.cl/lsg-auth/health
# Swagger
https://lsg.diinf.usach.cl/lsg-auth/docscd lsg-auth
# Crear y activar entorno virtual
python -m venv .venv
.\.venv\Scripts\Activate.ps1 # Windows PowerShell
source .venv/bin/activate # Linux/macOS
# Instalar dependencias
pip install --upgrade pip
pip install -r requirements.txt
# Configurar variables de entorno
cp .env.example .env
# Agregar al .env para dev local:
# DB_HOST=127.0.0.1
# DB_PORT=3306
# Levantar API
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# Swagger: http://localhost:8000/docs1. Login:
POST /login {username: email, password: "..."}
→ {"access_token": "eyJ...", "token_type": "bearer"}
Payload del JWT:
{
"sub": "26",
"player_id": 26,
"email": "joaquin.macias@usach.cl",
"roles": ["admin", "researcher"], ← lista (v1.1+)
"type": "user",
"exp": ...,
"iss": "lsg-auth",
"aud": "lsg-core-api"
}
2. Consultar tiempo restante:
GET /token/remaining Authorization: Bearer <token>
→ {"expires_in_seconds": 6843, "expires_at": "2026-05-07T12:23:41Z"}
3. Usar token en lsg-core-api-prod:
Authorization: Bearer <token> (válido 120 minutos)
4. Gestión de roles (admin):
# Asignar rol
PATCH /admin/players/46/roles
Body: {"role": "researcher", "action": "grant"}
→ {"status": "ok", "player_id": 46, "role": "researcher", "action": "grant"}
# Ver historial de roles (activos e históricos)
GET /admin/players/46/roles
GET /admin/players/46/roles?include_revoked=false ← solo activos
# Revocar rol
PATCH /admin/players/46/roles
Body: {"role": "researcher", "action": "revoke"}
POST /token/refresh- Ahora entrega información sobre el token (expires_in_seconds, issued_at, expires_at).
POST /login- Ahora entrega información sobre el token (expires_in_seconds).GET /whoami- Ahora entrega información adicional sobre el token (expires_in_seconds, issued_at, expires_at).
- Roles expandidos a "player" y "developer" para que puedan usar la API.
Nuevos endpoints:
POST /token/refresh- Renueva el JWT sin re-login; actualiza roles desde la BD.POST /admin/players/batch-temp- Genera lote de cuentas temporales (1-50) con email{prefix}_{6chars}@lsg.temp, contraseña aleatoria de 6 chars y fecha de expiración configurable (1-90 días). Las contraseñas solo son visibles en la respuesta de creación.PATCH /admin/players/{id}/password- Cambio de contraseña de cualquier jugador por el admin. Hasheo bcrypt automático.
Bugfixes y mejoras:
GET /token/remaining- Corregido: retornaba-1(placeholder). Ahora decodifica el JWT real del header y calculaexp - now()correctamente.models.py- Refactorización completa:- Eliminada columna
roleobsoleta dePlayer(la BD ya la había eliminado en PATCH-01). - Agregada columna
temp_expires_atenPlayerpara cuentas temporales. - Nuevo modelo
PlayerRolecon relación_roleshaciaPlayer. - Nueva propiedad
Player.roles(lista de roles activos,revoked_at IS NULL). - Nueva propiedad
Player.is_temp_expiredpara validar expiración enPOST /login. password_hashcorregido anullable=True(la BD permite auth externa vía CHECK).
- Eliminada columna
POST /login- Agrega verificación: si la cuenta temporal expiró (temp_expires_at < NOW()), retorna 401 con"code": "TEMP_ACCOUNT_EXPIRED".schemas.py- Nuevos schemas:BatchTempPlayersRequest,TempPlayerOut,RoleAssignRequest.- Rol
developerincorporado al CHECK deplayer_roles(PATCH-09). - BD PATCH-09: columna
temp_expires_atenplayers, vistasv_temp_players_activeyv_temp_players_expired.
- Bugfix: agregada clase
RoleAssignRequestaschemas.py(faltaba; causabaAttributeErroren startup de uvicorn e impedía levantar el servicio). - Pydantic v2: reemplazado
orm_mode = Truepormodel_config = {"from_attributes": True}enPlayerOut. - Pydantic v2: reemplazado
min_anystr_lengthporstr_min_lengthenPasswordChangeRequest. - El campo
actionenRoleAssignRequestahora usaLiteral["grant", "revoke"]; la validación pasa a Pydantic (422 automático) en lugar delHTTPExceptionmanual previo.
- Sistema de roles multi-rol (
player_roles): un jugador puede tener N roles activos. - JWT emite
"roles": [...](lista) en lugar de"role": "..."(string). Compatibilidad backward:security.pydelsg-core-api-prodacepta ambos formatos durante la transición. JWT_EXPIRE_MINUTESajustado a 120 minutos para sesiones de investigación extendidas.- Nuevo endpoint
GET /token/remaining- tiempo restante sin consultar la BD. - Nuevo endpoint
PATCH /admin/players/{id}/roles- asignar/revocar roles (grant/revoke). - Nuevo endpoint
GET /admin/players/{id}/roles- historial completo de roles coninclude_revoked. - Endpoint
POST /playersrestringido a roladmin. Bootstrap via CLI. models.py: eliminada columnaroledePlayer; nuevo modeloPlayerRole.schemas.py: migración a Pydantic v2 (ConfigDict,field_validator).cli_create_user.py: inserta enplayer_rolesen lugar deplayers.role.
- [1] González-Ibáñez, R., Macías-Cáceres, J., Villalta-Paucar, M. (2025). LifeSync-Games: A Technical Note on a Novel Framework for Video Game Development. 2025 44th International Conference of the Chilean Computer Science Society (SCCC), Valparaiso, Chile, pp. 1-4, doi: 10.1109/SCCC67219.2025.11420722.
- [2] González-Ibáñez R., Macías-Cáceres J., Villalta-Paucar M., (2025). LifeSync-Games: Toward a Video Game Paradigm for Promoting Responsible Gaming and Human Development. arXiv preprint: 2510.19691 [cs.HC].
- [3] Macías-Cáceres J., Gutiérrez-Vela F., Paderewski-Rodriguez P., González-Ibáñez, R., (2026). LifeSync-Games: Signal-Driven Pervasive Game Design: The LifeSync-Games Framework as a Player Experience Integration Layer. arXiv preprint: 2609.03169 [cs.HC].