Browse Source

Add documentation

master
Adam Pippin 3 years ago
parent
commit
e712443bfb
  1. 34
      README.md
  2. 6
      config/authkit.php
  3. 57
      docs/LARAVEL_CONFIG.md
  4. 61
      docs/LARAVEL_INSTALL.md
  5. 113
      docs/LARAVEL_USAGE.md
  6. 0
      docs/OTHER_INSTALL.md
  7. 0
      docs/OTHER_USAGE.md
  8. 30
      src/Events/UserInfoEvent.php
  9. 2
      src/Events/UserRegistration.php
  10. 44
      src/Http/Controllers/AuthenticationController.php

34
README.md

@ -0,0 +1,34 @@
# authkit2
Drop-in OIDC single-sign-on solution for PHP and Laravel projects.
## Purpose
Provide a low-impact way to integrate new and existing Laravel projects with a
single-sign-on solution providing identities for users and software across an
entire ecosystem of projects.
## Features
* Drop-in, almost zero-config solution.
* Integrates with existing Laravel user and authentication systems.
* Exposes core functionality in a framework-agnostic way to allow use outside
of Laravel.
## Getting Started
These documents generally reference Keycloak as the OpenID Connect (OIDC)
provider, however it should ostensibly work with any provider.
### Laravel
* [Installation](docs/LARAVEL_INSTALL.md)
* [Configuration](docs/LARAVEL_CONFIG.md)
* [Usage](docs/LARAVEL_USAGE.md)
### Other
* [Installation](docs/OTHER_INSTALL.md)
* [Usage](docs/OTHER_USAGE.md)

6
config/authkit.php

