Looking to hire Laravel developers? Try LaraJobs

sdk-php-laravel maintained by authn-sh

Description
Laravel package for authn.sh — service provider, middleware, Blade directives, facade. Wraps authn-sh/sdk-php for Laravel 11+.
Last update
2026/05/15 15:35 (dev-main)
License
Downloads
7

Comments
comments powered by Disqus

authn-sh/sdk-php-laravel

Laravel package for authn.sh — auto-discovered service provider, container bindings, middleware, Blade directives, and an Authn facade. Wraps authn-sh/sdk-php for Laravel 11+.

Requirements

  • PHP 8.2+
  • Laravel 11+ (^11.0 || ^12.0)
  • A PSR-18 HTTP client (Guzzle, Symfony HttpClient, etc.) — required by the underlying authn-sh/sdk-php SDK and auto-discovered via php-http/discovery.

Install

composer require authn-sh/sdk-php-laravel

The service provider auto-discovers. Publish the config:

php artisan vendor:publish --tag=authn-config

Middleware

Register authn on routes that require a valid session JWT:

Route::middleware('authn')->group(function () {
    Route::get('/dashboard', DashboardController::class);
});

Blade directives

@authnSignedIn / @authnSignedOut

@authnSignedIn
    <p>Welcome back, {{ Authn::auth()->sub }}</p>
@endauthnSignedIn

@authnSignedOut
    <a href="/login">Sign in</a>
@endauthnSignedOut

@authnHas

Evaluates organization roles and permissions from the active session JWT:

@authnHas('role:org:admin')
    <a href="/admin">Admin panel</a>
@else
    <p>You do not have admin access.</p>
@endauthnHas

@authnHas('permission:org:billing:manage')
    <a href="/billing">Billing</a>
@endauthnHas

The argument must be prefixed with role: or permission:. Any other prefix raises an InvalidArgumentException at render time so typos fail loudly.

@authnHasConnectedAccount

Renders the inner block when the active session JWT advertises an ExternalAccount linked to the named OAuth provider:

@authnHasConnectedAccount('google')
    <p>Connected to Google.</p>
@else
    <a href="/sign-in/sso-callback?provider=google">Connect Google</a>
@endauthnHasConnectedAccount

The matching route middleware authn.connected:<provider_key> fail-closes by redirecting to authn.connected_accounts.redirect_url (the {provider} placeholder is substituted with the route argument):

Route::get('/integrations/google', [GoogleController::class, 'show'])
    ->middleware(['authn', 'authn.connected:google']);

@authnHasPasskey

Renders the inner block based on the active session's passkey signal. Two modes:

  • verified (default) — matches when the current session was authenticated by a passkey first-factor (VerifiedClaims::wasVerifiedByPasskey()).
  • enrolled — matches whenever the user has at least one verified passkey on file (VerifiedClaims::hasPasskey()).
@authnHasPasskey
    <a href="/account/api-keys">Manage API keys</a>
@else
    <p>This area requires a passkey sign-in.</p>
@endauthnHasPasskey

@authnHasPasskey('enrolled')
    <p>You already have a passkey on file.</p>
@else
    <a href="/user/security/passkeys">Add your first passkey</a>
@endauthnHasPasskey

The matching route middleware authn.requires_passkey[:<mode>] fail-closes by redirecting unauthenticated requests to authn.url.sign_in and signed-in-but-under-authenticated requests to authn.passkey.enroll_url:

Route::get('/account/security', [SecurityController::class, 'show'])
    ->middleware(['authn', 'authn.requires_passkey']);            // verified (default)

Route::get('/account/api-keys', [ApiKeyController::class, 'index'])
    ->middleware(['authn', 'authn.requires_passkey:verified']);

Route::get('/account/passkey-required-area', [Controller::class, 'show'])
    ->middleware(['authn', 'authn.requires_passkey:enrolled']);

The default mode is configurable via authn.passkey.default_strict_mode (env AUTHN_PASSKEY_DEFAULT_STRICT_MODE).

@authnHasEnterpriseAccount

Renders the inner block based on the active session's enterprise-SSO signal. Two modes for the no-argument form:

  • verified (default) — matches when the current session was authenticated through an enterprise IdP (VerifiedClaims->enterpriseConnectionId !== null).
  • linked — matches whenever the user has at least one linked EnterpriseAccount, whether or not the current session is enterprise-SSO.
@authnHasEnterpriseAccount
    <p>You signed in via enterprise SSO.</p>
@else
    <a href="/sign-in/enterprise-sso">Sign in with your organization</a>
@endauthnHasEnterpriseAccount

With a connection-id argument, the directive matches when the active session's enterpriseConnectionId equals it OR when the user has a linked EnterpriseAccount for that connection:

@authnHasEnterpriseAccount('entcon_01HKX9SY9V7H7TF8C8K7J9X4ZA')
    <a href="/admin/enterprise">Org admin</a>
@endauthnHasEnterpriseAccount

The matching route middleware authn.requires_enterprise_sso[:<mode>] fail-closes by redirecting unauthenticated requests to authn.url.sign_in and signed-in-but-under-authenticated requests to authn.enterprise_sso.redirect_url:

Route::get('/workspace', [WorkspaceController::class, 'show'])
    ->middleware(['authn', 'authn.requires_enterprise_sso']);            // verified (default)

Route::get('/workspace/admin', [WorkspaceAdminController::class, 'show'])
    ->middleware(['authn', 'authn.requires_enterprise_sso:verified']);

Route::get('/workspace/profile', [ProfileController::class, 'show'])
    ->middleware(['authn', 'authn.requires_enterprise_sso:linked']);

The default mode is configurable via authn.enterprise_sso.default_strict_mode (env AUTHN_ENTERPRISE_SSO_DEFAULT_STRICT_MODE).

Facade methods

use Authn\Sdk\Laravel\Facades\Authn;

$claims = Authn::auth();              // ?VerifiedClaims — null outside an authn-authenticated request
Authn::hasRole('org:admin');          // bool
Authn::hasPermission('org:foo:bar');  // bool
Authn::hasPasskey();                  // bool — defaults to the configured strict mode ("verified")
Authn::hasPasskey('enrolled');        // bool — true when the user has any verified passkey
Authn::hasEnterpriseSso();            // bool — defaults to the configured strict mode ("verified")
Authn::hasEnterpriseSso('linked');    // bool — true when the user has any linked EnterpriseAccount

Development

composer install
composer test       # Pest + Orchestra Testbench
composer phpstan    # PHPStan + Larastan @ level 10
composer pint       # code style check
composer pint:fix   # apply fixes

CI runs the full suite on the PHP 8.2 / 8.3 / 8.4 / 8.5 × Laravel 11 / 12 matrix.

License

AGPL-3.0-only.