SAML: IMPLEMENTACIÓN DE SINGLE SIGN-ON EN LARAVEL

SAML, Security Assertion Markup Language, es un estándar que se basa en XML, que permite el intercambio seguro de información de autenticación y autorización entre un Identity Provider (IdP) y un Service Provider (SP).
  • Identity Provider (IdP): Sistema que autentica al usuario como por ejemplo: Azure AD, Okta, Google Workspace, ADFS, Keycloak, OneLogin).
  • Service Provider (SP): La aplicación PHP que consume la autenticación.

Ventajas de usar SAMLSingle Sign-On

  • Mayor seguridad ya que las credenciales no viajan entre sistemas.
  • Descentralización de la gestión de usuarios.
  • Soporte para atributos como roles, departamentos, permisos, etc..
  • Ampliamente aceptado en entornos empresariales.

Implementación

La forma más limpia y moderna de implementar SAML, en Laravel es usando el siguiente paquete:

composer require "aacotroneo/laravel-saml2"

Instalación y configuración

composer require aacotroneo/laravel-saml2
Publicar la configuración
php artisan vendor:publish --provider="Aacotroneo\Saml2\Saml2ServiceProvider"

Configuración básica ("config/saml2_settings.php")

return [
'default' => 'idp1',

'idp1' => [
'idp' => [
  'entityId' => 'https://idp.ejemplo.com/saml/metadata',
  'singleSignOnService' => [
  'url' => 'https://idp.ejemplo.com/saml/sso',
  ],
  'singleLogoutService' => [
  'url' => 'https://idp.ejemplo.com/saml/slo',
  ],
  'x509cert' => 'MIID...',
],
'sp' => [
  'entityId' => 'https://miapp.com/saml/metadata',
  'assertionConsumerService' => [
  'url' => 'https://miapp.com/saml/acs',
  ],
  'singleLogoutService' => [
  'url' => 'https://miapp.com/saml/sls',
  ],
],
'security' => [
  'nameIdEncrypted' => false,
  'authnRequestsSigned' => false,
  'logoutRequestSigned' => false,
  'logoutResponseSigned' => false,
  'signMetadata' => false,
  'wantMessagesSigned' => false,
  'wantAssertionsSigned' => true,
  'wantNameId' => true,
  'wantNameIdEncrypted' => false,
],
],
];

Rutas principales

// routes/web.php
Route::get('/saml/login', 'SamlController@login');
Route::get('/saml/metadata', 'SamlController@metadata');
Route::post('/saml/acs', 'SamlController@acs');
Route::get('/saml/sls', 'SamlController@sls');

Controlador de ejemplo

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Aacotroneo\Saml2\Saml2Auth;

class SamlController extends Controller
{
protected $saml;

public function __construct(Saml2Auth $saml)
{
$this->saml = $saml;
}

public function login()
{
return $this->saml->login(config('app.url') . '/dashboard');
}

public function acs()
{
$this->saml->processResponse();

if ($this->saml->getLastError()) {
  return response()->json(['error' => $this->saml->getLastError()], 500);
}

$user = $this->saml->getSaml2User();

// Atributos recibidos del IdP
$attributes = $user->getAttributes();
$nameId = $user->getNameId();

// Crear o actualizar usuario en tu sistema
$laravelUser = \App\Models\User::updateOrCreate(
  ['email' => $nameId],
  [
  'name' => $attributes['name'][0] ?? $nameId,
  // otros campos...
  ]
);

auth()->login($laravelUser);

return redirect('/dashboard');
}
}