multitech-osp/laravel-keycloak

This package is abandoned and no longer maintained. No replacement package was suggested.

Laravel Keycloak for laravel

v1.2.5 2025-01-03 13:19 UTC

README

This library provides integration between Laravel and Keycloak for handling authentication and authorization.

Update Guide

Upgrading to v1.2.0 from v1.1.0

In the configuration(config/keycloak.php) file prefix is ​​no longer an array, from now on it is a string.

  • you need to change the name of the roles.prefixes key to roles.prefix

Installation

To install the library, use Composer:

composer require multitech-osp/laravel-keycloak

Add Service Provider

After installing the package, add the service provider in config/app.php

'providers' => [
    ...
    \MultitechOsp\LaravelKeycloak\LaravelKeycloakServiceProvider::class
],

Publishing Configuration

After installing the package, publish the configuration file using the following Artisan command:

php artisan vendor:publish --provider="MultitechOsp\LaravelKeycloak\LaravelKeycloakServiceProvider"

Configuration

After publishing, the configuration file will be located at config/keycloak.php. You can set the necessary Keycloak parameters in this file or directly in your .env file.

Example of configuration settings in the .env file:

KEYCLOAK_URL=http://your-keycloak-url
KEYCLOAK_REALM=your-realm
KEYCLOAK_SCOPE=openid
KEYCLOAK_CLIENT_ID=your-client-id
KEYCLOAK_LOAD_MODEL=false
KEYCLOAK_MODEL_ID=id

Configuration File Example

return [
    'url' => env('KEYCLOAK_URL'),
    'realm' => env('KEYCLOAK_REALM'),
    'scope' => env('KEYCLOAK_SCOPE', 'openid'),
    'client_id' => env('KEYCLOAK_CLIENT_ID', 'auth'),

    'model_load' => env('KEYCLOAK_LOAD_MODEL', false),
    'model_id' => env('KEYCLOAK_MODEL_ID', 'id'),

     'roles' => [
        'prefix' => env('KEYCLOAK_ROLES_PREFIX', 'sac'),
        'admin' => env('KEYCLOAK_ROLES_ADMIN', 'admin'),
        'permissions' => [
            'route_name' => [
                'keycloak_role_permission'
            ]
        ],
        'ignore_routes' => [
            'route_name'
        ]
    ],

    'routes' => [
        'prefix' => env('KEYCLOAK_ROUTE_PREFIX', 'api/v1'),
        'middleware' => env('KEYCLOAK_ROUTE_MIDDLEWARE', 'auth:api'),
    ],

    'manage' => [
        'client_id' => env('KEYCLOAK_MANAGE_CLIENT_ID', 'admin-cli'),
        'username' => env('KEYCLOAK_MANAGE_USERNAME', 'admin-realm'),
        'password' => env('KEYCLOAK_MANAGE_PASSWORD'),
        'subgroups_uuid' => [
            'admin' => env('KEYCLOAK_MANAGE_SUBGROUPS_ADMIN'),
            'view' => env('KEYCLOAK_MANAGE_SUBGROUPS_VIEW'),
        ]
    ],
];

Setting Up the Guard

To use the Keycloak guard, you need to configure it in your config/auth.php configuration file located in the config directory:

'guards' => [
    ...,

    'api' => [
        'driver' => 'keycloak',
        'provider' => 'users',
    ],
],

Middleware

The keycloak-role middleware will be published and should be used to validate permissions using Keycloak.

Usage Example

Route::middleware('keycloak-role:api')
      ->apiResource('products', 'ProductController', ['except' => ['delete']]);

Configuration

To configure route permissions, use the config\keycloak.php file. It has the following structure:

'roles' => [
    'prefix' => env('KEYCLOAK_ROLES_PREFIX', 'sac'),
    'admin' => env('KEYCLOAK_ROLES_ADMIN', 'admin'),
    'permissions' => [
        'route_name' => [
            'keycloak_role_permission'
        ]
    ],
    'ignore_routes' => [
        'route_name'
    ]
],

Parameter Descriptions

  • roles.prefix: Permission prefix.
  • roles.admin: Name of the permission that has general admin access to the application.
  • roles.permissions: Array containing the names of the routes and the permissions that have access to them. The array index is the route name, and its value is an array with the Keycloak permissions that can access it.

Example of Permissions

The lib will take the route name and replace the "." by "-" and add the prefix. Ex: products.show to sac:products-show.

If the permission does not follow this pattern, it must be defined in the permissions array

'permissions' => [
    'products.show' => [
        'sac-admin:products-show'
    ]
]
  • roles.ignore_routes: An array containing the names of the routes that do not require permission to be accessed.

Available methods

MultitechOsp\LaravelKeycloak\KeycloakService.php

public function login(string $username = '', string $password = ''): array;
public function logout(string $accessToken, string $refreshToken): bool;
public function refreshToken(string $refreshToken): array;
public function user(string $accessToken): array;

Commands

