Base IdP

Base IdP

SDKs

Laravel / PHP

The squareexp/base-idp Composer package — confidential server login and token verification for Laravel applications.

The Laravel package wraps Base IdP for PHP applications. It runs the login, exchanges the code, verifies tokens, and exposes the current user through guards and middleware. It is the right tool for server-rendered PHP apps that authenticate users themselves.

If your PHP app is a pure API behind a mobile or SPA frontend, you only need the verifier — no secret required. The package supports both shapes.

Install

composer require squareexp/base-idp

The package targets Laravel 10 and 11. It also works in a plain PHP application; the framework integration is optional.

Publish the config

php artisan vendor:publish --provider="Squareexp\BaseIdp\BaseIdpServiceProvider"

This publishes config/base-idp.php where you control timeouts, the expected scopes, and the default redirect path.

Configure

.env
BASE_IDP_CLIENT_ID=sq_live_yourapp
BASE_IDP_CLIENT_SECRET=sqk_your_secret

The secret is server-side only. Keep .env out of version control.

The login routes

Wire two routes — one to start the flow, one to handle the callback.

routes/web.php
use Squareexp\BaseIdp\Http\Controllers\BaseIdpController;

Route::get('/auth/login',    [BaseIdpController::class, 'login'])->name('auth.login');
Route::get('/auth/callback', [BaseIdpController::class, 'callback'])->name('auth.callback');

The default callback handler signs the user into Laravel's session guard and redirects to /. Override the controller to do something different.

Custom callback handling

app/Http/Controllers/AuthController.php
use Squareexp\BaseIdp\Facades\BaseIdp;

class AuthController extends Controller
{
    public function callback(Request $request)
    {
        $tokens = BaseIdp::exchangeCode($request->query('code'));
        $principal = BaseIdp::verify($tokens['access_token']);

        $user = User::updateOrCreate(
            ['idp_subject' => $principal['sub']],
            [
                'email' => $principal['email'],
                'name'  => $principal['name'],
            ],
        );

        Auth::login($user);
        return redirect()->intended('/dashboard');
    }
}

Verifying tokens

For an API that only verifies incoming tokens:

use Squareexp\BaseIdp\Facades\BaseIdp;

$principal = BaseIdp::verify($bearerToken);
// [
//   'sub'    => 'usr_01HRZ...',
//   'gid'    => 'gid_01HRZ...',
//   'email'  => 'user@example.com',
//   'name'   => 'Ajmal Leonard',
//   'scopes' => ['openid', 'profile'],
// ]

Middleware

The package ships a route middleware:

routes/api.php
Route::middleware('base-idp')->group(function () {
    Route::get('/me', function (Request $request) {
        return response()->json($request->attributes->get('principal'));
    });
});

The middleware reads the Authorization: Bearer header, verifies the token, and attaches the principal to the request.

Auth guard

For server-rendered apps the package registers a guard you can wire into config/auth.php.

config/auth.php
'guards' => [
    'base-idp' => [
        'driver'  => 'base-idp',
        'provider' => 'users',
    ],
],

Use it the same way you use any other guard:

Auth::guard('base-idp')->user();

Refresh

$tokens = BaseIdp::refresh($refreshToken);

Store the new refresh token. The old one is invalidated as soon as you call this.

Logout

BaseIdp::logout($refreshToken);
Auth::guard('base-idp')->logout();

The first call revokes the session on Base IdP. The second clears Laravel's session.

A complete fullstack flow

app/Http/Controllers/AuthController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Squareexp\BaseIdp\Facades\BaseIdp;
use App\Models\User;

class AuthController extends Controller
{
    public function login()
    {
        return redirect(BaseIdp::authorizeUrl([
            'state' => session('return_to', '/'),
        ]));
    }

    public function callback(Request $request)
    {
        $tokens = BaseIdp::exchangeCode($request->query('code'));
        $principal = BaseIdp::verify($tokens['access_token']);

        $user = User::updateOrCreate(
            ['idp_subject' => $principal['sub']],
            [
                'email' => $principal['email'],
                'name'  => $principal['name'],
            ],
        );

        Auth::login($user);
        return redirect($request->query('state', '/dashboard'));
    }

    public function logout(Request $request)
    {
        $refresh = session('base_idp_refresh');
        if ($refresh) {
            BaseIdp::logout($refresh);
        }
        Auth::logout();
        return redirect('/');
    }
}

A complete confidential-server login in one controller. Add the routes, publish the config, set the two env values, and the app is integrated.

Where to go next

On this page