🔑 Simple Keycloak Guard for Laravel / Lumen

Overview

 

Simple Keycloak Guard for Laravel / Lumen

This package helps you authenticate users on a Laravel API based on JWT tokens generated from Keycloak Server.

Requirements

✔️ I`m building an API with Laravel.

✔️ I will not use Laravel Passport for authentication, because Keycloak Server will do the job.

✔️ The frontend is a separated project.

✔️ The frontend users authenticate directly on Keycloak Server to obtain a JWT token. This process have nothing to do with the Laravel API.

✔️ The frontend keep the JWT token from Keycloak Server.

✔️ The frontend make requests to the Laravel API, with that token.

💔 If your app does not match requirements, probably you are looking for https://socialiteproviders.com/Keycloak or https://github.com/Vizir/laravel-keycloak-web-guard

The flow

  1. The frontend user authenticates on Keycloak Server

  2. The frontend user obtains a JWT token.

  3. In another moment, the frontend user makes a request to some protected endpoint on a Laravel API, with that token.

  4. The Laravel API (through Keycloak Guard) handle it.

    • Verify token signature.
    • Verify token structure.
    • Verify token expiration time.
    • Verify if my API allows resource access from token.
  5. If everything is ok, find the user on database and authenticate it on my API.

  6. Return response

Install

Laravel / Lumen

Require the package

composer require robsontenorio/laravel-keycloak-guard

Lumen only

Register the provider in your boostrap app file bootstrap/app.php

Add the following line in the "Register Service Providers" section at the bottom of the file.

$app->register(\KeycloakGuard\KeycloakGuardServiceProvider::class);

For facades, uncomment $app->withFacades(); in your boostrap app file bootstrap/app.php

Configuration

Keycloak Guard

The Keycloak Guard configuration can be handled from Laravel .env file. ⚠️ Be sure all strings are trimmed.

Optionally you can publish the config file.

php artisan vendor:publish  --provider="KeycloakGuard\KeycloakGuardServiceProvider"
<?php

return [  
  'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', null),

  'load_user_from_database' => env('KEYCLOAK_LOAD_USER_FROM_DATABASE', true),

  'user_provider_credential' => env('KEYCLOAK_USER_PROVIDER_CREDENTIAL', 'username'),

  'token_principal_attribute' => env('KEYCLOAK_TOKEN_PRINCIPAL_ATTRIBUTE', 'preferred_username'),

  'append_decoded_token' => env('KEYCLOAK_APPEND_DECODED_TOKEN', false),

  'allowed_resources' => env('KEYCLOAK_ALLOWED_RESOURCES', null)
];

✔️ realm_public_key

Required.

The Keycloak Server realm public key (string).

How to get realm public key? Click on "Realm Settings" > "Keys" > "Algorithm RS256" Line > "Public Key" Button

✔️ load_user_from_database

Required. Default is true.

If you do not have an users table you must disable this.

It fetchs user from database and fill values into authenticated user object. If enabled, it will work together with user_provider_credential and token_principal_attribute.

✔️ user_provider_credential

Required. Default is username.

The field from "users" table that contains the user unique identifier (eg. username, email, nickname). This will be confronted against token_principal_attribute attribute, while authenticating.

✔️ token_principal_attribute

Required. Default is preferred_username.

The property from JWT token that contains the user identifier. This will be confronted against user_provider_credential attribute, while authenticating.

✔️ append_decoded_token

Default is false.

Appends to the authenticated user the full decoded JWT token ($user->token). Useful if you need to know roles, groups and other user info holded by JWT token. Even choosing false, you can also get it using Auth::token(), see API section.

✔️ allowed_resources

Required

Usually you API should handle one resource_access. But, if you handle multiples, just use a comma separated list of allowed resources accepted by API. This attribute will be confronted against resource_access attribute from JWT token, while authenticating.

Laravel Auth

Changes on config/auth.php

...
'defaults' => [
        'guard' => 'api', # <-- For sure, i`m building an API
        'passwords' => 'users',
    ],
    
    ....
    
    'guards' => [
        'api' => [
            'driver' => 'keycloak', # <-- Set the API guard driver to "keycloak"
            'provider' => 'users',
        ],
    ],