The keycloak:sync-permissions command will synchronize permissions based on the routes that the keycloak-role middleware has

php artisan keyclock:sync-permissions

Routes

POST config('keycloak.routes.prefix')/auth/login
body:

{
    "username": "admin",
    "password": "admin"
}

POST config('keycloak.routes.prefix')/auth/logout
header: Authorization Bearer {access_token}
body:

{
    "refresh_token": "your refresh token"
}

POST config('keycloak.routes.prefix')/auth/refresh
body:

{
    "refresh_token": "your refresh token"
}

GET config('keycloak.routes.prefix')/auth/verify/token
header: Authorization Bearer {access_token}

Swagger-PHP

If you need swaggers docs see bellow code shortcut.

    /**
     * @OA\Post(
     *     path="/api/v1/auth/login",
     *     tags={"Auth"},
     *     summary="Auth login",
     *     operationId="auth.login",
     *     @OA\RequestBody(
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                   @OA\Property(
     *                      property="username",
     *                      type="string",
     *                      example="admin",
     *                      description="Username or email"
     *                  ),
     *                   @OA\Property(
     *                      property="password",
     *                      type="string",
     *                      description="Password"
     *                  ),
     *              ),
     *         )
     *     ),
     *
     *     @OA\Response(
     *          response=200,
     *          description="successful operation",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                   @OA\Property(property="token",
     *                      type="string",
     *                      example="eyJ0eXAiOiJKV1QiLCJhbG...",
     *                      description="Token"
     *                  ),
     *                  @OA\Property(property="token_type",
     *                      type="string",
     *                      example="Bearer ",
     *                      description="Token type"
     *                  ),
     *                  @OA\Property(property="expires_in",
     *                      type="string",
     *                      example="2020-02-01 12:22:22",
     *                      description="expires_in"
     *                  ),
     *              ),
     *          ),
     *      ),
     *      @OA\Response(
     *         response=422,
     *         description="Validation"
     *      ),
     *      @OA\Response(
     *         response=400,
     *         description="The given data was invalid"
     *      ),
     * )
     */
    /**
     * @OA\Post(
     *     path="/api/v1/auth/refresh",
     *     tags={"Auth"},
     *     summary="Auth refresh",
     *     operationId="auth.refresh",
     *     @OA\RequestBody(
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                   @OA\Property(
     *                      property="refresh_token",
     *                      type="string",
     *                      example="eyJhbGciOiJIUzI1NiI...",
     *                      description="Refresh token"
     *                  ),
     *              ),
     *         )
     *     ),
     *     @OA\Response(
     *          response=200,
     *          description="successful operation",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                   @OA\Property(
     *                      property="refresh_token",
     *                      type="string",
     *                      example="eyJhbGciOiJIUzI1NiI...",
     *                      description="Refresh token"
     *                  ),
     *              ),
     *          ),
     *      ),
     *      @OA\Response(
     *         response=422,
     *         description="Validation"
     *      ),
     *      @OA\Response(
     *         response=400,
     *         description="The given data was invalid"
     *      ),
     * )
     */
    /**
     * @OA\Post(
     *     path="/api/v1/auth/logout",
     *     tags={"Auth"},
     *     summary="Auth logout",
     *     operationId="auth.logout",
     *     security={{"token":{}}},
     *     @OA\RequestBody(
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                   @OA\Property(
     *                      property="refresh_token",
     *                      type="string",
     *                      example="eyJhbGciOiJIUzI1NiI...",
     *                      description="Refresh token"
     *                  ),
     *              ),
     *         )
     *     ),
     *      @OA\Response(
     *          response=200,
     *          description="successful operation",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                   @OA\Property(
     *                      property="boolean",
     *                      type="boolean",
     *                      example="true",
     *                      description="If invalidation token return true"
     *                  ),
     *              ),
     *          ),
     *      ),
     *      @OA\Response(
     *         response=400,
     *         description="The given data was invalid"
     *      ),
     * )
     */
    /**
     * @OA\Get(
     *     path="/api/v1/auth/verify-token",
     *     tags={"Auth"},
     *     summary="Auth verify-token",
     *     operationId="auth.verify-token",
     *     security={{"token":{}}},
     *     @OA\RequestBody(
     *          @OA\MediaType(
     *              mediaType="application/json",
     *         )
     *     ),
     *      @OA\Response(
     *          response=200,
     *          description="successful operation",
     *
     *          @OA\MediaType(
     *              mediaType="application/json",
     *          ),
     *      ),
     *      @OA\Response(
     *         response=400,
     *         description="The given data was invalid"
     *      ),
     * )
     */

Publish Library new version in Packagist

Just create a new tag and it will be made available automatically

License

This library is open-source software licensed under the MIT license.

Contributing

Contributions are welcome! Please submit a pull request or open an issue to discuss potential changes.

Support

For support, please open an issue in this repository.

This README provides an overview of how to install, configure, and use the Laravel Keycloak integration library. Be sure to replace placeholder values with your actual Keycloak settings.