finmetrik / console-sso-sdk
PHP SDK for Console SSO Hub integration - Single Sign-On client library
Installs: 522
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/finmetrik/console-sso-sdk
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
- psr/log: ^3.0
Requires (Dev)
- mockery/mockery: ^1.6
- phpunit/phpunit: ^10.0
README
PHP SDK for integrating with the Console SSO Hub. Enables Single Sign-On (SSO) authentication for your PHP applications.
Requirements
- PHP 8.1 or higher
- Composer
- GuzzleHTTP 7.0+
Installation
composer require finmetrik/console-sso-sdk
Configuration
Laravel
The package auto-discovers in Laravel. Publish the config file:
php artisan vendor:publish --tag=sso-config
Add to your .env:
SSO_HUB_URL=https://console.example.com SSO_APP_KEY=your-app-key SSO_APP_SECRET=your-app-secret SSO_CALLBACK_URL=/sso/callback SSO_VERIFY_SSL=true
Other PHP Frameworks
use Finmetrik\ConsoleSso\SsoClient; $sso = new SsoClient([ 'hub_url' => 'https://console.example.com', 'app_key' => 'your-app-key', 'app_secret' => 'your-app-secret', 'verify_ssl' => true, ]);
Quick Start (Recommended Flow)
The recommended flow is to redirect users to Console's /sso/authorize endpoint, which handles the login UI and redirects back with a token.
1. Redirect to Console SSO
// In your controller public function redirectToSso() { $hubUrl = config('sso.hub_url'); $appKey = config('sso.app_key'); $callbackUrl = urlencode(route('sso.callback')); // Redirect to Console's authorization endpoint return redirect("{$hubUrl}/sso/authorize?app_key={$appKey}&redirect_uri={$callbackUrl}"); }
2. Handle Callback
use Finmetrik\ConsoleSso\Laravel\Facades\Sso; use Finmetrik\ConsoleSso\Exceptions\TokenExpiredException; use Finmetrik\ConsoleSso\Exceptions\CompanyNotSubscribedException; public function callback(Request $request) { // Check for errors from Console if ($request->has('error')) { return redirect('/login')->withErrors([ 'sso' => $request->error_description ?? 'Authentication failed' ]); } $token = $request->query('token'); if (!$token) { return redirect('/login')->withErrors(['sso' => 'No token received']); } try { // Verify token - only sends the token, Console returns user data $result = Sso::verifyToken($token); // $result contains: // - user: ['id', 'email', 'name', 'role'] // - company: ['id', 'uuid', 'name'] // - session: ['id', 'expires_at'] // Create or update local user $user = User::updateOrCreate( ['email' => $result['user']['email']], ['name' => $result['user']['name']] ); Auth::login($user, true); // Store SSO session for later validation session(['sso_session_id' => $result['session']['id']]); return redirect('/dashboard'); } catch (TokenExpiredException $e) { return redirect('/login')->withErrors(['sso' => 'Login link expired']); } catch (CompanyNotSubscribedException $e) { return redirect('/login')->withErrors(['sso' => 'Access denied']); } catch (\Exception $e) { Log::error('SSO Error', ['message' => $e->getMessage()]); return redirect('/login')->withErrors(['sso' => 'Authentication failed']); } }
3. Logout
public function logout(Request $request) { $sessionId = session('sso_session_id'); if ($sessionId) { try { Sso::destroySession($sessionId); } catch (\Exception $e) { // Log but don't fail Log::warning('SSO session destroy failed', ['error' => $e->getMessage()]); } } Auth::logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect('/login'); }
SSO Flow Diagram
┌─────────────────┐ ┌─────────────────┐
│ Your App │ │ Console │
│ (Client App) │ │ (SSO Hub) │
└────────┬────────┘ └────────┬────────┘
│ │
│ 1. User clicks "Login with SSO" │
│ │
│ 2. Redirect to Console: │
│ /sso/authorize? │
│ app_key=xxx& │
│ redirect_uri=xxx │
│ ─────────────────────────────────> │
│ │
│ 3. User logs in │
│ (if needed) │
│ │
│ 4. Redirect back with token: │
│ /sso/callback?token=abc123 │
│ <───────────────────────────────── │
│ │
│ 5. Verify token via API: │
│ POST /api/hub/sso/verify │
│ Body: { "token": "abc123" } │
│ ─────────────────────────────────> │
│ │
│ 6. Return user data + session │
│ <───────────────────────────────── │
│ │
│ 7. Create local user & login │
│ │
▼ ▼
User is now logged in!
Signature Algorithm
The SDK automatically signs all API requests using HMAC-SHA256:
signature = HMAC-SHA256(app_key + timestamp + request_body, app_secret)
Headers sent with each request:
X-App-Key: Your application keyX-Timestamp: Unix timestampX-Signature: HMAC signature
Session Validation Middleware
Laravel
Register the middleware:
// app/Http/Kernel.php (Laravel 10 and below) protected $middlewareAliases = [ 'sso.session' => \Finmetrik\ConsoleSso\Laravel\Middleware\ValidateSsoSession::class, ]; // bootstrap/app.php (Laravel 11+) ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'sso.session' => \Finmetrik\ConsoleSso\Laravel\Middleware\ValidateSsoSession::class, ]); })
Apply to routes:
Route::middleware(['auth', 'sso.session'])->group(function () { Route::get('/dashboard', [DashboardController::class, 'index']); });
Manual Validation
try { $session = Sso::validateSession($sessionId); // Session is valid } catch (SessionExpiredException $e) { Auth::logout(); return redirect('/login'); }
API Reference
SsoClient Methods
| Method | Description |
|---|---|
bootstrap() |
Fetch app configuration from SSO Hub |
verifyToken(string $token) |
Verify SSO token and get user data |
validateSession(string $sessionId) |
Check if session is valid |
refreshSession(string $sessionId) |
Extend session expiry |
destroySession(string $sessionId) |
Logout / destroy session |
isConfigured() |
Check if client is configured |
getAppInfo() |
Get app information |
Exceptions
| Exception | When Thrown |
|---|---|
ConfigurationException |
Missing or invalid configuration |
ApiException |
API request failed |
TokenExpiredException |
SSO token has expired |
TokenUsedException |
Token already consumed |
SessionExpiredException |
Session has expired |
CompanyNotSubscribedException |
Company not subscribed to app |
UserNotFoundException |
User not found in company |
Error Handling
use Finmetrik\ConsoleSso\Exceptions\{ TokenExpiredException, TokenUsedException, SessionExpiredException, CompanyNotSubscribedException, UserNotFoundException, ApiException }; try { $result = Sso::verifyToken($token); } catch (TokenExpiredException $e) { // Token expired (5 min validity), redirect to login } catch (TokenUsedException $e) { // Token already used, possible replay attack } catch (CompanyNotSubscribedException $e) { // Company doesn't have access to this app } catch (UserNotFoundException $e) { // User not found in company } catch (ApiException $e) { // General API error Log::error('SSO Error: ' . $e->getMessage()); }
Debugging
Enable debug mode by passing a PSR-3 logger:
use Monolog\Logger; use Monolog\Handler\StreamHandler; $logger = new Logger('sso'); $logger->pushHandler(new StreamHandler('path/to/sso.log', Logger::DEBUG)); $sso = new SsoClient([ 'hub_url' => '...', 'app_key' => '...', 'app_secret' => '...', 'logger' => $logger, ]);
Security
- Never expose
app_secretin client-side code - Always use HTTPS in production
- Store session IDs server-side only
- Implement CSRF protection on callback endpoints
- Validate the
stateparameter if using one
Changelog
v1.0.1
- Fixed token verification to only require token parameter
- Console now returns user data from token record
- Updated documentation with correct flow
v1.0.0
- Initial release
License
MIT License. See LICENSE for details.
Support
- Documentation: See
docs/plan/in Console repository - Issues: Contact your Console administrator