Laravel Routes

Just protect some endpoints on routes/api.php and you are done!

// public endpoints
Route::get('/hello', function () {
    return ':)';
});

// protected endpoints
Route::group(['middleware' => 'auth:api'], function () {
    Route::get('/protected-endpoint', 'SecretController@index');
    // more endpoints ...
});

Lumen Routes

Just protect some endpoints on routes/web.php and you are done!

// public endpoints
$router->get('/hello', function () {
    return ':)';
});

// protected endpoints
$router->group(['middleware' => 'auth'], function () {
    $router->get('/protected-endpoint', 'SecretController@index');
    // more endpoints ...
});

API

Simple Keycloak Guard implements Illuminate\Contracts\Auth\Guard. So, all Laravel default methods will be available. Ex: Auth::user() returns the authenticated user.

Default methods:

  • check()
  • guest()
  • user()
  • id()
  • validate()
  • setUser()

Keycloak Guard methods:

  • token()

Ex: Auth::token() returns full decoded JWT token from authenticated user

  • hasRole('some-resource', 'some-role'): Check if the authenticated user has especific role into a resource.

Ex: Whit this payload:

'resource_access' => [
  'myapp-backend' => [
      'roles' => [
        'myapp-backend-role1',
        'myapp-backend-role2'
      ]
  ],
  'myapp-frontend' => [
    'roles' => [
      'myapp-frontend-role1',
      'myapp-frontend-role2'
    ]
  ]
]
Auth::hasRole('myapp-backend', 'myapp-backend-role1') // true
Auth::hasRole('myapp-frontend', 'myapp-frontend-role1') // true
Auth::hasRole('myapp-backend', 'myapp-frontend-role1') // false

Contact

Twitter @robsontenorio