@ -14,7 +14,9 @@ return [
*/
'enable' => true,
// Scopes to request from the OIDC service
/**
* Scopes to request from the OIDC provider
*/
'scopes' => ['email'],
/*
@ -61,7 +63,7 @@ return [
*
* Currently authorization is not implemented.
*/
'enable' => true,
'enable' => false,
'provider' => [
'url' => env('AUTHZ_HOST', 'http://localhost:4466/')

57
docs/LARAVEL_CONFIG.md

@ -0,0 +1,57 @@
# authkit2 - Laravel Configuration
How to configure authkit2 in your Laravel project.
This requires that you have [installed](LARAVEL_INSTALL.md) authkit2.
# Credentials
There are two ways to configure the OIDC client:
* With a JSON configuration file
* With environment variables
## JSON Configuration
In Keycloak, under `Clients -> Your Client -> Installation`, you can select the
"Keycloak OIDC JSON" and receive a configuration file in a format such as:
```js
{
"realm": "Test",
"auth-server-url": "http://localhost:8080/auth/",
"ssl-required": "external",
"resource": "test",
"credentials": {
"secret": "a0ee4936-ef77-4e4b-87f3-8b4a2706d53a"
},
"confidential-port": 0
}
```
Put this file in your application at `storage/app/auth.json`. You're done!
## Environment Variables
* `AUTHKIT_CLIENT_ID`: Client ID (`resource` in the above JSON)
* `AUTHKIT_CLIENT_SECRET`: Client Secret (`credentials.secret` in the above JSON)
* `AUTHKIT_ENDPOINT`: URL to the OIDC provider
# Customization
There are several other customizable values that are not initially exposed. If
you want to customize these values, you can publish the default configuration
and customize it.
To publish the configuration, from your project's folder:
```
$ php artisan vendor:publish --tag authkit2_config
```
You can then edit the configuration at: `config/authkit.php`
Documentation for those options exists in the comments alongside them.
# Next Steps
See [Usage](LARAVEL_USAGE.md).

61
docs/LARAVEL_INSTALL.md

@ -0,0 +1,61 @@
# authkit2 - Laravel Installation
How to set up authkit2 in your Laravel project.
# Install
Add the authkit2 repository to your composer.json, e.g.,:
```js
"repositories": [
{
"type": "path",
"url": "../path/to/authkit2/repo/"
}
]
```
// TODO: Update to VCS for release.
Then install the package with composer:
```js
$ composer require cmg/authkit2
```
Laravel will automatically discover and register the appropriate service
providers.
# Migrations
There are two sets of migrations:
* New Project: Removes the `password` and `email_verified_at` columns from the
users table.
* Existing Project: Makes the `password` column nullable.
Both migrations are based around the default Laravel user table. If your table
is heavily customized, you will need to modify the migrations before running
them.
## New Project
In your project folder, run:
```
$ php artisan vendor:publish --tag=authkit2_migrate_fresh_project
$ php artisan migrate
```
## Existing Project
In your project folder, run:
```
$ php artisan vendor:publish --tag=authkit2_migrate_existing_project
$ php artisan migrate
```
# Next Steps
See [Configuration](LARAVEL_CONFIG.md).

113
docs/LARAVEL_USAGE.md

@ -0,0 +1,113 @@
# authkit2 - Laravel Usage
How to use authkit2 in your Laravel project.
This requires that you have [installed](LARAVEL_INSTALL.md) and
[configured](LARAVEL_CONFIG.md) authkit2.
# Basic Usage
For basic usage in a new application there's nothing more to do. authkit2
integrates with the default Laravel authentication system and will work out of
the box to sign users in and out of your application.
You can explicitly trigger a login or logout by redirecting to:
* `/auth/login` and
* `/auth/logout`
# Events
Your application will be notified of logins, logins, and new users (to your
application) through [Laravel Events](https://laravel.com/docs/master/events).
* `UserRegistration`
* `UserLogin`
* `UserLogout`
## UserRegistration
This event is fired when a user authenticated that has _not_ previously
authenticated through the OIDC provider. The event is passed the fields
returned by the OIDC provider (e.g., email, name).
If a listener is registered, it is expected to return an instance of your User
model, initialized and saves, that will be tied to the OIDC ID the user has
authenticated with.
For example, a minimal implementation to recreate the default behaviour would
be:
```php
public function handle($event)
{
$user = new \App\Models\User();
$user->name = $event->fields['name'];
$user->email = $event->fields['email'];
$user->save();
return $user;
}
```
If you wanted an implementation to help migrate existing users to OIDC users,
something like the following may work:
```php
public function handle($event)
{
// Try and load an existing user with the given email address
$user = \App\Models\User::where('email', $event->fields['email'])->first();
if (!isset($user))
{
// If that user wasn't found, this is an entirely new user
$user = new \App\Models\User();
$user->name = $event->fields['name'];
$user->email = $event->fields['email'];
$user->save();
return $user;
}
else
{
// If the user was found, then we can tie them to the OIDC
// user.
$user->name = $event->fields['name'];
$user->email = $event->fields['email'];
// Clear the user's password to prevent non-OIDC logins going
// forward.
$user->password = null;
$user->save();
return $user;
}
}
```
## UserLogin
This event is fired when a user is authenticated (whether an existing user, or
after a UserRegistration event). The event is passed the user model and the
fields returned by the OIDC provider (e.g., email, name).
If a listener is registered, it is expected to update the user model with any
updated fields returned by the OIDC provider.
For example, a minimal implementation to recreate the default behaviour would
be:
```php
public function handle($event)
{
$user = $event->user;
$user->name = $event->fields['name'];
$user->email = $event->fields['email'];
$user->save();
}
```
## UserLogout
This event is fired when the user tries to log out of your application. The
event is fired _before_ the user is logged out of the Laravel authentication
system or redirected to the OIDC provider to logout.

0
docs/OTHER_INSTALL.md

0
docs/OTHER_USAGE.md

30
src/Events/UserInfoEvent.php

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace authkit2\Events;
/**
* Notification for a user along with their info provided by OIDC provider
*/
class UserInfoEvent extends UserEvent
{
/**
* Additional fields returned during login
*
* @var mixed
*/
public $fields;
/**
* Initialize new event
*
* @param mixed $user
* @param array<string,string> $fields
*/
public function __construct($user, array $fields)
{
parent::__construct($user);
$this->fields = $fields;
}
}

2
src/Events/UserRegistration.php

@ -8,6 +8,6 @@ namespace authkit2\Events;
* Notification that a user logging in is brand new
* to this app.
*/
class UserRegistration extends UserEvent
class UserRegistration extends UserInfoEvent
{
}

44
src/Http/Controllers/AuthenticationController.php

@ -44,6 +44,8 @@ class AuthenticationController extends Controller
*/
public function callback(Request $request)
{
$user_class = config('auth.providers.users.model');
// Verify the passed in state value
$this->user_flow->validateState($request->state);
@ -60,19 +62,33 @@ class AuthenticationController extends Controller
if (!isset($token))
{
// No token for that user, either create them or migrate
$register_event_result = event(new \authkit2\Events\UserRegistration($user_info));
$user = null;
foreach ($register_event_result as $result)
$register_event_result = event(new \authkit2\Events\UserRegistration(null, $user_info));
if (sizeof($register_event_result) == 0)
{
if (is_object($result))
// If there were no register event handlers then just assume we're using laravel default
// stuff and create a user for them.
$user = new $user_class();
$user->name = $user_info['name'];
$user->email = $user_info['email'];
$user->save();
}
else
{
// Otherwise, expect one returned by the event handlers.
$user = null;
foreach ($register_event_result as $result)
{
$user = $result;
if (is_object($result))
{
$user = $result;
}
}
}
if (!isset($user))
{
// TODO: Log a useful error
// TODO: Log a useful error message
abort(500);
}
@ -82,11 +98,23 @@ class AuthenticationController extends Controller
$token->refresh_token = $oidc_token->getRefreshToken();
$token->user_id = $user->{$user->getAuthIdentifierName()};
$token->save();
}
else
{
$user = new $user_class();
$user = $user_class::where($user->getAuthIdentifierName(), $token->user_id)->first();
}
\Auth::login($user);
event(new \authkit2\Events\UserLogin($user, $user_info));
$login_event_result = event(new \authkit2\Events\UserLogin($user, $user_info));
if (!sizeof($login_event_result))
{
// If nothing handled the login event, assume we're using laravel default
// everything and just go ahead and update the name/email
$user->name = $user_info['name'];
$user->email = $user_info['email'];
$user->save();
}
return redirect(url(config('authkit.authn.urls.post_login')));
}

Loading…
Cancel
Save