onelabs / xguard-client
Laravel client SDK for x-guard OAuth 2.0 / OpenID Connect Identity Provider. Drop-in Socialite provider with default routes, controller, and Blade button component.
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.0
- illuminate/auth: ^10.0|^11.0|^12.0
- illuminate/contracts: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- illuminate/view: ^10.0|^11.0|^12.0
- laravel/socialite: ^5.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
Laravel client SDK for x-guard — an OAuth 2.0 / OpenID Connect Identity Provider.
Drop in a Socialite provider, default routes, and a Blade login button. Get SSO into your Laravel app in 5 minutes.
composer require onelabs/xguard-client
What it does
- Registers
Socialite::driver('xguard')— same API you already know. - Provides default
/auth/xguard/redirect&/auth/xguard/callbackroutes (opt-out). - Resolves SSO users to your local
Usermodel (override viaUserResolverinterface). - Ships a Blade component
<x-xguard-login-button />for instant UI.
Requirements
- PHP 8.2+
- Laravel 10, 11, or 12
- A running x-guard server with an App registered for your project (see Developer Portal Setup)
Installation
1. Install the package
composer require onelabs/xguard-client
2. Add credentials to .env
XGUARD_ISSUER=https://x-guard.example.com XGUARD_CLIENT_ID=xg_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx XGUARD_CLIENT_SECRET=PASTE_PLAIN_SECRET_HERE XGUARD_REDIRECT_URI="${APP_URL}/auth/xguard/callback"
The
XGUARD_REDIRECT_URIvalue must exactly match a Redirect URI registered for your App in the x-guard Developer Portal — including scheme, host, port, and path.
3. Add to config/services.php
'xguard' => [ 'issuer' => env('XGUARD_ISSUER'), 'client_id' => env('XGUARD_CLIENT_ID'), 'client_secret' => env('XGUARD_CLIENT_SECRET'), 'redirect' => env('XGUARD_REDIRECT_URI'), ],
4. Publish the migration & run it
The package adds an sso_user_id column to your users table (used to link local users to x-guard).
php artisan vendor:publish --tag=xguard-migrations php artisan migrate
The migration is idempotent: it skips if the column already exists.
5. Add the login button to your login view
<x-xguard-login-button />
That's it. The button points to the default /auth/xguard/redirect route, which handles the entire OAuth flow.
Customization
Publish the config (optional)
php artisan vendor:publish --tag=xguard-config
This creates config/xguard.php where you can change defaults: scopes, route prefix, redirect-after-login, lookup column, button text, etc.
Customize the button label/style
<x-xguard-login-button label="Masuk via SSO" class="btn btn-lg btn-primary w-100" /> <x-xguard-login-button :icon="false" />
Publish the button view (full markup control)
php artisan vendor:publish --tag=xguard-views
# edits resources/views/vendor/xguard/components/login-button.blade.php
Custom user resolver
Need different linking logic (multi-table user storage, role assignment, domain whitelist)?
Implement the UserResolver contract:
// app/Auth/MyXGuardResolver.php namespace App\Auth; use Onelabs\XGuardClient\Contracts\UserResolver; use Illuminate\Contracts\Auth\Authenticatable; use Laravel\Socialite\Two\User as SocialiteUser; class MyXGuardResolver implements UserResolver { public function resolve(SocialiteUser $ssoUser): Authenticatable { // Reject users from non-allowed domains, for example: if (!str_ends_with((string) $ssoUser->getEmail(), '@mycompany.com')) { throw new \DomainException('Hanya email @mycompany.com yang diizinkan.'); } // ... your custom lookup / create logic ... return \App\Models\User::firstOrCreate( ['sso_user_id' => $ssoUser->getId()], ['name' => $ssoUser->getName(), 'email' => $ssoUser->getEmail()], ); } }
Bind it in AppServiceProvider::register():
$this->app->bind( \Onelabs\XGuardClient\Contracts\UserResolver::class, \App\Auth\MyXGuardResolver::class, );
Disable default routes (write your own)
Set XGUARD_USE_DEFAULT_ROUTES=false (or in published config: 'use_default_routes' => false), then write your own controller using the Socialite driver:
public function redirect() { return Socialite::driver('xguard')->redirect(); } public function callback(Request $request) { $ssoUser = Socialite::driver('xguard')->user(); // ... your logic }
Custom scopes per request
Socialite::driver('xguard') ->scopes(['openid', 'profile', 'email', 'phone', 'offline_access']) ->redirect();
Available scopes: openid (required), profile, email, phone, offline_access (triggers refresh token).
Getting credentials from x-guard
You need 4 values from your x-guard developer:
| Value | Where it comes from |
|---|---|
XGUARD_ISSUER |
The x-guard server URL (e.g. https://x-guard.example.com) |
XGUARD_CLIENT_ID |
Developer Portal → your App → Overview tab |
XGUARD_CLIENT_SECRET |
Developer Portal → your App → Credentials → Generate New Secret (shown ONCE) |
XGUARD_REDIRECT_URI |
Developer Portal → your App → Redirect URIs → add the exact callback URL of this Laravel app |
If you're the x-guard developer, log in to x-guard and follow the Developer Portal flow.
How it works (under the hood)
- User clicks
<x-xguard-login-button />→ goes to/auth/xguard/redirect XGuardLoginController::redirect()callsSocialite::driver('xguard')->redirect()- User is sent to
{XGUARD_ISSUER}/oauth/authorize→ logs in at x-guard → grants consent - x-guard redirects back to
{XGUARD_REDIRECT_URI}?code=...&state=... XGuardLoginController::callback():- Exchanges code for tokens at
{XGUARD_ISSUER}/oauth/token - Fetches user profile from
{XGUARD_ISSUER}/oauth/userinfo - Calls
UserResolver::resolve($ssoUser)to find/create the local user Auth::login()and redirects to the intended URL (orredirect_after_login)
- Exchanges code for tokens at
PKCE, JWT signature verification (via the OIDC userinfo server-to-server fetch), state CSRF protection — all handled by Socialite + the underlying x-guard server.
Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
error=invalid_request, redirect_uri not registered |
XGUARD_REDIRECT_URI doesn't exactly match what's whitelisted in Developer Portal. Check scheme, host, port, path, trailing slash. |
error=invalid_client |
Wrong XGUARD_CLIENT_SECRET or App is suspended in Developer Portal. |
| User logs in but new account is created instead of linking | Check that email returned by x-guard matches your local user's email exactly. Or override UserResolver. |
| Button has no link (href="#") | Default routes disabled but no xguard.redirect route defined. Either re-enable default routes or pass href="...". |
Class "Laravel\Socialite\..." not found |
Socialite is a peer dep — composer require laravel/socialite once. |
License
MIT — see LICENSE.
Credits
- Built on top of Laravel Socialite.
- The icon is a generic shield-with-lock SVG; replace by publishing the view if you have x-guard branding.