signward/idserver-client

PHP client SDK for Signward Identity Server — OIDC authentication for Laravel and other PHP apps.

Maintainers

Package info

github.com/signward/idserver-client-php

Homepage

Issues

Documentation

pkg:composer/signward/idserver-client

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-06-06 11:11 UTC

This package is auto-updated.

Last update: 2026-06-06 11:24:05 UTC


README

PHP client SDK for Signward Identity Server — OIDC authentication for Laravel and any other PHP app. Open source (MIT), no Microsoft-stack dependency.

Features

  • OIDC Authorization Code flow with PKCE
  • Local JWT validation via the server's JWKS (HS256 + RS256), with discovery + JWKS caching
  • Typed User model with built-in and per-tenant custom roles
  • Token exchange, refresh, userinfo, and end-session helpers
  • First-class Laravel integration: auto-discovered service provider, idserver.auth / idserver.role middleware, bundled login routes, and an IdServer facade
  • MIT licensed

Install

composer require signward/idserver-client

PHP 8.1+. The Laravel integration targets Laravel 10, 11, and 12.

Quickstart — Laravel

The package is auto-discovered. Publish the config and set your tenant credentials:

php artisan vendor:publish --tag=idserver-config
IDSERVER_AUTHORITY=https://mytenant.signward.com
IDSERVER_CLIENT_ID=my-webapp
IDSERVER_CLIENT_SECRET=...

Register https://myapp.com/auth/idserver/callback as a redirect URI on your Signward client. The package mounts these routes automatically:

Route Purpose
GET /auth/idserver/login Start the OIDC flow (redirects to Signward, with PKCE)
GET /auth/idserver/callback Exchange the code for tokens, store them in the session
GET|POST /auth/idserver/logout Clear the session and end the Signward session

Protect routes with the bundled middleware:

use Illuminate\Support\Facades\Route;
use Signward\IdServer\Laravel\Facades\IdServer;

// Require a signed-in Signward user
Route::middleware('idserver.auth')->group(function () {
    Route::get('/dashboard', function () {
        $user = IdServer::user();          // Signward\IdServer\Models\User
        return "Hello {$user->email}";
    });
});

// Require a role (any-of). Built-in and per-tenant custom roles both count.
Route::middleware('idserver.role:admin,editor')->get('/admin', fn () => 'Admin area');

IdServer::user() returns null when signed out; IdServer::check() is a quick boolean. IdServer::client() exposes the underlying IdServerClient for advanced use (refresh, userinfo, logout URL).

Quickstart — plain PHP (no framework)

use Signward\IdServer\IdServerClient;

$client = IdServerClient::create(
    authority: 'https://mytenant.signward.com',
    clientId: 'my-webapp',
    clientSecret: '...',
);

// 1) Redirect the user to the login page:
['url' => $url, 'verifier' => $verifier] = $client->authorizeUrlWithPkce(
    redirectUri: 'https://myapp.com/callback',
    state: 'random-state',
);
// Store $verifier + state in the session, then redirect to $url.

// 2) On callback (?code=...&state=...):
$tokens = $client->exchangeCode($code, 'https://myapp.com/callback', $verifier);

// 3) Validate the access token locally (signature, iss, aud, exp via JWKS):
$user = $client->validateToken($tokens->accessToken);
echo $user->email, ' ', implode(',', $user->roles);

// 4) Or fetch userinfo remotely:
$user = $client->userinfo($tokens->accessToken);

// 5) Later, refresh:
$fresh = $client->refreshToken($tokens->refreshToken);

// 6) Logout URL:
$logout = $client->endSessionUrl(
    idTokenHint: $tokens->idToken,
    postLogoutRedirectUri: 'https://myapp.com/',
);

Validating tokens in an API

To protect a stateless API (no login flow), validate the incoming bearer token:

use Signward\IdServer\IdServerClient;
use Signward\IdServer\IdServerOptions;
use Signward\IdServer\Exceptions\InvalidTokenException;

$client = new IdServerClient(new IdServerOptions(
    authority: 'https://mytenant.signward.com',
    clientId: 'my-api',
    audience: 'idserver-api',     // expected `aud` claim
));

try {
    $user = $client->validateToken($bearerToken);
} catch (InvalidTokenException $e) {
    http_response_code(401);
    exit;
}

if (!$user->hasAnyRole(['admin'])) {
    http_response_code(403);
    exit;
}

User model

final class User
{
    public readonly ?string $userId;        // "sub" claim
    public readonly ?string $email;
    public readonly ?bool $emailVerified;
    public readonly ?string $name;
    public readonly ?string $tenantId;
    public readonly array $roles;           // built-in roles
    public readonly array $customRoles;     // per-tenant RBAC roles
    public readonly array $claims;          // raw JWT / userinfo payload

    public function hasRole(string $role): bool;
    public function hasCustomRole(string $role): bool;
    public function hasAnyRole(array $roles, bool $includeCustom = true): bool;
    public function hasAllRoles(array $roles, bool $includeCustom = true): bool;
}

Configuration

config/idserver.php (publishable). All values default from environment variables:

return [
    'authority' => env('IDSERVER_AUTHORITY'),
    'client_id' => env('IDSERVER_CLIENT_ID'),
    'client_secret' => env('IDSERVER_CLIENT_SECRET'),
    'scopes' => ['openid', 'profile', 'email', 'roles'],
    'audience' => env('IDSERVER_AUDIENCE'),
    'verify_ssl' => env('IDSERVER_VERIFY_SSL', true),
    'routes' => ['enabled' => true, 'prefix' => 'auth/idserver', 'middleware' => ['web']],
    'redirects' => ['after_login' => '/', 'after_logout' => '/'],
];

Outside Laravel, pass an IdServerOptions to the client directly (see above).

Error handling

All exceptions extend Signward\IdServer\Exceptions\IdServerException:

use Signward\IdServer\Exceptions\InvalidTokenException;
use Signward\IdServer\Exceptions\TokenExchangeException;
use Signward\IdServer\Exceptions\IdServerException;

try {
    $user = $client->validateToken($token);
} catch (InvalidTokenException $e) {
    // bad signature / issuer / audience / expiry
} catch (TokenExchangeException $e) {
    echo $e->error, ' ', $e->description, ' ', $e->statusCode;
} catch (IdServerException $e) {
    // discovery / userinfo / transport error
}

License

MIT