PHPackages                             sengheat/laravel-sso - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. sengheat/laravel-sso

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

sengheat/laravel-sso
====================

A simple and flexible Single Sign-On (SSO) package for Laravel supporting Google, GitHub, Azure AD, and any OAuth2/OpenID Connect provider.

2.0.2(3w ago)02↓100%MITBladePHP ^8.1

Since May 6Pushed 3w agoCompare

[ Source](https://github.com/SengHeat/sso-token)[ Packagist](https://packagist.org/packages/sengheat/laravel-sso)[ RSS](/packages/sengheat-laravel-sso/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (5)Versions (5)Used By (0)

sengheat/sso-token
==================

[](#sengheatsso-token)

A Laravel SSO package — one central auth-service issues tokens, every other service verifies them without HTTP calls.

---

How It Works
------------

[](#how-it-works)

```
┌─────────────────────────────────────────────────────────┐
│                    auth-service (Issuer)                 │
│  /sso/login  →  user logs in  →  issues api_token       │
└───────────────────────────┬─────────────────────────────┘
                            │ token
          ┌─────────────────┼─────────────────┐
          ▼                 ▼                 ▼
   order-service      logistic-portal    mobile-app
   verifies token     verifies token     verifies token
   via auth_db        via auth_db        via auth_db
   (no HTTP call)     (no HTTP call)     (no HTTP call)

```

---

Installation
------------

[](#installation)

```
composer require sengheat/sso-token
php artisan sso:install
```

---

Part 1 — Auth Service (Issuer)
------------------------------

[](#part-1--auth-service-issuer)

The service that owns the `users` table and issues tokens.

### 1. Enable issuer mode in `.env`

[](#1-enable-issuer-mode-in-env)

```
SSO_FORM_AUTH=true
SSO_FORM_AUTH_REGISTER=true
SSO_ALLOWED_REDIRECTS=http://localhost:3000/sso/callback,https://portal.yourdomain.com/sso/callback
```

### 2. Publish and configure `config/sso.php`

[](#2-publish-and-configure-configssophp)

```
return [
    'form_auth' => [
        'enabled'        => env('SSO_FORM_AUTH', false),
        'allow_register' => env('SSO_FORM_AUTH_REGISTER', true),
    ],

    'allowed_redirects' => array_filter(explode(',', env('SSO_ALLOWED_REDIRECTS', ''))),

    'redirect_after_login'  => env('SSO_REDIRECT_AFTER_LOGIN', '/dashboard'),
    'redirect_after_logout' => env('SSO_REDIRECT_AFTER_LOGOUT', '/sso/login'),

    'user_model'      => env('SSO_USER_MODEL', \App\Models\User::class),
    'register_routes' => true,
    'run_migrations'  => true,

    'cache_store'     => env('SSO_CACHE_STORE', null),
    'token_cache_ttl' => env('SSO_TOKEN_CACHE_TTL', 300),
];
```

### 3. Add `HasSSOProfile` trait to User model

[](#3-add-hasssoprofile-trait-to-user-model)

```
use SengHeat\LaravelSso\Traits\HasSSOProfile;

class User extends Authenticatable
{
    use HasSSOProfile;

    protected $fillable = [
        'name', 'email', 'password', 'api_token',
        'sso_provider', 'sso_provider_id', 'sso_token', 'sso_avatar',
    ];

    protected $hidden = [
        'password', 'remember_token', 'api_token',
        'sso_token', 'sso_provider', 'sso_provider_id', 'sso_avatar',
    ];
}
```

### 4. Add `api` guard to `config/auth.php`

[](#4-add-api-guard-to-configauthphp)

```
'guards' => [
    'web' => ['driver' => 'session', 'provider' => 'users'],
    'api' => ['driver' => 'token',   'provider' => 'users', 'hash' => true],
],
```

### 5. Run migration

[](#5-run-migration)

```
php artisan migrate
```

Adds to `users` table: `sso_provider`, `sso_provider_id`, `sso_token`, `sso_avatar`, `api_token` (indexed).

### Available routes (auto-registered)

[](#available-routes-auto-registered)

MethodURIDescriptionGET`/sso/login`Login page (form)POST`/sso/login`Process loginGET`/sso/register`Register pagePOST`/sso/register`Process registerPOST`/api/sso/login`API login → `{user, token}`POST`/api/sso/register`API register → `{user, token}`POST`/api/sso/exchange`Exchange one-time code → `{user, token}`POST`/api/sso/logout`Revoke tokenGET`/api/sso/user`Current user info---

Part 2 — Other Services (Consumers)
-----------------------------------

[](#part-2--other-services-consumers)

Order service, product service, any service that needs to verify tokens.

### 1. Install package

[](#1-install-package)

```
composer require sengheat/sso-token
```

### 2. Add `auth_db` connection to `config/database.php`

[](#2-add-auth_db-connection-to-configdatabasephp)

```
'auth_db' => [
    'driver'   => 'pgsql',
    'host'     => env('AUTH_DB_HOST', '127.0.0.1'),
    'port'     => env('AUTH_DB_PORT', '5432'),
    'database' => env('AUTH_DB_DATABASE', 'auth_service'),
    'username' => env('AUTH_DB_USERNAME', 'postgres'),
    'password' => env('AUTH_DB_PASSWORD', ''),
],
```

### 3. Add `sso` guard to `config/auth.php`

[](#3-add-sso-guard-to-configauthphp)

```
'guards' => [
    'sso' => ['driver' => 'token', 'provider' => 'sso_users', 'hash' => true],
],

'providers' => [
    'sso_users' => ['driver' => 'eloquent', 'model' => App\Models\User::class],
],
```

### 4. User model — points to `auth_db`

[](#4-user-model--points-to-auth_db)

```
use SengHeat\LaravelSso\Traits\HasSSOProfile;

class User extends Authenticatable
{
    use HasSSOProfile;

    protected $connection = 'auth_db';

    protected $fillable = [
        'name', 'email', 'api_token',
        'sso_provider', 'sso_provider_id', 'sso_avatar',
    ];
}
```

### 5. Register middleware in `bootstrap/app.php`

[](#5-register-middleware-in-bootstrapappphp)

```
use SengHeat\LaravelSso\Http\Middleware\EnsureSSOUser;
use SengHeat\LaravelSso\Http\Middleware\CacheSsoToken;

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'sso.only'  => EnsureSSOUser::class,
        'sso.cache' => CacheSsoToken::class,
    ]);
})
```

### 6. Protect routes

[](#6-protect-routes)

```
// sso.cache runs first — loads user from Redis or auth_db
// auth:sso runs second — user already set, no DB query
Route::middleware(['sso.cache:sso', 'auth:sso'])->group(function () {
    Route::apiResource('orders', OrderController::class);
});
```

### 7. `.env`

[](#7-env)

```
AUTH_DB_HOST=127.0.0.1
AUTH_DB_PORT=5432
AUTH_DB_DATABASE=auth_service
AUTH_DB_USERNAME=order_svc
AUTH_DB_PASSWORD=your_password

SSO_CACHE_STORE=redis
SSO_TOKEN_CACHE_TTL=300
SSO_FORM_AUTH=false
```

### How token verification works

[](#how-token-verification-works)

```
Request: Authorization: Bearer
    │
    ├─ sso.cache:sso
    │      HIT  → load from Redis → Auth::guard('sso')->setUser($user)
    │      MISS → query auth_db  → store in Redis for 300s
    │
    └─ auth:sso → user already set → skip DB ✓

```

### PostgreSQL security (production)

[](#postgresql-security-production)

```
-- Create restricted read-only user per service
CREATE USER order_svc WITH PASSWORD 'secret';
GRANT CONNECT ON DATABASE auth_service TO order_svc;
GRANT USAGE ON SCHEMA public TO order_svc;
GRANT SELECT ON TABLE public.users TO order_svc;
```

```
# pg_hba.conf — only allow order-service IP
host  auth_service  order_svc  /32  scram-sha-256
host  auth_service  all        0.0.0.0/0              reject

```

---

Part 3 — Web Portal (Next.js / React)
-------------------------------------

[](#part-3--web-portal-nextjs--react)

Token is **never** visible in the URL. A 30-second one-time code is used instead.

### Flow

[](#flow)

```
1. Portal → redirect to auth-service login with redirect_to
2. User logs in on auth-service
3. Auth-service → redirect back with ?code=xxx  (30s expiry, one-time)
4. Portal → POST /api/sso/exchange with code → receives real token
5. Token stored in app state / secure storage

```

### Step 1 — Redirect to auth-service

[](#step-1--redirect-to-auth-service)

```
// .env.local
// NEXT_PUBLIC_SSO_URL=http://localhost:8000
// NEXT_PUBLIC_SSO_REDIRECT_URI=http://localhost:3000/sso/callback

const params = new URLSearchParams({ redirect_to: process.env.NEXT_PUBLIC_SSO_REDIRECT_URI! })
window.location.href = `${process.env.NEXT_PUBLIC_SSO_URL}/sso/login?${params}`
```

### Step 2 — Callback page (`/sso/callback`)

[](#step-2--callback-page-ssocallback)

```
"use client"
import { useEffect } from "react"
import { useRouter, useSearchParams } from "next/navigation"

export default function SsoCallbackPage() {
    const router = useRouter()
    const searchParams = useSearchParams()

    useEffect(() => {
        const code = searchParams.get("code")
        if (!code) { router.replace("/login"); return }

        fetch(`${process.env.NEXT_PUBLIC_SSO_URL}/api/sso/exchange`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ code }),
        })
        .then(res => res.json())
        .then(({ user, token }) => {
            // store token in zustand / context / cookie
            storeLogin(user, token)
            router.replace("/dashboard")
        })
        .catch(() => router.replace("/login"))
    }, [])

    return Signing you in…
}
```

### Step 3 — Use token on any service

[](#step-3--use-token-on-any-service)

```
fetch("http://localhost:8001/api/orders", {
    headers: { Authorization: `Bearer ${token}` }
})
```

### `.env.local`

[](#envlocal)

```
NEXT_PUBLIC_SSO_URL=http://localhost:8000
NEXT_PUBLIC_SSO_REDIRECT_URI=http://localhost:3000/sso/callback
```

---

Part 4 — Mobile App (React Native / Flutter)
--------------------------------------------

[](#part-4--mobile-app-react-native--flutter)

Mobile calls the **API endpoints directly** — no browser redirect needed.

### Login

[](#login)

```
POST /api/sso/login
{ "email": "user@example.com", "password": "password" }

→ { "user": {...}, "token": "xxxx" }

```

Store token securely (iOS Keychain / Android Keystore).

### Use token on any service

[](#use-token-on-any-service)

```
GET  http://order-service/api/orders
Authorization: Bearer xxxx

```

### Logout

[](#logout)

```
POST /api/sso/logout
Authorization: Bearer xxxx
→ token revoked on all services instantly

```

### React Native

[](#react-native)

```
import * as SecureStore from "expo-secure-store"

// Login
const { user, token } = await fetch("http://auth:8000/api/sso/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
}).then(r => r.json())

await SecureStore.setItemAsync("sso_token", token)

// Use on any service
const token = await SecureStore.getItemAsync("sso_token")
const orders = await fetch("http://orders:8001/api/orders", {
    headers: { Authorization: `Bearer ${token}` },
}).then(r => r.json())
```

### Flutter

[](#flutter)

```
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final storage = FlutterSecureStorage();

// Login
final res = await http.post(Uri.parse('http://auth:8000/api/sso/login'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({'email': email, 'password': password}));
final token = jsonDecode(res.body)['token'];
await storage.write(key: 'sso_token', value: token);

// Use on any service
final token = await storage.read(key: 'sso_token');
final orders = await http.get(Uri.parse('http://orders:8001/api/orders'),
    headers: {'Authorization': 'Bearer $token'});
```

---

Trait Helpers
-------------

[](#trait-helpers)

```
$user->isSSOUser();               // true if logged in via OAuth provider
$user->usesProvider('google');    // true | false
$user->ssoAvatar('/default.png'); // avatar URL with fallback

$user->generateApiToken();        // generates token, stores SHA256 hash, returns plain
$user->revokeApiToken();          // sets api_token = null (logout)

User::fromProvider('google')->get();  // query scope
User::nativeUsers()->get();           // users without OAuth
```

---

Security Summary
----------------

[](#security-summary)

ConcernSolutionToken in URLOne-time code exchange — token never in URLToken storageSHA256 hash in DB — plain token only in HTTP responseOpen redirect`SSO_ALLOWED_REDIRECTS` whitelistDB accessDedicated read-only PostgreSQL user per serviceNetwork`pg_hba.conf` IP whitelist on auth\_db portBrute force`throttle:20,1` on login routesCacheRedis with TTL — stale tokens auto-expire---

Environment Variables Reference
-------------------------------

[](#environment-variables-reference)

VariableServiceDefaultDescription`SSO_FORM_AUTH`Issuer`false`Enable built-in login/register`SSO_FORM_AUTH_REGISTER`Issuer`true`Allow new registrations`SSO_ALLOWED_REDIRECTS`Issuer—Comma-separated portal callback URLs`SSO_REDIRECT_AFTER_LOGIN`Issuer`/dashboard`Fallback redirect after login`SSO_REDIRECT_AFTER_LOGOUT`Issuer`/sso/login`Redirect after logout`SSO_CACHE_STORE`Consumer`null` (default)Cache driver: `redis`, `file``SSO_TOKEN_CACHE_TTL`Consumer`300`Token cache TTL in seconds`AUTH_DB_HOST`Consumer`127.0.0.1`Auth-service DB host`AUTH_DB_PORT`Consumer`5432`Auth-service DB port`AUTH_DB_DATABASE`Consumer`auth_service`Auth-service DB name`AUTH_DB_USERNAME`Consumer—Read-only DB user`AUTH_DB_PASSWORD`Consumer—DB password---

License
-------

[](#license)

MIT — Seng Heat

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance95

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

4

Last Release

24d ago

Major Versions

v1.0.2 → 2.0.22026-05-16

PHP version history (2 changes)1.0.0PHP ^8.2

2.0.2PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/464241a632f520db5c02e8ff2b000173d54720b303233551b4cccad22d913895?d=identicon)[SengHeat](/maintainers/SengHeat)

---

Top Contributors

[![SengHeat](https://avatars.githubusercontent.com/u/150521943?v=4)](https://github.com/SengHeat "SengHeat (23 commits)")

---

Tags

laravelgoogleAuthenticationSSOsocialiteoauth2githubazureOpenId

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/sengheat-laravel-sso/health.svg)

```
[![Health](https://phpackages.com/badges/sengheat-laravel-sso/health.svg)](https://phpackages.com/packages/sengheat-laravel-sso)
```

###  Alternatives

[hwi/oauth-bundle

Support for authenticating users using both OAuth1.0a and OAuth2 in Symfony.

2.4k22.0M75](/packages/hwi-oauth-bundle)[andrewdwallo/filament-companies

A comprehensive Laravel authentication and authorization system designed for Filament, focusing on multi-tenant company management.

34654.9k2](/packages/andrewdwallo-filament-companies)[truckersmp/steam-socialite

Laravel Socialite provider for Steam OpenID.

1517.7k](/packages/truckersmp-steam-socialite)[maicol07/laravel-oidc-client

OpenID Connect Client for Laravel

271.3k](/packages/maicol07-laravel-oidc-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
