mohsen-mhm/laravel-keycloak-auth

Laravel package for Keycloak authentication integration using SocialiteProviders

v1.5.0 2025-09-16 06:31 UTC

This package is auto-updated.

Last update: 2025-09-16 06:32:05 UTC


README

A Laravel package that provides seamless integration with Keycloak for authentication using SocialiteProviders. This package enables Laravel applications to authenticate users via a self-hosted Keycloak server with minimal configuration.

Features

  • Easy integration with self-hosted Keycloak servers
  • Automatic user creation and synchronization
  • Web authentication for traditional Laravel Blade applications
  • API authentication for separate frontend applications (React, Vue, etc.)
  • Configurable authentication routes
  • Middleware for protecting routes
  • Laravel Sanctum token support for API authentication
  • Support for multiple Laravel versions (9.x, 10.x, 11.x, 12.x)
  • Built on top of Laravel Socialite and SocialiteProviders

Installation

Install the package via Composer:

composer require mohsen-mhm/laravel-keycloak-auth

For API authentication, install either Laravel Sanctum or Laravel Passport:

Option A: Laravel Sanctum (recommended for SPAs)

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate

Option B: Laravel Passport (full OAuth2 server)

composer require laravel/passport
php artisan vendor:publish --tag=passport-migrations
php artisan migrate
php artisan passport:install

Publish the configuration file:

php artisan vendor:publish --tag=keycloak-auth-config

Environment Configuration

Add the following environment variables to your .env file:

KEYCLOAK_SERVER_URL=http://localhost:8080
KEYCLOAK_REALM=your-realm
KEYCLOAK_CLIENT_ID=your-client-id
KEYCLOAK_CLIENT_SECRET=your-client-secret
KEYCLOAK_AUTO_CREATE_USERS=true
KEYCLOAK_VERIFY_SSL=true

# Optional: API callback URL for separate frontends
KEYCLOAK_API_CALLBACK_URL=http://your-frontend.com/auth/callback

Database Migration

Add a keycloak_id column to your users table:

Schema::table('users', function (Blueprint $table) {
    $table->string('keycloak_id')->nullable()->unique();
});

Usage

This package supports two authentication modes:

1. Web Authentication (Traditional Laravel with Blade)

Basic Authentication

Redirect users to Keycloak for authentication:

// In your controller
return redirect()->route('keycloak.login');

Or create a login link in your view:

<a href="{{ route('keycloak.login') }}">Login with Keycloak</a>

Protecting Routes

Use the provided middleware to protect your routes:

// In your routes/web.php
Route::middleware(['keycloak.auth'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::get('/profile', [ProfileController::class, 'show']);
});

Logout

Logout from both Laravel and Keycloak:

// In your controller
return redirect()->route('keycloak.logout');

Or create a logout link:

<form method="POST" action="{{ route('keycloak.logout') }}">
    @csrf
    <button type="submit">Logout</button>
</form>

2. API Authentication (Separate Frontend Applications)

Perfect for React, Vue, Angular, or mobile applications that need to authenticate with Keycloak and get API tokens.

Available API Endpoints

  • GET /api/auth/keycloak/login-url - Get Keycloak login URL
  • POST /api/auth/keycloak/callback - Handle OAuth callback and get tokens
  • POST /api/auth/keycloak/refresh - Refresh expired tokens
  • POST /api/auth/keycloak/logout - Logout and revoke tokens
  • GET /api/auth/keycloak/user - Get authenticated user info

Frontend Integration Example

Step 1: Get Login URL

const response = await fetch('/api/auth/keycloak/login-url?redirect_uri=http://your-frontend.com/callback');
const { login_url, state } = await response.json();

// Redirect user to Keycloak
window.location.href = login_url;

Step 2: Handle Callback (when user returns from Keycloak)

// Extract code and state from URL parameters
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');

// Exchange code for tokens
const response = await fetch('/api/auth/keycloak/callback', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    code: code,
    state: state,
    redirect_uri: 'http://your-frontend.com/callback'
  })
});

const { access_token, keycloak_token, refresh_token, user } = await response.json();

// Store tokens securely
localStorage.setItem('access_token', access_token);
localStorage.setItem('refresh_token', refresh_token);

Step 3: Make Authenticated API Calls

const response = await fetch('/api/auth/keycloak/user', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
    'Accept': 'application/json'
  }
});

const userData = await response.json();

Step 4: Refresh Tokens

const response = await fetch('/api/auth/keycloak/refresh', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    refresh_token: localStorage.getItem('refresh_token')
  })
});

const { access_token, refresh_token } = await response.json();
// Update stored tokens

Step 5: Logout

await fetch('/api/auth/keycloak/logout', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    refresh_token: localStorage.getItem('refresh_token')
  })
});

// Clear stored tokens
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');

Protecting API Routes

You have two options for protecting API routes:

Option 1: Use Keycloak API Middleware (validates Keycloak tokens directly)

// In your routes/api.php
Route::middleware(['keycloak.api'])->group(function () {
    Route::get('/protected-data', [ApiController::class, 'getData']);
    Route::post('/create-resource', [ApiController::class, 'create']);
});