Comments
  • Feature: allow create / update users on the API users table with autenticated token

    Feature: allow create / update users on the API users table with autenticated token

    This PR aims to, as an option, allow the API to create / update the authenticated user in the local users table. To achieve this, its possible to inform a custom method on a custom UserProvider, that will be called instead retrieveByCredentials and will receive the complete decoded token as parameter, not just the credentials.

    opened by frital 13
  • Requests to secured api route fail very strangely

    Requests to secured api route fail very strangely

    I'm trying to implement a similar setup to the example. My frontend authenticates with KeyCloak and I make a test request to the backend. My test request is GET /api/v1/bla and has a header Authorization: Bearer eyJhbG.... When I refresh my browser, my frontend identifies the KC session as active and working. My user is identified via email in Laravel and in the KC.

    My test route:

      Route::group(['middleware' => 'auth:api'], function () {
          Route::get('bla', function () {
              Log::debug("boo");
              return JsonResponse::create(["T" => 23], 200);
          });
    });
    

    My network tab: image

    So, the 1st time I call bla, it timeouts after ~3.1 minutes. Then the 2nd time it works (wtf) and then the 3rd time it responds with {"code":500,"message":"[Keycloak Guard] Expired token"}.

    Could it be that I have configured something somewhere incorrectly? I don't know where to look or where to start, I'm completely lost. I want to handle all authorization myself in the Laravel app, I just want to authenticate the user with KC and map them to a Laravel user.

    opened by Req 11
  • ClientID & Secret rather than realm_public_key

    ClientID & Secret rather than realm_public_key

    Hello, I am using the latest version of keycloak (4.3.0.Final) which encourages ClientID & Secret with a URL redirect.

    I do not see where I would configure this into your application?

    The use of a Realm Public Key is not encouraged as our IT security policy calls for periodic rotation of Realm Key Pairs. It would be much better to configure my Laravel Application on a per-client, rather than per-realm basis. Are you looking to implement this into this code?

    What am I missing?

    opened by lancemitchell 10
  • Prepare to update all

    Prepare to update all

    Resolves #75 #70 #66 #67 #63 #64

    • Update all dependencies
    • Add leeway option to prevent token excepetion due to network issues
      • #38
    • Add option api_token to get an alternative token
      • #64
      • #63
    • Add option ignore_resources_validation to ignore resource validation
      • #66
      • #67
    • Add phpcsfixer
    • Add github actions
    • Add VSCODE devcontainer
    • Update README

    Inspired by #75 . Big thansks to @Nizari effort !

    Just opted to not include all boilerplate from #75 in order to keep things simple. After this MR I am able to review anothers MRs.

    opened by robsontenorio 6
  • "message": "[Keycloak Guard] Signature verification failed"

    Hi, I am using Laravel 8.37.0 and Vuejs. I have installed and configured this package as per the instruction given in the read-me file. But I am getting following error message while calling from postman. Kindly check and help me to resolve this issue.

    "message": "[Keycloak Guard] Signature verification failed", "exception": "KeycloakGuard\Exceptions\TokenException", "file": "/opt/lampp/htdocs/archive/officer-hwn-web-robsontenorio/vendor/robsontenorio/laravel-keycloak-guard/src/KeycloakGuard.php", "line": 41,

    In my auth.php I have

    'defaults' => [ 'guard' => 'api', 'passwords' => 'users', ], 'guards' => [ 'api' => [ 'driver' => 'keycloak', 'provider' => 'users', // 'hash' => false, ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], In my config/keycloak.php i have

    return [ 'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', "MII####"),

    'load_user_from_database' => env('KEYCLOAK_LOAD_USER_FROM_DATABASE', true),

    'user_provider_credential' => env('KEYCLOAK_USER_PROVIDER_CREDENTIAL', 'email'),

    'token_principal_attribute' => env('KEYCLOAK_TOKEN_PRINCIPAL_ATTRIBUTE', 'username'),

    'append_decoded_token' => env('KEYCLOAK_APPEND_DECODED_TOKEN', false),

    'allowed_resources' => env('KEYCLOAK_ALLOWED_RESOURCES', null) ];

    In Postman Request

    Method : Get URL : 127.0.0.1:8000/api/v1/protected-endpoint Authorization : Type :Bearer_token , Token {{access_token}} Body : username : "###user@###corp.com" password : test1 grant_type:password

    opened by viveksudalai 6
  • "[Keycloak Guard] Cannot handle token prior to" message

    Hi

    I got the error message of

    KeycloakGuard\Exceptions\TokenException
    [Keycloak Guard] Cannot handle token prior to 2020-12-02T01:54:16+0000
    

    when try to verify the access_token.

    My route is

    Route::group(['middleware' => 'auth:api'], function () {
        Route::get('/protected', function () {
            return Auth::token();
        });
    });
    
    

    The route did returns full decoded JWT token from authenticated user only after several seconds after the token was generated.

    I am not sure whether I need to clean cache or do something to make it work.

    The error displayed on vendor/robsontenorio/laravel-keycloak-guard/src/KeycloakGuard.php: Line 41

        try {
    
          $this->decodedToken = Token::decode($this->request->bearerToken(), $this->config['realm_public_key']);
    
        } catch (\Exception $e) {
    
          throw new TokenException($e->getMessage());
    
        }
    
    • I am using Laravel 8
    opened by haizad 6
  • (Lumen) Publising issue

    (Lumen) Publising issue

    Configuration and user providers are not published.

    php artisan vendor:publish --provider="KeycloakGuard\KeycloakGuardServiceProvider" "Nothing to publish for tag []."

    opened by Momodedf 6
  • [Keycloak Guard] Algorithm not allowed

    [Keycloak Guard] Algorithm not allowed

    Hi, when I try to retrieve some resources from my api laravel application , I have the following error :

    "message": "[Keycloak Guard] Algorithm not allowed", "exception": "KeycloakGuard\Exceptions\TokenException", "file": "C:\*******\keycloak-laravel\vendor\robsontenorio\laravel-keycloak-guard\src\KeycloakGuard.php", "line": 41,

    Do you have any idea about this ? Thanks for you answer.

    Charlie

    opened by Shawlee76 4
  • [Keycloak error] Wrong number of segements

    [Keycloak error] Wrong number of segements

    I've successfully obtained an access token as follow: eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSNnpnSXF6dHp4aVlVY25aR2JmOHBfLU5jams1VmhqNGprUzNndXdCb0pJIn0.eyJleHAiOjE2MjAyNTEyMTUsImlhdCI6MTYxNzY1OTIxNSwiYXV0aF90aW1lIjoxNjE3NjU3NDcwLCJqdGkiOiJiMDVjY2NjMi03N2RjLTQ3NmEtOGY2NS1jZDA1ZTc3OWFkOTUiLCJpc3MiOiJodHRwOi8vZGV2LnNoYW5iZS5pbzo4MDgwL2F1dGgvcmVhbG1zL3NoYW5iZSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJkNTVkNzZhYS0wODY3LTQwZGUtYTVmNS00NTU4MWMyODhkNTIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJzaGFuYmUtYXBwIiwic2Vzc2lvbl9zdGF0ZSI6ImVlOWU4YzM3LWRlYmEtNDU5YS05OThiLWNhOWQ1OGRlMTZkZSIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgb2ZmbGluZV9hY2Nlc3MgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6Ik1laHJkYWQgU2hva3JpIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiY29kZS5wb2V0OTVAZ21haWwuY29tIiwiZ2l2ZW5fbmFtZSI6Ik1laHJkYWQiLCJmYW1pbHlfbmFtZSI6IlNob2tyaSIsImVtYWlsIjoiY29kZS5wb2V0OTVAZ21 apparently firebase/jwt checks for . to segment token and this token has 1 . so 2 segments is generated(it should be 3)
    Does keycloak has a special config to generate 3 segments? as far as I know this is an open id connect access token And I don't have any idea why it doesn't have 2 dots in it.

    opened by mehrdad-shokri 4
  • Route [login] not defined.

    Route [login] not defined.

    I tried out your implementation and I am having some trouble.

    Symfony\Component\Routing\Exception\RouteNotFoundException Route [login] not defined. http://localhost:8000/api/protected-endpoint latest laravel

    I have made default as requested (for API) and using firecamp for testing. I wonder what I am doing wrong.

    What is the expected behaviour to reach a site which is protected and not authenticated? Do you have a sample implementation at hand which I can look at?

    I would be using laravel as an API server only and use reactjs as UI, but after a few days research I might move away from laravel (not that openidconnect client friendly by default). You are my last hope:) PHP can natively run on hosting servers hence my choice instead of an expressjs or some other which requires at least a docker/kubernetes/etc

    I hope you can answer fast though my project is private so non-commercial.

    opened by bitvilag 4
  • Call to undefined method KeycloakGuard\KeycloakGuard::attempt()

    Call to undefined method KeycloakGuard\KeycloakGuard::attempt()

    Hello ,

    I'm getting this error while trying to connect to my small application.

    What did I do wrong ?

    Here is my project : https://github.com/Jenkiiz/test_guard.git

    Thank you !

    opened by ghost 4
  •  Change the error message 500 for a wrong token

    Change the error message 500 for a wrong token

    If I enter the token incorrectly I get an error message [Guard Token] etc.... and the call goes into error 500. But this in the case of the frontend should receive a 401. Is there a way to fix this problem

    opened by palla451 0
  • Unable to extend the guard

    Unable to extend the guard

    All KeycloakGuard's properites and methods are private. When we want to overwrite the class to add some additional logic it makes it more complicated. The question is if private is by design or can we change the props to be protected.

    We will be happy to provide a PR with the changes if you accept it.

    Thanks for the grat package!

    opened by pkarczmarczyk94 5
