Skip to content

Commit 734a751

Browse files
committed
Testings
0 parents  commit 734a751

20 files changed

+763
-0
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
build
2+
composer.lock
3+
docs
4+
vendor
5+
.php_cs.cache
6+
coverage
7+
.phpunit.result.cache

.travis.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
language: php
2+
3+
php:
4+
- 7.1
5+
- 7.2
6+
7+
env:
8+
matrix:
9+
- COMPOSER_FLAGS="--prefer-lowest"
10+
- COMPOSER_FLAGS=""
11+
12+
before_script:
13+
- travis_retry composer self-update
14+
- travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-source
15+
- composer dump-autoload
16+
17+
script:
18+
- vendor/bin/phpunit
19+
20+
after_success:
21+
- bash <(curl -s https://codecov.io/bash)

README.md

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<p align="center">
2+
<img src="bird.png">
3+
</p>
4+
<p align="center">
5+
&nbsp;
6+
<img src="https://img.shields.io/packagist/v/robsontenorio/laravel-keycloak-guard.svg" />
7+
<img src="https://img.shields.io/packagist/dt/robsontenorio/laravel-keycloak-guard.svg" />
8+
9+
</p>
10+
11+
# Simple Keycloak Guard for Laravel
12+
13+
This package helps you authenticate users on a Laravel API based on JWT tokens generated from **Keycloak Server**.
14+
15+
16+
# Requirements
17+
18+
✔️ I`m building an API with Laravel.
19+
20+
✔️ I will not use Laravel Passport for authentication, because Keycloak Server will do the job.
21+
22+
✔️ I already have an "users table", with unique identifiers, on my database.
23+
24+
✔️ The frontend is a separated project.
25+
26+
✔️ The frontend users authenticate **directly on Keycloak Server** to obtain a JWT token. This process have nothing to do with the Laravel API.
27+
28+
✔️ The frontend keep the JWT token from Keycloak Server.
29+
30+
✔️ The frontend make requests to the Laravel API, with that token.
31+
32+
33+
34+
# The flow
35+
36+
<p align="center">
37+
<img src="flow.png">
38+
</p>
39+
40+
41+
1. The frontend user authenticates on Keycloak Server
42+
43+
1. The frontend user obtains a JWT token.
44+
45+
1. In another moment, the frontend user makes a request to some protected endpoint on a Laravel API, with that token.
46+
47+
1. The Laravel API (through `Keycloak Guard`) handle it.
48+
- Verify token signature.
49+
- Verify token structure.
50+
- Verify token expiration time.
51+
- Verify if my API allows `resource access` from token.
52+
53+
1. If everything is ok, find the user on database and authenticate it on my API.
54+
55+
1. Return response
56+
57+
# Install
58+
59+
Require the package
60+
61+
```
62+
composer require robsontenorio/laravel-keycloak-guard
63+
```
64+
65+
Publish the config file
66+
67+
```
68+
php artisan vendor:publish --provider="KeycloakGuard\KeycloakGuardServiceProvider"
69+
70+
```
71+
72+
# Configuration
73+
74+
## Keycloack Guard
75+
76+
The Keycloak Guard configuration can be handled from Laravel `.env` file. ⚠️ Be sure all strings **are trimmed.**
77+
78+
```php
79+
<?php
80+
81+
return [
82+
'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', null),
83+
84+
'user_provider_credential' => env('KEYCLOAK_USER_PROVIDER_CREDENTIAL', 'username'),
85+
86+
'token_principal_attribute' => env('KEYCLOAK_TOKEN_PRINCIPAL_ATTRIBUTE', 'preferred_username'),
87+
88+
'append_decoded_token' => env('KEYCLOAK_APPEND_DECODED_TOKEN', false),
89+
90+
'allowed_resources' => env('KEYCLOAK_ALLOWED_RESOURCES', null)
91+
];
92+
93+
```
94+
95+
✔️ **realm_public_key**
96+
97+
*Required.*
98+
99+
The Keycloack Server realm public key (string).
100+
101+
✔️ **user_provider_credential**
102+
103+
*Required. Default is `username`.*
104+
105+
106+
Any 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.
107+
108+
✔️ **token_principal_attribute**
109+
110+
*Required. Default is `preferred_username`.*
111+
112+
The property from JWT token that contains the user identifier.
113+
This will be confronted against `user_provider_credential` attribute, while authenticating.
114+
115+
✔️ **append_decoded_token**
116+
117+
*Default is `false`.*
118+
119+
Appends to the authenticated user the full decoded JWT token. Useful if you need to know roles, groups and another user info holded by JWT token. Even choosing `false`, you can also get it using `Auth::token()`, see API section.
120+
121+
✔️ **allowed_resources**
122+
123+
*Required*
124+
125+
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.
126+
127+
## Laravel auth config
128+
129+
Changes on `config/auth.php`
130+
```php
131+
'defaults' => [
132+
'guard' => 'api', # <-- For sure, i`m building an API
133+
'passwords' => 'users',
134+
],
135+
136+
'guards' => [
137+
'api' => [
138+
'driver' => 'keycloak', # <-- Set the API guard to "keycloack"
139+
'provider' => 'users',
140+
],
141+
],
142+
```
143+
144+
# API
145+
146+
Simple Keycloak Guard implements `Illuminate\Contracts\Auth\Guard`. So, all Laravel default methods will be available. Ex: `Auth::user()` returns the authenticated user.
147+
148+
### Default methods:
149+
150+
- check()
151+
- guest()
152+
- user()
153+
- id()
154+
- validate()
155+
- setUser()
156+
157+
158+
### Keycloak Guard methods:
159+
160+
- token()
161+
162+
Ex: `Auth::token()` returns full decoded JWT token from authenticated user
163+
164+
# Contact
165+
166+
Twitter [@robsontenorio](https://twitter.com/robsontenorio)

bird.png

19.4 KB
Loading

composer.json

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "robsontenorio/laravel-keycloak-guard",
3+
"description": "🔑 Simple Keycloak Guard for Laravel",
4+
"keywords": [
5+
"laravel",
6+
"keycloak"
7+
],
8+
"homepage": "https://github.com/robsontenorio/laravel-keycloak-guard",
9+
"license": "MIT",
10+
"authors": [
11+
{
12+
"name": "Robson Tenório"
13+
}
14+
],
15+
"minimum-stability": "dev",
16+
"require": {
17+
"firebase/php-jwt": "^5.0"
18+
},
19+
"autoload": {
20+
"psr-4": {
21+
"KeycloakGuard\\": "src/"
22+
}
23+
},
24+
"autoload-dev": {
25+
"psr-4": {
26+
"KeycloakGuard\\Tests\\": "tests/"
27+
}
28+
},
29+
"scripts": {
30+
"test": "vendor/bin/phpunit",
31+
"test-coverage": "phpunit --coverage-html coverage"
32+
},
33+
"extra": {
34+
"laravel": {
35+
"providers": [
36+
"KeycloakGuard\\KeycloakGuardServiceProvider"
37+
]
38+
}
39+
},
40+
"require-dev": {
41+
"phpunit/phpunit": "^7.3@dev",
42+
"orchestra/testbench": "^3.7@dev"
43+
}
44+
}

config/keycloak.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
return [
4+
'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', null),
5+
6+
'user_provider_credential' => env('KEYCLOAK_USER_PROVIDER_CREDENTIAL', 'username'),
7+
8+
'token_principal_attribute' => env('KEYCLOAK_TOKEN_PRINCIPAL_ATTRIBUTE', 'preferred_username'),
9+
10+
'append_decoded_token' => env('KEYCLOAK_APPEND_DECODED_TOKEN', false),
11+
12+
'allowed_resources' => env('KEYCLOAK_ALLOWED_RESOURCES', null)
13+
];

flow.png

136 KB
Loading

phpunit.xml.dist

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit bootstrap="vendor/autoload.php"
3+
backupGlobals="false"
4+
backupStaticAttributes="false"
5+
colors="true"
6+
verbose="true"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
processIsolation="true"
11+
stopOnFailure="false">
12+
<testsuites>
13+
<testsuite name="Test Suite">
14+
<directory>tests</directory>
15+
</testsuite>
16+
</testsuites>
17+
<filter>
18+
<whitelist>
19+
<directory suffix=".php">src/</directory>
20+
</whitelist>
21+
</filter>
22+
<php>
23+
<env name="DB_CONNECTION" value="testing"/>
24+
</php>
25+
26+
</phpunit>
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
namespace KeycloakGuard\Exceptions;
3+
4+
class KeycloakGuardException extends \UnexpectedValueException
5+
{
6+
public function __construct(string $message)
7+
{
8+
$this->message = "[Keycloack Guard] {$message}";
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
namespace KeycloakGuard\Exceptions;
3+
4+
class ResourceAccessNotAllowedException extends KeycloakGuardException
5+
{
6+
7+
}

src/Exceptions/TokenException.php

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
namespace KeycloakGuard\Exceptions;
3+
4+
class TokenException extends KeycloakGuardException
5+
{
6+
7+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
namespace KeycloakGuard\Exceptions;
3+
4+
class UserNotFoundException extends KeycloakGuardException
5+
{
6+
7+
}

0 commit comments

Comments
 (0)