// Access Keycloak user info in your controller
class ApiController extends Controller
{
    public function getData(Request $request)
    {
        $keycloakUser = $request->get('keycloak_user');
        // $keycloakUser contains: sub, email, name, preferred_username, etc.
        
        return response()->json(['data' => 'protected data']);
    }
}

Option 2: Use Laravel Sanctum middleware (validates Laravel API tokens)

// In your routes/api.php
Route::middleware(['auth:sanctum'])->group(function () {
    Route::get('/protected-data', [ApiController::class, 'getData']);
    Route::post('/create-resource', [ApiController::class, 'create']);
});

// Access Laravel user in your controller
class ApiController extends Controller
{
    public function getData(Request $request)
    {
        $user = Auth::user(); // Laravel User model
        
        return response()->json(['data' => 'protected data']);
    }
}

Option 3: Use Laravel Passport middleware (validates OAuth2 tokens)

// In your routes/api.php
Route::middleware(['auth:api'])->group(function () {
    Route::get('/protected-data', [ApiController::class, 'getData']);
    Route::post('/create-resource', [ApiController::class, 'create']);
});

// Access Laravel user in your controller
class ApiController extends Controller
{
    public function getData(Request $request)
    {
        $user = Auth::user(); // Laravel User model
        
        return response()->json(['data' => 'protected data']);
    }
}

When to use which:

  • Use keycloak.api when you want to validate Keycloak tokens directly (stateless, no database lookup)
  • Use auth:sanctum when you need simple API tokens for SPAs and mobile apps
  • Use auth:api when you need full OAuth2 server capabilities with Passport

3. Passport Authentication (OAuth2 Server)

For applications requiring full OAuth2 server capabilities with Laravel Passport.

Available Passport API Endpoints

  • GET /api/auth/keycloak/passport/login-url - Get Keycloak login URL
  • POST /api/auth/keycloak/passport/callback - Handle OAuth callback and get Passport tokens
  • POST /api/auth/keycloak/passport/refresh - Refresh expired tokens
  • POST /api/auth/keycloak/passport/logout - Logout and revoke tokens
  • GET /api/auth/keycloak/passport/user - Get authenticated user info

Passport Frontend Integration Example

Step 1: Get Login URL

const response = await fetch('/api/auth/keycloak/passport/login-url?redirect_uri=http://your-frontend.com/callback');
const { login_url, state } = await response.json();

// Redirect user to Keycloak
window.location.href = login_url;

Step 2: Handle Callback

// Extract code and state from URL parameters
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get('code');
const state = urlParams.get('state');

// Exchange code for Passport OAuth2 tokens
const response = await fetch('/api/auth/keycloak/passport/callback', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    code: code,
    state: state,
    redirect_uri: 'http://your-frontend.com/callback'
  })
});

const { access_token, token_type, expires_in, keycloak_token } = await response.json();

// Store OAuth2 tokens
localStorage.setItem('access_token', access_token);
localStorage.setItem('token_type', token_type);

Step 3: Make Authenticated API Calls

const response = await fetch('/api/auth/keycloak/passport/user', {
  headers: {
    'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
    'Accept': 'application/json'
  }
});

const userData = await response.json();

Using the Service

You can also use the KeycloakAuthService directly:

use MohsenMhm\LaravelKeycloakAuth\Services\KeycloakAuthService;

class YourController extends Controller
{
    public function __construct(private KeycloakAuthService $keycloakAuth)
    {
    }

    public function checkKeycloakStatus()
    {
        if ($this->keycloakAuth->isKeycloakAvailable()) {
            return 'Keycloak is available';
        }
        
        return 'Keycloak is not available';
    }
}

Configuration

The package configuration file (config/keycloak-auth.php) allows you to customize:

  • Keycloak server settings (URL, realm, client credentials)
  • Authentication routes
  • User model and field mapping
  • Redirect URLs after login/logout
  • Auto user creation settings

Keycloak Setup

  1. Create a new client in your Keycloak realm
  2. Set the client type to "OpenID Connect"
  3. Enable "Client authentication"
  4. Add valid redirect URIs:
    • For web authentication: http://your-app.com/auth/keycloak/callback
    • For API authentication: http://your-frontend.com/auth/callback (or your frontend's callback URL)
  5. Configure the client scopes as needed (openid, profile, email)
  6. Set the access type to "confidential"
  7. Enable "Authorization Code Flow" in the authentication flow settings

Requirements

  • PHP 8.2 or higher
  • Laravel 9.x, 10.x, 11.x, or 12.x
  • A running Keycloak server
  • Laravel Sanctum (for simple API authentication) OR Laravel Passport (for OAuth2 server)

Security Considerations

  • Always use HTTPS in production
  • Keep your Keycloak client secret secure
  • Regularly update the package and its dependencies
  • Configure proper CORS settings in Keycloak

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This package is open-sourced software licensed under the MIT license.

Support

If you encounter any issues or have questions, please create an issue on the GitHub repository.