Releases(1.4.0)
Owner
Robson Tenório
Laravel, Vue, Vuetify, InertiaJs, GraphQL.
Robson Tenório
HTTP Basic Auth Guard for Lumen 5.x

HTTP Basic Auth Guard HTTP Basic Auth Guard is a Lumen Package that lets you use basic as your driver for the authentication guard in your application

Christopher Lass 40 Nov 11, 2022
Laravel Auth guard for FusionAuth JWT

Laravel FusionAuth JWT Implement an Auth guard for FusionAuth JWTs in Laravel. It ships with also a middleware to check against the user role. Install

Theraloss 7 Feb 21, 2022
An OAuth 2.0 bridge for Laravel and Lumen [DEPRECATED FOR LARAVEL 5.3+]

OAuth 2.0 Server for Laravel (deprecated for Laravel 5.3+) Note: This package is no longer maintaned for Laravel 5.3+ since Laravel now features the P

Luca Degasperi 2.4k Jan 6, 2023
🔐 JSON Web Token Authentication for Laravel & Lumen

Documentation Documentation for 1.* here For version 0.5.* See the WIKI for documentation. Supported by Auth0 If you want to easily add secure authent

Sean Tymon 10.7k Dec 31, 2022
Making Laravel Passport work with Lumen

lumen-passport Making Laravel Passport work with Lumen A simple service provider that makes Laravel Passport work with Lumen Dependencies PHP >= 5.6.3

