Identity Provider institucional para iedagropivijay.edu.co basado en:
- Laravel 12
- Laravel Passport (OAuth2 Authorization Server)
- OpenID Connect sobre Passport (
jeremy379/laravel-openid-connect) - Laravel Socialite (Google obligatorio)
- Filament 5 (panel admin)
- MySQL
auth centraliza autenticación (Google + OIDC/OAuth2) para:
https://gestionplanes.testhttps://teachingassistance.testhttp://localhost:8000(silo local)
Los roles y permisos siguen siendo locales en cada app cliente.
- PHP 8.2+
- Composer 2+
- MySQL
- Node.js (opcional para assets de frontend)
- Instalar dependencias:
composer install- Crear entorno:
cp .env.example .env
php artisan key:generate- Configurar
.env:
APP_URL(local o productivo)DB_*GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETGOOGLE_REDIRECT_URIINSTITUTION_EMAIL_DOMAINSUPERADMIN_EMAILSISSUERTOKEN_TTL_MINUTESREFRESH_TOKEN_TTL_DAYSCORS_ALLOWED_ORIGINS
- Generar llaves de Passport:
php artisan passport:keys --force- Migrar y seed:
php artisan migrate --seed- Limpiar cachés:
php artisan optimize:clear- Levantar servidor:
php artisan serve- Opcional frontend:
npm install
npm run devAPP_URL=http://localhost:8000
ISSUER=http://localhost:8000
OIDC_FORCE_HTTPS=false
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=http://localhost:8000/auth/google/callback
INSTITUTION_EMAIL_DOMAIN=iedagropivijay.edu.co
SUPERADMIN_EMAILS=admin@iedagropivijay.edu.co
TOKEN_TTL_MINUTES=30
REFRESH_TOKEN_TTL_DAYS=14
CORS_ALLOWED_ORIGINS=https://gestionplanes.test,https://teachingassistance.test,http://localhost:8000
SSO_ALLOWED_REDIRECT_HOSTS=gestionplanes.test,teachingassistance.test,localhost,127.0.0.1
SSO_INSECURE_REDIRECT_HOSTS=localhost,127.0.0.1
SSO_POST_LOGOUT_REDIRECT_HOSTS=gestionplanes.test,teachingassistance.test,localhost,127.0.0.1
SSO_FRONTCHANNEL_LOGOUT_CLIENTS=silo|http://localhost:8000/sso/frontchannel-logout
SSO_FRONTCHANNEL_LOGOUT_SECRETS=silo|change-me-local-secret
SSO_FRONTCHANNEL_LOGOUT_TTL_SECONDS=120auth puede cerrar sesión local de clientes (por ejemplo silo) durante el logout.
Variables:
SSO_FRONTCHANNEL_LOGOUT_CLIENTS(formato CSV:cliente|url_logout)SSO_FRONTCHANNEL_LOGOUT_SECRETS(formato CSV:cliente|secret)SSO_FRONTCHANNEL_LOGOUT_TTL_SECONDS(ventana de validez dets)
Ejemplo local:
SSO_FRONTCHANNEL_LOGOUT_CLIENTS=silo|http://localhost:8000/sso/frontchannel-logoutSSO_FRONTCHANNEL_LOGOUT_SECRETS=silo|<mismo-secret-que-en-silo>
Endpoints:
GET /loginGET /auth/google/redirectGET /auth/google/callbackPOST /logout
Reglas:
- No existe login con contraseña.
- Solo correos de dominios permitidos (
INSTITUTION_EMAIL_DOMAIN). - Usuarios inactivos (
is_active = false) no pueden continuar.
GET /oauth/authorizePOST /oauth/tokenPOST /oauth/token/refresh
- Discovery:
GET /.well-known/openid-configuration - JWKS:
GET /oauth/jwks - UserInfo:
GET /oauth/userinfo(requiereBearer access_token+ scopeopenid)
curl -G 'https://auth.iedagropivijay.edu.co/oauth/authorize' \
--data-urlencode 'client_id=YOUR_CLIENT_ID' \
--data-urlencode 'redirect_uri=https://planes.iedagropivijay.edu.co/auth/callback' \
--data-urlencode 'response_type=code' \
--data-urlencode 'scope=openid email profile' \
--data-urlencode 'state=STATE123' \
--data-urlencode 'code_challenge=BASE64URL_SHA256_VERIFIER' \
--data-urlencode 'code_challenge_method=S256' \
--data-urlencode 'nonce=NONCE123'curl -X POST 'https://auth.iedagropivijay.edu.co/oauth/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=authorization_code' \
-d 'client_id=YOUR_CLIENT_ID' \
-d 'client_secret=YOUR_CLIENT_SECRET' \
-d 'redirect_uri=https://planes.iedagropivijay.edu.co/auth/callback' \
-d 'code=AUTH_CODE' \
-d 'code_verifier=ORIGINAL_CODE_VERIFIER'Si el scope incluyó openid, la respuesta contiene id_token.
curl 'https://auth.iedagropivijay.edu.co/oauth/userinfo' \
-H 'Authorization: Bearer ACCESS_TOKEN'Ruta: https://auth.iedagropivijay.edu.co/admin
- Usuarios:
- Ver
email,is_active,last_login_at,google_id - Activar/desactivar usuarios
- Ver
- Clientes OAuth:
- Crear/editar nombre
- Definir redirect URIs exactas (sin wildcard, host permitido)
- HTTPS obligatorio, excepto
http://localhostyhttp://127.0.0.1 - Definir scopes (
openid,email,profile, ...) - Revocar/activar cliente
- Regenerar
client_secret
Acceso al panel:
- Solo correos listados en
SUPERADMIN_EMAILS - Usuario debe estar activo
SuperAdminsSeeder: crea/actualiza superadmins desdeSUPERADMIN_EMAILSOAuthClientsSeeder: crea clientes base:planes→https://gestionplanes.test/sso/callbackasistencia→https://teachingassistance.test/sso/callbacksilo→http://localhost:8000/sso/callback
Ajusta esas redirect URIs en panel o seeder según tu callback final.
php artisan db:seed --class=OAuthClientsSeederphp artisan tinker --execute="print_r(App\Models\OAuthClient::query()->get(['id','name','redirect_uris'])->toArray());"client_secret en texto plano solo se muestra al crear/regenerar el cliente.
- Opción recomendada: panel Filament
admin/oauth-clients→ acciónRegenerar secrety copiar el valor. - Opción CLI (rota secreto y lo imprime una sola vez):
php artisan tinker --execute='$c=App\Models\OAuthClient::where("name","planes")->firstOrFail(); $c->secret=\Illuminate\Support\Str::random(40); $c->save(); echo $c->plainSecret.PHP_EOL;'Cliente recomendado:
- Authorization Code + PKCE
- Scopes:
openid email profile
En callback del cliente:
- Intercambiar
codepor tokens en/oauth/token - Validar
id_token(iss,aud,exp,nonce) - Consumir
/oauth/userinfosi se requiere - Crear/actualizar usuario local y asignar rol local
Tabla: audit_logins
Eventos auditados:
login_googleauthorizetoken_issuedlogout
Estados:
successfailed
Incluye user_id, client_id, IP, user-agent y metadatos JSON.
php artisan testIncluye pruebas para:
- Discovery/JWKS
- UserInfo + scope
openid - Callback Google (éxito y dominio no permitido)
- Auditoría de emisión/fallo de token