A lightweight PHP-based single sign-on authentication service designed to work seamlessly with Traefik ForwardAuth middleware.
- Simplified admin access without requiring username input
- JWT-based authentication with configurable expiration
- Multiple access lists for granular user permissions
- Cookie-based session management for persistent authentication
- Event system for custom authentication hooks
- Global logout functionality to invalidate all sessions
services:
sso:
image: robinmoser/sso:latest
environment:
HOSTNAME: sso.example.com
WEB_ALIAS_DOMAIN: sso.example.com
volumes:
- ./config:/app/config:ro
labels:
- 'traefik.enable=true'
- 'traefik.http.services.sso.loadbalancer.server.port=80'
- 'traefik.http.routers.sso.rule=Host(`sso.example.com`)'- Copy
configs/credentials.example.phptoconfig/credentials.php - Update user credentials and access lists
$credentials = [
'expire' => '14 day',
'users' => [
'admin' => '$1$password_hash...',
'user2' => '$1$password_hash...',
'user1' => '$1$password_hash...',
],
'lists' => [
// default access list for the admin user:
'private' => ['admin'],
'family' => ['admin', 'user2', 'user3'],
]
];- Configure your Traefik Serivices to use the SSO service
services:
myservice:
image: my_service_image
labels:
- 'traefik.enable=true'
- 'traefik.http.services.myservice.loadbalancer.server.port=80'
- 'traefik.http.routers.myservice.middlewares=auth'
- 'traefik.http.routers.myservice.rule=Host(`service.example.com`)'
- 'traefik.http.middlewares.auth.forwardauth.address=http://sso.example.com/'When no ?list= parameter is provided, only a password is required, as the only allowed user is admin.
This provides simplified access for admin-only services.
- 'traefik.http.middlewares.auth.forwardauth.address=http://sso.example.com/'When configuring Traefik ForwardAuth with a ?list= parameter, users must provide both username and password:
- 'traefik.http.middlewares.auth.forwardauth.address=http://sso.example.com/?list=family'This gives access to all users defined in the specified access list.
To invalidate all active JWT tokens and force all users to re-authenticate, enter logout in the password field on the SSO login page. This immediately expires all existing sessions across all services.
The SSO service includes an event system that triggers on authentication actions.
You can create custom event handlers by placing PHP files in the events/ directory.
user.login.success- Triggered after successful authenticationuser.login.failure- Triggered after failed authentication attemptsuser.logout- Triggered when global logout is performed
Create a file in events/ directory to handle authentication events:
<?php
// events/pushover.php
Event::bind('user.login.failure', function($args = array()) {
pushover("Login failed! (IP: " . $_SERVER['HTTP_X_REAL_IP'] . ")\n"
. "User: " . $args[0] . "@" . $args[1] . "\n" . $_GET['service']);
});
Event::bind('user.login.success', function($args = array()) {
pushover("Login succeeded! (IP: " . $_SERVER['HTTP_X_REAL_IP'] . ")\n"
. "User: " . $args[0] . "@" . $args[1] . "\n" . $_GET['service']);
});
function pushover($message) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.pushover.net/1/messages.json');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'token' => 'YOUR_APP_TOKEN',
'user' => 'YOUR_USER_KEY',
'message' => $message
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}All PHP files in the events/ directory are automatically loaded and executed.
- User accesses a protected service behind Traefik
- Traefik forwards auth request to SSO service
- If not authenticated, user is redirected to login page
- After successful login, JWT token is issued and user redirected back
- Subsequent requests use local cookies for authentication
Created by Robin Moser, 2020