Denis Mysenko 651 Dec 1, 2022
JWT auth for Laravel and Lumen

JWT Artisan Token auth for Laravel and Lumen web artisans JWT is a great solution for authenticating API requests between various services. This packa

⑅ Generation Tux ⑅ 141 Dec 21, 2022
🔐 JSON Web Token Authentication for Laravel & Lumen

Credits This repository it a fork from original tymonsdesigns/jwt-auth, we decided to fork and work independent because the original one was not being

null 490 Dec 27, 2022
An invisible reCAPTCHA package for Laravel, Lumen, CI or native PHP.

Invisible reCAPTCHA Why Invisible reCAPTCHA? Invisible reCAPTCHA is an improved version of reCAPTCHA v2(no captcha). In reCAPTCHA v2, users need to cl

Albert Chen 578 Nov 30, 2022
Authentication REST-API built with Lumen PHP Framework

Authentication REST-API built with Lumen PHP Framework Laravel Lumen is a stunningly fast PHP micro-framework for building web applications with expre

Hüseyin Yağlı 1 Oct 12, 2021
A Simple method to create laravel authentication for an existing laravel project.

Laravel Simple Auth A Simple method to create laravel authentication for an existing laravel project. Indroduction Why I created this kind of package?

Dasun Tharanga 10 Dec 14, 2021
Simple JWT Auth support for Laravel PHP Framework

Laravel JWT Simple JWT Auth for Laravel PHP Framework using Firebase JWT under the hood. Installation Standard Composer package installation: composer

Ricardo Čerljenko 34 Nov 21, 2022
Simple PASETO Auth support for Laravel PHP Framework

Laravel PASETO Simple PASETO Auth for Laravel PHP Framework using paragonie/paseto under the hood. Installation Standard Composer package installation

Ricardo Čerljenko 9 Jan 11, 2022
Braindead simple social login with Laravel and Eloquent.

Important: This package is not actively maintained. For bug fixes and new features, please fork. Eloquent OAuth Use the Laravel 4 wrapper for easy int

Adam Wathan 374 Dec 21, 2022
Simple readonly LDAP authentication with Laravel 5.2

ldap-auth Very basic READ ONLY LDAP authentication driver for Laravel 5.2+ Look HERE for the package for Laravel 5.1. However, only the 5.2 Version wi

Stan 26 Jun 20, 2021
A simple, safe magic login link generator for Laravel

Laravel Passwordless Login A simple, safe magic login link generator for Laravel This package provides a temporary signed route that logs in a user. W

gro.sv 689 Dec 25, 2022
Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.

Introduction Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs. Official Documentation Documentation for Sanctum

The Laravel Framework 2.4k Dec 30, 2022
A simple two factor authentication for laravel applications

Laravel 2fa A simple two factor authentication for laravel applications. Installation Require via composer Update database Replace authentication trai

Rezkonline 1 Feb 9, 2022
This is a simple laravel authentication built with livewire jetstream.

About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experie

Emmanuel Dada 1 Feb 4, 2022
Laravel Passport is an OAuth2 server and API authentication package that is simple and enjoyable to use

Introduction Laravel Passport is an OAuth2 server and API authentication package that is simple and enjoyable to use. Official Documentation Documenta

The Laravel Framework 3.1k Dec 31, 2022