Permitir que un docente lance CEO Sim desde cualquier LMS sin sync manual de users.
restriccion: El handshake OIDC requiere nonce + state guardados a traves de dos hops HTTP. El id_token JWT debe validarse contra el endpoint JWKS del LMS (distinto por plataforma). Los claims de rol difieren: Instructor en Canvas no es Instructor en Moodle con el mismo shape JSON. El deep linking permite al docente apuntar a una ronda especifica.
LtiSsoService maneja la iniciacion OIDC (nonce en Redis), el endpoint de launch valida el JWT id_token, y LtiUserMappingRepository provisiona o reusa un User idempotentemente. resolveRoleId() mapea URIs de rol del LMS a role_ids internos. LtiDeploymentRepository persiste contexto + URLs NRPS/AGS por deployment. Un middleware ValidateLtiLaunch intercepta cada launch.
// En el launch (id_token ya validado):
$platform = LtiPlatformRepository::findByIssuerAndClientId($issuer, $clientId);
$mapping = LtiUserMappingRepository::findByPlatformAndLtiUserId(
$platform->id, $claims['sub']
);
if (! $mapping) {
$user = User::firstOrCreate(['email' => $claims['email']], [
'name' => $claims['name'],
'password' => Hash::make(Str::random(16)),
]);
$mapping = LtiUserMappingRepository::create([
'lti_platform_id' => $platform->id,
'lti_user_id' => $claims['sub'],
'user_id' => $user->id,
'lti_roles' => $claims['roles'],
]);
}
$user->update(['role_id' => $this->resolveRoleId($claims['roles'])]);
return new JwtResponse($user, $this->getToolToken($user));