Merge pull request #495 from thephpleague/gh-pages-v5

Docs for V5
This commit is contained in:
Alex Bilbie 2016-03-24 19:30:40 +00:00
commit 72ac486b5b
27 changed files with 954 additions and 630 deletions

3
.gitignore vendored
View File

@ -3,3 +3,6 @@ composer.lock
.idea
build
vendor
examples
src
phpunit.xml

9
V4-docs.md Normal file
View File

@ -0,0 +1,9 @@
---
layout: default
title: V4 Docs
permalink: /v4-docs/
---
# V4 Docs
Checkout the `gh-pages-v4` branch and run `jekyll serve`

View File

@ -1,18 +1,26 @@
Getting Started:
Introduction: '/'
Terminology: '/terminology/'
Requirements: '/requirements/'
Installation: '/installation/'
Implementing storage interfaces: '/implementing-storage-interfaces/'
Database Setup: '/database-setup/'
Framework Integrations: '/framework-integrations/'
V4 Docs: '/V4-docs/'
Authorization Server:
'Which grant?': '/authorization-server/which-grant/'
'Authorization Code Grant': '/authorization-server/auth-code-grant/'
'Which Grant?': '/authorization-server/which-grant/'
'Client Credentials Grant': '/authorization-server/client-credentials-grant/'
'Password Grant': '/authorization-server/resource-owner-password-credentials-grant/'
'Authorization Code Grant': '/authorization-server/auth-code-grant/'
'Implict Grant': '/authorization-server/implicit-grant/'
'Refresh Token Grant': '/authorization-server/refresh-token-grant/'
'Server Customisation': '/authorization-server/customisation/'
'Events': '/authorization-server/events/'
'Custom token identifier generator': '/authorization-server/custom-token-identifier-generator/'
'Token types': '/token-types/'
'Custom grants': '/authorization-server/custom-grants/'
# 'Creating custom grants': '/authorization-server/custom-grants/'
'Domain Events': '/authorization-server/events/'
Resource Server:
'Securing your API': '/resource-server/securing-your-api/'
Respository Interfaces:
'Access Token Repository Interface': '/access-token-repository-interface/'
'Client Repository Interface': '/client-repository-interface/'
'Refresh Token Repository Interface': '/refresh-token-repository-interface/'
'Scope Repository Interface': '/scope-repository-interface/'
'Auth Code Repository Interface': '/auth-code-repository-interface/'
'User Repository Interface': '/user-repository-interface/'

View File

@ -1,198 +1,173 @@
---
layout: default
title: Authorization server with authorization code grant
title: Authorization code grant
permalink: /authorization-server/auth-code-grant/
---
# Authorization server with authorization code grant
# Authorization code grant
The authorization code grant should be very familiar if you've ever signed into a web app using your Facebook or Google account.
## Flow
### Part One
The client will redirect the user to the authorization server with the following parameters in the query string:
* `response_type` with the value `code`
* `client_id` with the client identifier
* `redirect_uri` with the client redirect URI. This parameter is optional, but if not send the user will be redirected to a pre-registered redirect URI.
* `scope` a space delimited list of scopes
* `state` with a CSRF token. This parameter is optional but highly recommended.
All of these parameters will be validated by the authorization server.
The user will then be asked to login to the authorization server and approve the client.
If the user approves the client they will be redirected back to the authorization server with the following parameters in the query string:
* `code` with the authorization code
* `state` with the state parameter sent in the original request
### Part Two
The client will now send a POST request to the authorization server with the following parameters:
* `grant_type` with the value of `authorization_code`
* `client_id` with the client identifier
* `client_secret` with the client secret
* `redirect_uri` with the same redirect URI the user was redirect back to
* `code` with the authorization code from the query string (remember to url decode it first)
The authorization server will respond with a JSON object containing the following properties:
* `token_type` with the value `Bearer`
* `expires_in` with an integer representing the TTL of the access token
* `access_token` a JWT signed with the authorization server's private key
* `refresh_token` an encrypted payload that can be used to refresh the access token when it expires.
## Setup
Wherever you intialise your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
Wherever you initialize your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
~~~ php
$server = new \League\OAuth2\Server\AuthorizationServer;
{% highlight php %}
// Init our repositories
$clientRepository = new ClientRepository();
$scopeRepository = new ScopeRepository();
$accessTokenRepository = new AccessTokenRepository();
$authCodeRepository = new AuthCodeRepository();
$refreshTokenRepository = new RefreshTokenRepository();
$userRepository = new UserRepository();
$server->setSessionStorage(new Storage\SessionStorage);
$server->setAccessTokenStorage(new Storage\AccessTokenStorage);
$server->setClientStorage(new Storage\ClientStorage);
$server->setScopeStorage(new Storage\ScopeStorage);
$server->setAuthCodeStorage(new Storage\AuthCodeStorage);
// Path to public and private keys
$privateKeyPath = 'file://path/to/private.key';
$publicKeyPath = 'file://path/to/public.key';
$authCodeGrant = new \League\OAuth2\Server\Grant\AuthCodeGrant();
$server->addGrantType($authCodeGrant);
~~~
// Setup the authorization server
$server = new \League\OAuth2\Server\Server(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
// Enable the authentication code grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new \League\OAuth2\Server\Grant\AuthCodeGrant(
$authCodeRepository,
$refreshTokenRepository,
$userRepository,
new \DateInterval('PT10M')
),
new \DateInterval('PT1H')
);
{% endhighlight %}
## Implementation
Create a route which will respond to a request to `/oauth` which is where the client will redirect the user to.
The client will request an access token so create an `/oauth2` endpoint.
~~~ php
$router->get('/oauth', function (Request $request) use ($server) {
{% highlight php %}
$app->post('/oauth2', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
// First ensure the parameters in the query string are correct
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
// Try to respond to the request
try {
return $server->respondToRequest($request, $response);
$authParams = $server->getGrantType('authorization_code')->checkAuthorizeParams();
} catch (\League\OAuth2\Server\Exception\OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $e) {
if ($e->shouldRedirect()) {
return new Response('', 302, [
'Location' => $e->getRedirectUri()
]);
}
return new Response(
json_encode([
'error' => $e->errorType,
'message' => $e->getMessage()
]),
$e->httpStatusCode, // All of the library's exception classes have a status code specific to the error
$e->getHttpHeaders() // Some exceptions have headers which need to be sent
);
}
// Everything is okay, save $authParams to the a session and redirect the user to sign-in
return new Response('', 302, [
'Location' => '/signin'
]);
});
~~~
The user is redirected to a sign-in screen. If the user is not signed in then sign them in.
~~~ php
$router->get('/signin', function (Request $request) use ($server) {
if ($user) {
$response = new Response('', 302, [
'Location' => '/authorize'
]);
return $response;
} else {
// Logic here to show the a sign-in form and sign the user in
}
});
~~~
The final part is to show a web page that tells the user the name of the client, the scopes requested and two buttons, an "Approve" button and a "Deny" button.
View:
~~~ php
// Authorize view
<h1><?= $authParams['client']->getName() ?> would like to access:</h1>
<ul>
<?php foreach ($authParams['scopes'] as $scope): ?>
<li>
<?= $scope->getName() ?>: <?= $scope->getDescription() ?>
</li>
<?= endforeach; ?>
</ul>
<form method="post">
<input type="submit" value="Approve" name="authorization">
<input type="submit" value="Deny" name="authorization">
</form>
~~~
Route:
~~~ php
$router->post('/signin', function (Request $request) use ($server) {
if (!isset($_POST['authorization'])) {
// show form
}
// If the user authorizes the request then redirect the user back with an authorization code
if ($_POST['authorization'] === 'Approve') {
$redirectUri = $server->getGrantType('authorization_code')->newAuthorizeRequest('user', 1, $authParams);
$response = new Response('', 302, [
'Location' => $redirectUri
]);
return $response;
}
// The user denied the request so redirect back with a message
else {
$error = new \League\OAuth2\Server\Util\AccessDeniedException;
$redirectUri = \League\OAuth2\Server\Util\RedirectUri::make(
$authParams['redirect_uri'],
[
'error' => $error->errorType,
'message' => $e->getMessage()
]
);
$response = new Response('', 302, [
'Location' => $redirectUri
]);
return $response;
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
~~~
{% endhighlight %}
The user will be redirected back to the client with either an error message or an authorization code.
## Modify the login and authorize pages
If the client recieves an authorization code it will request to turn it into an access token. For this you need an `/access_token` endpoint.
You can easily modify the HTML pages used by the authorization server. The library comes with built-in support for Twig, Smarty, Mustache and Plates templates.
~~~ php
$router->post('/access_token', function (Request $request) use ($server) {
The default implementation uses `league/plates` and has some [very basic HTML templates](https://github.com/thephpleague/oauth2-server/tree/V5-WIP/src/TemplateRenderer/DefaultTemplates).
try {
The login template has the following variable available:
$response = $server->issueAccessToken();
return new Response(
json_encode($response),
200
[
'Content-type' => 'application/json',
'Cache-Control' => 'no-store',
'Pragma' => 'no-store'
]
* `error` (null or a string) - Set if there was an error with the login
The form inputs must be called `username` and `password` and must be POSTed.
The authorize template has the following variable available:
* `client` - The name of the client the user is authorizing
* `scopes` - An array of ScopeEntityInterface. Use `getIdentifier` to get a string you can print
The form must be POSTed with an input named `action` with the value `approve` if the user approves the client.
### Using Plates with custom templates
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\PlatesRenderer(
new Engine('/path/to/templates'),
'login_template_name',
'authorize_template_name'
);
$authCodeGrant->setTemplateRenderer($renderer);
{% endhighlight %}
} catch (\Exception $e) {
### Using Twig with custom templates
return new Response(
json_encode([
'error' => $e->errorType,
'message' => $e->getMessage()
]),
$e->httpStatusCode,
$e->getHttpHeaders()
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\TwigRenderer(
$environment, // instance of Twig_Environment
'login_template_name',
'authorize_template_name'
);
$authCodeGrant->setTemplateRenderer($renderer);
{% endhighlight %}
}
### Using Smarty with custom templates
});
~~~
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\SmartyRenderer(
$smarty, // instance of \Smarty
'login_template_name',
'authorize_template_name'
);
$authCodeGrant->setTemplateRenderer($renderer);
{% endhighlight %}
### Notes
### Using Mustache with custom templates
* You could combine the sign-in form and authorize form into one form
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\MustacheRenderer(
$engine, // instance of \Mustache_Engine
'login_template_name',
'authorize_template_name'
);
$authCodeGrant->setTemplateRenderer($renderer);
{% endhighlight %}

View File

@ -1,59 +1,79 @@
---
layout: default
title: Authorization server with client credentials grant
title: Client credentials grant
permalink: /authorization-server/client-credentials-grant/
---
# Authorization server with client credentials grant
# Client credentials grant
This grant is suitable for machine-to-machine authentication, for example for use in a cron job which is performing maintenance tasks over an API. Another example would be a client making requests to an API that dont require users permission.
## Flow
The client sends a POST request with following body parameters to the authorization server:
* `grant_type` with the value `client_credentials`
* `client_id` with the the client's ID
* `client_secret` with the client's secret
* `scope` with a space-delimited list of requested scope permissions.
The authorization server will respond with a JSON object containing the following properties:
* `token_type` with the value `Bearer`
* `expires_in` with an integer representing the TTL of the access token
* `access_token` a JWT signed with the authorization server's private key
## Setup
Wherever you intialise your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
Wherever you initialize your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
~~~ php
$server = new \League\OAuth2\Server\AuthorizationServer;
{% highlight php %}
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$server->setSessionStorage(new Storage\SessionStorage);
$server->setAccessTokenStorage(new Storage\AccessTokenStorage);
$server->setClientStorage(new Storage\ClientStorage);
$server->setScopeStorage(new Storage\ScopeStorage);
// Path to public and private keys
$privateKeyPath = 'file://path/to/private.key';
$publicKeyPath = 'file://path/to/public.key';
$clientCredentials = new \League\OAuth2\Server\Grant\ClientCredentialsGrant();
$server->addGrantType($clientCredentials);
~~~
// Setup the authorization server
$server = new \League\OAuth2\Server\Server(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
// Enable the client credentials grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new \League\OAuth2\Server\Grant\ClientCredentialsGrant(),
new \DateInterval('PT1H')
);
{% endhighlight %}
## Implementation
The client will request an access token so create an `/access_token` endpoint.
~~~ php
$router->post('/access_token', function (Request $request) use ($server) {
{% highlight php %}
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
// Try to respond to the request
try {
return $server->respondToRequest($request, $response);
$response = $server->issueAccessToken();
return new Response(
json_encode($response),
200,
[
'Content-type' => 'application/json',
'Cache-Control' => 'no-store',
'Pragma' => 'no-store'
]
);
} catch (\Exception $e) {
return new Response(
json_encode([
'error' => $e->errorType,
'message' => $e->getMessage()
]),
$e->httpStatusCode,
$e->getHttpHeaders()
);
} catch (\League\OAuth2\Server\Exception\OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
~~~
{% endhighlight %}

View File

@ -1,9 +0,0 @@
---
layout: default
title: Authorization server customisation
permalink: /authorization-server/customisation/
---
# Authorization server customisation
## TODO

View File

@ -10,34 +10,38 @@ During the lifecycle of a request passing through the authorization server a num
You can subscribe to these events by attaching listeners to the authorization server.
## error.auth.client
To access the emitter call this method:
~~~ php
$server->addEventListener('error.auth.client', function ($event) { });
~~~
{% highlight php %}
$server->getEmitter(); // returns instance of \League\Event\EmitterInterface
{% endhighlight %}
## client.authentication.failed
{% highlight php %}
$server->getEmitter()->addListener(
'client.authentication.failed',
function (\League\OAuth2\Server\RequestEvent $event) {
// do something
}
);
{% endhighlight %}
This event is emitted when a client fails to authenticate. You might wish to listen to this event in order to ban clients that fail to authenticate after `n` number of attempts.
You can retrieve the request object that was used by calling `getRequest()` on the event object passed into your callable.
## error.auth.user
## user.authentication.failed
~~~ php
$server->addEventListener('error.auth.user', function ($event) { });
~~~
{% highlight php %}
$server->getEmitter()->addListener(
'user.authentication.failed',
function (\League\OAuth2\Server\RequestEvent $event) {
// do something
}
);
{% endhighlight %}
This event is emitted when a user fails to authenticate. You might wish to listen to this event in order to reset passwords or ban users that fail to authenticate after `n` number of attempts.
You can retrieve the request object that was used by calling `getRequest()` on the event object passed into your callable.
## session.owner
~~~ php
$server->addEventListener('session.owner', function ($event) { });
~~~
This event is emitted when a session has been allocated an owner (for example a user or a client).
You might want to use this event to dynamically associate scopes to the session depending on the users role or ACL permissions.
You can access the session entity objected by calling `getSession()` on the event object passed into your callable.

154
auth-server-implicit.md Executable file
View File

@ -0,0 +1,154 @@
---
layout: default
title: Implicit grant
permalink: /authorization-server/implicit-grant/
---
# Implicit grant
The implicit grant is similar to the authorization code grant with two distinct differences.
It is intended to be used for user-agent-based clients (e.g. single page web apps) that can't keep a client secret because all of the application code and storage is easily accessible.
Secondly instead of the authorization server returning an authorization code which is exchanged for an access token, the authorization server returns an access token.
## Flow
The client will redirect the user to the authorization server with the following parameters in the query string:
* `response_type` with the value `token`
* `client_id` with the client identifier
* `redirect_uri` with the client redirect URI. This parameter is optional, but if not send the user will be redirected to a pre-registered redirect URI.
* `scope` a space delimited list of scopes
* `state` with a CSRF token. This parameter is optional but highly recommended.
All of these parameters will be validated by the authorization server.
The user will then be asked to login to the authorization server and approve the client.
If the user approves the client they will be redirected back to the authorization server with the following parameters in the query string:
* `token_type` with the value `Bearer`
* `expires_in` with an integer representing the TTL of the access token
* `access_token` a JWT signed with the authorization server's private key
****Note**** this grant does not return a refresh token.
## Setup
Wherever you initialize your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
{% highlight php %}
// Init our repositories
$clientRepository = new ClientRepository();
$scopeRepository = new ScopeRepository();
$accessTokenRepository = new AccessTokenRepository();
$userRepository = new UserRepository();
// Path to public and private keys
$privateKeyPath = 'file://path/to/private.key';
$publicKeyPath = 'file://path/to/public.key';
// Setup the authorization server
$server = new \League\OAuth2\Server\Server(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
// Enable the implicit grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new ImplicitGrant($userRepository),
new \DateInterval('PT1H')
);
{% endhighlight %}
## Implementation
The client will request an access token so create an `/oauth2` endpoint.
{% highlight php %}
$app->post('/oauth2', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
// Try to respond to the request
try {
return $server->respondToRequest($request, $response);
} catch (\League\OAuth2\Server\Exception\OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
{% endhighlight %}
## Modify the login and authorize pages
You can easily modify the HTML pages used by the authorization server. The library comes with built-in support for Twig, Smarty, Mustache and Plates templates.
The default implementation uses `league/plates` and has some [very basic HTML templates](https://github.com/thephpleague/oauth2-server/tree/V5-WIP/src/TemplateRenderer/DefaultTemplates).
The login template has the following variable available:
* `error` (null or a string) - Set if there was an error with the login
The form inputs must be called `username` and `password` and must be POSTed.
The authorize template has the following variable available:
* `client` - The name of the client the user is authorizing
* `scopes` - An array of ScopeEntityInterface. Use `getIdentifier` to get a string you can print
The form must be POSTed with an input named `action` with the value `approve` if the user approves the client.
### Using Plates with custom templates
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\PlatesRenderer(
new Engine('/path/to/templates'),
'login_template_name',
'authorize_template_name'
);
$implicitGrant->setTemplateRenderer($renderer);
{% endhighlight %}
### Using Twig with custom templates
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\TwigRenderer(
$environment, // instance of Twig_Environment
'login_template_name',
'authorize_template_name'
);
$implicitGrant->setTemplateRenderer($renderer);
{% endhighlight %}
### Using Smarty with custom templates
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\SmartyRenderer(
$smarty, // instance of \Smarty
'login_template_name',
'authorize_template_name'
);
$implicitGrant->setTemplateRenderer($renderer);
{% endhighlight %}
### Using Mustache with custom templates
{% highlight php %}
$renderer = new \League\OAuth2\Server\TemplateRenderer\MustacheRenderer(
$engine, // instance of \Mustache_Engine
'login_template_name',
'authorize_template_name'
);
$implicitGrant->setTemplateRenderer($renderer);
{% endhighlight %}

View File

@ -1,64 +1,89 @@
---
layout: default
title: Authorization server with resource owner password credentials grant
title: Resource owner password credentials grant
permalink: /authorization-server/resource-owner-password-credentials-grant/
---
# Authorization server with resource owner password credentials grant
# Resource owner password credentials grant
This grant is a great user experience for <u>trusted</u> first party clients both on the web and in native applications.
## Flow
The client will ask the user for their authorization credentials (ususally a username and password).
The client then sends a POST request with following body parameters to the authorization server:
* `grant_type` with the value `password`
* `client_id` with the the client's ID
* `client_secret` with the client's secret
* `scope` with a space-delimited list of requested scope permissions.
* `username` with the user's username
* `password` with the user's password
The authorization server will respond with a JSON object containing the following properties:
* `token_type` with the value `Bearer`
* `expires_in` with an integer representing the TTL of the access token
* `access_token` a JWT signed with the authorization server's private key
* `refresh_token` an encrypted payload that can be used to refresh the access token when it expires.
## Setup
Wherever you intialise your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
Wherever you initialize your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
~~~ php
$server = new \League\OAuth2\Server\AuthorizationServer;
{% highlight php %}
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$userRepository = new UserRepository();
$refreshTokenRepository = new RefreshTokenRepository();
$server->setSessionStorage(new Storage\SessionStorage);
$server->setAccessTokenStorage(new Storage\AccessTokenStorage);
$server->setClientStorage(new Storage\ClientStorage);
$server->setScopeStorage(new Storage\ScopeStorage);
// Path to public and private keys
$privateKeyPath = 'file://path/to/private.key';
$publicKeyPath = 'file://path/to/public.key';
$passwordGrant = new \League\OAuth2\Server\Grant\PasswordGrant();
$passwordGrant->setVerifyCredentialsCallback(function ($username, $password) {
// implement logic here to validate a username and password, return an ID if valid, otherwise return false
});
$server->addGrantType($passwordGrant);
~~~
// Setup the authorization server
$server = new \League\OAuth2\Server\Server(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
// Enable the password grant on the server with an access token TTL of 1 hour
$server->enableGrantType(
new \League\OAuth2\Server\Grant\PasswordGrant(
$userRepository,
$refreshTokenRepository
),
new \DateInterval('PT1H')
);
{% endhighlight %}
## Implementation
The client will request an access token so create an `/access_token` endpoint.
~~~ php
$router->post('/access_token', function (Request $request) use ($server) {
{% highlight php %}
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
// Try to respond to the request
try {
return $server->respondToRequest($request, $response);
$response = $server->issueAccessToken();
return new Response(
json_encode($response),
200,
[
'Content-type' => 'application/json',
'Cache-Control' => 'no-store',
'Pragma' => 'no-store'
]
);
} catch (OAuthException $e) {
return new Response(
json_encode([
'error' => $e->errorType,
'message' => $e->getMessage()
]),
$e->httpStatusCode,
$e->getHttpHeaders()
);
} catch (\League\OAuth2\Server\Exception\OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
~~~
{% endhighlight %}

View File

@ -1,26 +1,81 @@
---
layout: default
title: Authorization server with refresh token grant
title: Refresh token grant
permalink: /authorization-server/refresh-token-grant/
---
# Authorization server with refresh token grant
# Refresh token grant
Access tokens eventually expire; however some grants respond with a refresh token which enables the client to refresh the access token.
## Flow
The client sends a POST request with following body parameters to the authorization server:
* `grant_type` with the value `refresh_token`
* `client_id` with the the client's ID
* `client_secret` with the client's secret
* `scope` with a space-delimited list of requested scope permissions. This is optional; if not sent the original scopes will be used, otherwise you can request a reduced set of scopes.
The authorization server will respond with a JSON object containing the following properties:
* `token_type` with the value `Bearer`
* `expires_in` with an integer representing the TTL of the access token
* `access_token` a new JWT signed with the authorization server's private key
* `refresh_token` an encrypted payload that can be used to refresh the access token when it expires
## Setup
Wherever you intialise your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
Wherever you initialize your objects, initialize a new instance of the authorization server and bind the storage interfaces and authorization code grant:
~~~ php
$server = new \League\OAuth2\Server\AuthorizationServer;
{% highlight php %}
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$refreshTokenRepository = new RefreshTokenRepository();
$server->setSessionStorage(new Storage\SessionStorage);
$server->setAccessTokenStorage(new Storage\AccessTokenStorage);
$server->setClientStorage(new Storage\ClientStorage);
$server->setScopeStorage(new Storage\ScopeStorage);
$server->setRefreshTokenStorage(new Storage\RefreshTokenStorage);
// Path to public and private keys
$privateKeyPath = 'file://path/to/private.key';
$publicKeyPath = 'file://path/to/public.key';
$refreshTokenGrant = new \League\OAuth2\Server\Grant\RefreshTokenGrant();
$server->addGrantType($refreshTokenGrant);
~~~
// Setup the authorization server
$server = new \League\OAuth2\Server\Server(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
When the refresh token grant is enabled, a refresh token will automatically be created with access tokens issued requested using the [authorization code](/authorization-server/auth-code-grant/) or [resource owner password credentials](/authorization-server/resource-owner-password-credentials-grant/) grants.
// Enable the refresh token grant on the server with a token TTL of 1 hour
$server->enableGrantType(
new \League\OAuth2\Server\Grant\RefreshTokenGrant($refreshTokenRepository),
new \DateInterval('PT1H')
);
{% endhighlight %}
## Implementation
The client will request an access token so create an `/access_token` endpoint.
{% highlight php %}
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
/* @var \League\OAuth2\Server\Server $server */
$server = $app->getContainer()->get(Server::class);
// Try to respond to the request
try {
return $server->respondToRequest($request, $response);
} catch (\League\OAuth2\Server\Exception\OAuthServerException $exception) {
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
{% endhighlight %}

View File

@ -6,112 +6,34 @@ permalink: /authorization-server/which-grant/
# Which OAuth 2.0 grant should I use?
This page was originally posted at [http://alexbilbie.com/2013/02/a-guide-to-oauth-2-grants/](http://alexbilbie.com/2013/02/a-guide-to-oauth-2-grants/).
A grant is a method of acquiring an access token. Deciding which grants to implement requires implementing the right grant depending on the type of client and the best experience for your users.
---
<figure>
<img src="/images/grants.svg" style="width:100%">
</figure>
OAuth 2.0 by its nature is a very flexible standard and can be adapted to work in many different scenarios. The [core specification](http://tools.ietf.org/html/rfc6749) describes four authorization grants:
## First party or third party client?
* Authorization code grant
* Implicit grant
* Resource owner credentials grant
* Client credentials grant
A first party client is a client that you trust enough to handle the end user's authorization credentials. For example Spotify's iPhone app is owned and developed by Spotify so therefore they implicitly trust it.
The specification also details another grant called the _refresh token grant_.
A third party client is a client that you don't trust.
Furthermore there are a number of other grants that have gone through the IETF ratification process (none of which at the time of writing have been formally standardised):
## Access Token Owner?
* Message authentication code (MAC) tokens
* SAML 2.0 Bearer Assertion Profiles
* JSON web token grant
An access token represents a permission granted to a client to access some protected resources.
The end goal of each of these grants (except the refresh token grant) is for the client application to have an access token (which represents a users permission for the client to access their data) which it can use to authenticate a request to an API endpoint.
If you are authorizing a machine to access resources and you don't require the permission of a user to access said resources you should implement the [client credentials grant](/authorization-server/client-credentials-grant/).
This page describes each of the above grants and their appropriate use cases.
If you require the permission of a user to access resources you need to determine the client type.
As a refresher here is a quick glossary of OAuth terms (taken from the core spec):
## Client Type?
* **Resource owner (a.k.a. the User)** - An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user.
* **Resource server (a.k.a. the API server)** - The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens.
* **Client** - An application making protected resource requests on behalf of the resource owner and with its authorization. The term client does not imply any particular implementation characteristics (e.g. whether the application executes on a server, a desktop, or other devices).
* **Authorization server** - The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization.
Depending on whether or not the client is capable of keeping of keeping a secret will depend on which grant the client should use.
## Authorization code grant ([section 4.1](http://tools.ietf.org/html/rfc6749#section-4.1))
If the client is a web application that has a server side component then you should implement the [authorization code grant](/authorization-server/auth-code-grant/).
**To enable this grant:**
If the client is a web application that has runs entirely on the front end (e.g. a single page web application) you should implement the [password grant](/authorization-server/resource-owner-password-credentials-grant/) for a first party clients and the [implicit grant](/authorization-server/auth-code-grant/) for a third party clients.
~~~ php
$authCodeGrant = new \League\OAuth2\Server\Grant\AuthCodeGrant();
$server->addGrantType($authCodeGrant);
~~~
If the client is a native application such as a mobile app you should implement the [password grant](/authorization-server/resource-owner-password-credentials-grant/).
The authorization code grant is the grant that most people think of when OAuth is described.
If youve ever signed into a website or application with your Twitter/Facebook/Google/(insert major Internet company here) account then youll have experienced using this grant.
Essentially a user will click on a “sign in with Facebook” (or other <abbr title="Identity Provider">IdP</abbr>) and then be redirected from the application/website (the “client”) to the IdP authorization server. The user will then sign in to the IdP with their credentials, and then - if they havent already - authorise the client to allow it to use the users data (such as their name, email address, etc). If they authorise the request the user will be redirected back to the client with a token (called the authorization code) in the query string (e.g. `http://client.com/redirect?code=XYZ123`) which the client will capture and exchange for an access token in the background.
This grant is suitable where the resource owner is a user and they are using a client which is allows a user to interact with a website in a browser. An obvious example is the client being another website, but desktop applications such as Spotify or Reeder use embedded browsers.
Some mobile applications use this flow and again use an embedded browser (or redirect the user to the native browser and then are redirected back to the app using a custom protocol).
In this grant the access token is kept private from the resource owner.
If you have a mobile application that is for your own service (such as the official Spotify or Facebook apps on iOS) it isnt appropriate to use this grant as the app itself should already be trusted by your authorization server and so the _resource owner credentials grant would be more appropriate.
## Implicit grant ([section 4.2](http://tools.ietf.org/html/rfc6749#section-4.2))
**Not currently supported. [See #249](https://github.com/thephpleague/oauth2-server/issues/249)**
The implicit grant is similar to the authentication code grant described above. The user will be redirected in a browser to the IdP authorization server, sign in, authorise the request but instead of being returned to the client with an authentication code they are redirected with an access token straight away.
The purpose of the implicit grant is for use by clients which are not capable of keeping the clients own credentials secret; for example a JavaScript only application.
**If you decide to implement this grant then you must be aware that the access token should be treated as “public knowledge” (like a public RSA key)** and therefore it must have a very limited permissions when interacting with the API server. For example an access token that was granted using the authentication code grant could have permission to be used to delete resources owned by the user, however an access token granted through the implicit flow should only be able to “read” resources and never perform any destructive operations (i.e. non-idempotent method).
## Resource owner credentials grant ([section 4.3](http://tools.ietf.org/html/rfc6749#section-4.3))
**To enable this grant:**
~~~ php
$passwordGrant = new \League\OAuth2\Server\Grant\PasswordGrant();
$passwordGrant->setVerifyCredentialsCallback(function ($username, $password) {
// implement logic here to validate a username and password,
// return an ID if valid, return false otherwise
});
$server->addGrantType($passwordGrant);
~~~
When this grant is implemented the client itself will ask the user for their username and password (as opposed to being redirected to an IdP authorization server to authenticate) and then send these to the authorization server along with the clients own credentials. If the authentication is successful then the client will be issued with an access token.
This grant is suitable for trusted clients such as a services own mobile client (for example Spotifys iOS app). You could also use this in software where its not easy to implement the authorization code - for example we bolted this authorization grant into [OwnCloud](http://owncloud.org/) so we could retrieve details about a user that we couldnt access over LDAP from the universitys Active Directory server.
## Client credentials grant ([section 4.4](http://tools.ietf.org/html/rfc6749#section-4.4))
**To enable this grant:**
~~~ php
$clientCredentials = new League\OAuth2\Server\Grant\ClientCredentialsGrant();
$server->addGrantType($clientCredentials);
~~~
This grant is similar to the resource owner credentials grant except only the clients credentials are used to authenticate a request for an access token. Again this grant should only be allowed to be used by trusted clients.
This grant is suitable for machine-to-machine authentication, for example for use in a cron job which is performing maintenance tasks over an API. Another example would be a client making requests to an API that dont require users permission.
When someone visits a member of staffs page on the [University of Lincoln staff directory](http://staff.lincoln.ac.uk/) the website uses its own access token (that was generated using this grant) to authenticate a request to the API server to get the data about the member of staff that is used to build the page. When a member of staff signs in to update their profile however their own access token is used to retrieve and update their data. Therefore there is a good separation of concerns and we can easily restrict permissions that each type of access token has.
## Refresh token grant ([section 1.5](http://tools.ietf.org/html/rfc6749#section-1.5))
**To enable this grant:**
~~~ php
$refreshTokenGrant = new \League\OAuth2\Server\Grant\RefreshTokenGrant();
$server->addGrantType($refreshTokenGrant);
~~~
The OAuth 2.0 specification also details a fifth grant which can be used to “refresh” (i.e. renew) an access token which has expired.
Authorization servers which support this grant will also issue a “refresh token” when it returns an access token to a client. When the access token expires instead of sending the user back through the authorization code grant the client can use to the refresh token to retrieve a new access token with the same permissions as the old one.
A problem with the grant is that it means the client has to maintain state of each token and then either on a cron job keep access tokens up to date or when it tries to make a request and it fails then go and update the access token and repeat the request.
Third party native applications should use the [authorization code grant](/authorization-server/auth-code-grant/) (via the native browser, not an embedded browser - e.g. for iOS push the user to Safari or use [SFSafariViewController](https://developer.apple.com/library/ios/documentation/SafariServices/Reference/SFSafariViewController_Ref/), <u>don't</u> use an embedded [WKWebView](https://developer.apple.com/library/ios/documentation/WebKit/Reference/WKWebView_Ref/)).

11
database-setup.md Executable file
View File

@ -0,0 +1,11 @@
---
layout: default
title: Database Setup
permalink: /database-setup/
---
# Database Setup
This library has been developed so that you can use any type of backend storage; relational, document, key value, columnar or even hardcoded.
The documentation for each of the repository interfaces describes what sort of data you might want to store not how to store it.

11
framework-integrations.md Normal file
View File

@ -0,0 +1,11 @@
---
layout: default
title: Framework Integrations
permalink: /framework-integrations/
---
# Framework Integrations
Framework integrations will be listed here when there are some :)
Have you made one or written a blog post? Open a PR against the `gh-pages` branch and update this page.

BIN
images/grants.sketch Normal file

Binary file not shown.

119
images/grants.svg Normal file
View File

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="700px" height="639px" viewBox="0 0 700 639" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 3.6.1 (26313) - http://www.bohemiancoding.com/sketch -->
<title>Which Grant?</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Which-Grant?">
<path d="M368,75 L368,144" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M368,144 L371,133.2 L365,133.2 L368,144 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M368,260 L368,328" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M368,328 L371,317.2 L365,317.2 L368,328 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M368,445 L368,512" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M368,512 L371,501.2 L365,501.2 L368,512 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M96,445 L96,534.022469" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M96,534.022469 L99,523.222469 L93,523.222469 L96,534.022469 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M492.5,477 L550,477" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M550,477 L539.2,474 L539.2,480 L550,477 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M458.5,387 L491.5,387" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square"></path>
<path d="M492,387 L492,572" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square"></path>
<path d="M459.5,572 L491.5,572" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square"></path>
<path d="M277.5,387 L186.5,387" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M186.5,387 L197.3,390 L197.3,384 L186.5,387 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M277,573 L170,573" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M170,573 L180.8,576 L180.8,570 L170,573 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M458.5,202.5 L550,202.5" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M550,202.5 L539.2,199.5 L539.2,205.5 L550,202.5 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path d="M96,328 L96,241" id="Line" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<path id="Line-decoration-1" d="M96,241 L93,251.8 L99,251.8 L96,241 Z" stroke="#4A4A4A" stroke-width="2" stroke-linecap="square" fill="#4A4A4A"></path>
<g id="Start" transform="translate(298.000000, 8.000000)">
<rect id="Rectangle-1" stroke="#4A4A4A" stroke-width="2" fill="#EB524C" x="0" y="0" width="141" height="67" rx="10"></rect>
<text font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#FFFFFF">
<tspan x="54.5517578" y="39">Start</tspan>
</text>
</g>
<g id="Client-Credentials-Grant" transform="translate(552.000000, 169.000000)">
<rect id="Rectangle-1" stroke="#1B242E" stroke-width="2" fill="#2B3D50" x="0" y="0" width="141" height="67" rx="10"></rect>
<text id="Client-Credentials-G" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#FFFFFF">
<tspan x="11.4863281" y="31">Client Credentials</tspan>
<tspan x="52.3310547" y="48">Grant</tspan>
</text>
</g>
<g id="Auth-Code-Grant" transform="translate(552.000000, 445.000000)">
<rect id="Rectangle-1" stroke="#1B242E" stroke-width="2" fill="#2B3D50" x="0" y="0" width="141" height="67" rx="10"></rect>
<text id="Authorization-Code-G" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#FFFFFF">
<tspan x="25.8964844" y="31">Authorization</tspan>
<tspan x="32.8862305" y="48">Code Grant</tspan>
</text>
</g>
<g id="Implicit-Grant" transform="translate(26.000000, 171.000000)">
<rect id="Rectangle-1" stroke="#1B242E" stroke-width="2" fill="#2B3D50" x="0" y="0" width="141" height="67" rx="10"></rect>
<text font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#FFFFFF">
<tspan x="25.3828125" y="37">Implicit Grant</tspan>
</text>
</g>
<g id="Password-Grant" transform="translate(26.000000, 536.000000)">
<rect id="Rectangle-1" stroke="#1B242E" stroke-width="2" fill="#2B3D50" x="0" y="0" width="141" height="67" rx="10"></rect>
<text font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#FFFFFF">
<tspan x="16.8173828" y="37">Password Grant</tspan>
</text>
</g>
<g id="Access-Token-Owner?" transform="translate(278.000000, 145.000000)">
<polygon id="Polygon-1" stroke="#4A4A4A" stroke-width="2" fill="#FFFFFF" points="90 0 180 57.5 90 115 0 57.5 "></polygon>
<text id="Access-token-owner?" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="44.8657227" y="55">Access token</tspan>
<tspan x="65.1103516" y="72">owner?</tspan>
</text>
</g>
<g id="Client-Type" transform="translate(278.000000, 330.000000)">
<polygon id="Polygon-1" stroke="#4A4A4A" stroke-width="2" fill="#FFFFFF" points="90 0 180 57.5 90 115 0 57.5 "></polygon>
<text id="Client-type?" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="50.4414062" y="61">Client type?</tspan>
</text>
</g>
<g id="First-Party-Third-Party" transform="translate(278.000000, 515.000000)">
<polygon id="Polygon-1" stroke="#4A4A4A" stroke-width="2" fill="#FFFFFF" points="90 0 180 57.5 90 115 0 57.5 "></polygon>
<text id="First-party-or-third" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="47.3242188" y="51">First party or</tspan>
<tspan x="31.3828125" y="68">third party client?</tspan>
</text>
</g>
<g id="First-Party-Third-Party-Copy" transform="translate(6.000000, 329.000000)">
<polygon id="Polygon-1" stroke="#4A4A4A" stroke-width="2" fill="#FFFFFF" points="90 0 180 57.5 90 115 0 57.5 "></polygon>
<text id="First-party-or-third" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="47.3242188" y="51">First party or</tspan>
<tspan x="31.3828125" y="68">third party client?</tspan>
</text>
</g>
<text id="Machine" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="468.493164" y="192">Machine</tspan>
</text>
<text id="User" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="379.93457" y="298">User</tspan>
</text>
<text id="User-agent-based" font-family="Helvetica-Bold, Helvetica" font-size="12" font-weight="bold" fill="#4A4A4A">
<tspan x="180.319336" y="363">User-agent-based</tspan>
<tspan x="220.333008" y="377">app</tspan>
</text>
<text id="First-party" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="200.269043" y="560">First party</tspan>
</text>
<text id="First-party" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="19.269043" y="486">First party</tspan>
</text>
<text id="Third-party" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="504.441406" y="550">Third party</tspan>
</text>
<text id="Third-party" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="14.4414062" y="291">Third party</tspan>
</text>
<text id="Web-app" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="506.95752" y="418">Web app</tspan>
</text>
<text id="Native-app" font-family="Helvetica-Bold, Helvetica" font-size="14" font-weight="bold" fill="#4A4A4A">
<tspan x="290.100098" y="483">Native app</tspan>
</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -1,60 +0,0 @@
---
layout: default
title: Installation
permalink: /implementing-storage-interfaces/
---
# Implementing the storage interfaces
In order to use both the resource server and authorization server you need to implement a number of interfaces.
If you are using the resource server you need to implement the following interfaces:
* `League\OAuth2\Server\Storage\SessionInterface` - contains methods for retrieving and setting sessions
* `League\OAuth2\Server\Storage\AccessTokenInterface` - contains methods for retrieving, creating and deleting access tokens
* `League\OAuth2\Server\Storage\ClientInterface` - single method to get a client
* `League\OAuth2\Server\Storage\ScopeInterface` - single method to get a scope
If you are using the authorization server you need to implement the following interfaces:
* `League\OAuth2\Server\Storage\SessionInterface` - contains methods for retrieving and setting sessions
* `League\OAuth2\Server\Storage\AccessTokenInterface` - contains methods for retrieving, creating and deleting access tokens
* `League\OAuth2\Server\Storage\ClientInterface` - single method to get a client
* `League\OAuth2\Server\Storage\ScopeInterface` - single method to get a scope
If you are using the authorization code grant you also need to implement:
* `League\OAuth2\Server\Storage\AuthCodeInterface` - contains methods for retrieving, creating and deleting authorization codes
If you are using the refresh token grant you also need to implement:
* `League\OAuth2\Server\Storage\RefreshTokenInterface` - contains methods for retrieving, creating and deleting refresh tokens
Once you have written your class implementations then inject them into the server like so:
~~~ php
// Resource server
$sessionStorage = new Storage\SessionStorage();
$accessTokenStorage = new Storage\AccessTokenStorage();
$clientStorage = new Storage\ClientStorage();
$scopeStorage = new Storage\ScopeStorage();
$server = new ResourceServer(
$sessionStorage,
$accessTokenStorage,
$clientStorage,
$scopeStorage
);
// Authorization server
$server->setSessionStorage(new Storage\SessionStorage);
$server->setAccessTokenStorage(new Storage\AccessTokenStorage);
$server->setRefreshTokenStorage(new Storage\RefreshTokenStorage);
$server->setClientStorage(new Storage\ClientStorage);
$server->setScopeStorage(new Storage\ScopeStorage);
$server->setAuthCodeStorage(new Storage\AuthCodeStorage);
~~~
If you are using a relational database you can find some example storage implementations in the `examples/relational` folder in the codebase.
You don't have to use a database to store all of your settings

View File

@ -3,48 +3,51 @@ layout: default
title: Introduction
---
<div style="margin-top:1rem; color: #31708f; background-color: #d9edf7; padding: 15px; margin-bottom: 1rem; border: 1px solid #bcdff1; border-radius: .25rem; font-size: 1.5rem">
<p>This is the documentation for the version 5 release candidate.</p>
<p>Version 5 is stable and is the recommended version that you should implement as it is significantly simpler to work with.</p>
<p>Version 4 docs can be <a href="/V4-docs/">found here</a>.</p>
</div>
# Introduction
[![Author](http://img.shields.io/badge/author-@alexbilbie-yellow.svg?style=flat-square)](https://twitter.com/alexbilbie)
[![Author](http://img.shields.io/badge/author-@alexbilbie-red.svg?style=flat-square)](https://twitter.com/alexbilbie)
[![Source Code](http://img.shields.io/badge/source-thephpleague%2Foauth2--server-blue.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server)
[![Latest Version](http://img.shields.io/packagist/v/league/oauth2-server.svg?style=flat-square)](https://github.com/thephpleague/oauth2-server/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)<br />
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Build Status](https://img.shields.io/travis/thephpleague/oauth2-server/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-server)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/oauth2-server.svg?style=flat-square)](http://oauth2.thephpleague.com/master/)
[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-server.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-server)
[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-server.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-server)
This library makes working with OAuth 2.0 trivial. You can easily configure an OAuth 2.0 server to protect your API with access tokens, or allow clients to request new access tokens and refresh them.
`league/oauth2-server` is a library that makes implementing a standards compliant OAuth 2.0 server trivial. Your users can authenticate and authorize application clients, and protect your APIs.
It supports out of the box the following grants:
Out of the box it supports all of the grants defined in the [OAuth 2.0 Authorization Framework RFC](https://tools.ietf.org/html/rfc6749):
* Authorization code grant
* Implicit grant
* Client credentials grant
* Resource owner password credentials grant
* Refresh grant
You can also define your own grants.
<!--
You can also easily make your own [custom grants]().
-->
In addition it supports the following token types:
* Bearer tokens
* MAC tokens
* JSON web tokens (coming soon)
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
## Changelog
The changelog can be viewed here - [https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md).
The full changelog can be viewed here - [https://github.com/thephpleague/oauth2-server/releases](https://github.com/thephpleague/oauth2-server/releases).
The latest release is `4.1.3` (released 2015-03-22):
The latest release is [![GitHub tag](https://img.shields.io/github/tag/thephpleague/oauth2-server.svg)](https://github.com/thephpleague/oauth2-server/releases)
* Docblock, namespace and inconsistency fixes (Issue #303)
* Docblock type fix (Issue #310)
* Example bug fix (Issue #300)
* Updated league/event to ~2.1 (Issue #311)
* Fixed missing session scope (Issue #319)
* Updated interface docs (Issue #323)
* `.travis.yml` updates
## Support
## Questions?
Please ask questions on the [Github issues page](https://github.com/thephpleague/oauth2-server/issues).
This library was created by Alex Bilbie. Find him on Twitter at [@alexbilbie](https://twitter.com/alexbilbie).
<!--
For commercial support and custom implementations please visit [Glynde Labs](https://glyndelabs.com).
-->

View File

@ -6,12 +6,41 @@ permalink: /installation/
# Installation
The recommended installation method is via Composer.
The recommended installation method is using [Composer](https://getcomposer.org).
The following versions of PHP are supported:
* PHP 5.5 (>=5.5.9)
* PHP 5.6
* PHP 7.0
* HHVM
In your project root just run:
~~~shell
{% highlight shell %}
$ composer require league/oauth2-server
~~~
{% endhighlight %}
Ensure that youve set up your project to [autoload Composer-installed packages](https://getcomposer.org/doc/00-intro.md#autoloading).
Depending on [which grant]() you are implementing you will need to implement a number of repository interfaces. Each grant documentation page lists which repositories are required, and each repository interface has it's own documentation page.
The repositories are expected to return (on success) instances of [entity interfaces](https://github.com/thephpleague/oauth2-server/tree/V5-WIP/src/Entities/Interfaces); to make integration with your existing entities and models as easy as possible though, all required methods have been implemented as traits that you can use.
## Generating public and private keys
To generate the private key run this command on the terminal:
{% highlight shell %}
openssl genrsa -out private.key 1024
{% endhighlight %}
then extract the public key from the private key:
{% highlight shell %}
openssl rsa -in private.key -pubout > public.key
{% endhighlight %}
The private key must be kept secret (i.e. out of the web-root of the authorization server). The authorization server also requires the public key.
The public key should be distributed to any services (for example resource servers) that validate access tokens.

View File

@ -0,0 +1,29 @@
---
layout: default
title: AccessTokenRepositoryInterface documentation
permalink: /access-token-repository-interface/
---
# Access Token Repository Interface
## persistNewAccessToken() : void
When a new access token is created this method will be called. You don't have to do anything here but for auditing you probably want to.
The access token entity passed in has a number of methods you can call which contain data worth saving to a database:
* `getIdentifier() : string` this is randomly generated unique identifier (of 80+ characters in length) for the access token.
* `getExpiryDateTime() : \DateTime` the expiry date and time of the access token.
* `getUserIdentifier() : string|null` the user identifier represented by the access token.
* `getScopes() : ScopeEntityInterface[]` an array of scope entities
* `getClient()->getIdentifier() : string` the identifier of the client who requested the access token.
JWT access tokens contain an expiry date and so will be rejected automatically when used. You can safely clean up expired access tokens from your database.
## revokeAccessToken() : void
This method is called when a refresh token is used to reissue an access token. The original access token is revoked a new access token is issued.
## isAccessTokenRevoked() : boolean
This method is called when an access token is validated by the resource server middleware. Return `true` if the access token has been manually revoked before it expired. If the token is still valid return `false`.

View File

@ -0,0 +1,29 @@
---
layout: default
title: AuthCodeRepositoryInterface documentation
permalink: /auth-code-repository-interface/
---
# Auth Code Repository Interface
## persistNewAuthCode() : void
When a new access token is created this method will be called. You don't have to do anything here but for auditing you probably want to.
The access token entity passed in has a number of methods you can call which contain data worth saving to a database:
* `getIdentifier() : string` this is randomly generated unique identifier (of 80+ characters in length) for the access token.
* `getExpiryDateTime() : \DateTime` the expiry date and time of the access token.
* `getUserIdentifier() : string|null` the user identifier represented by the access token.
* `getScopes() : ScopeEntityInterface[]` an array of scope entities
* `getClient()->getIdentifier() : string` the identifier of the client who requested the access token.
JWT access tokens contain an expiry date and so will be rejected automatically when used. You can safely clean up expired access tokens from your database.
## revokeAuthCode() : void
This method is called when an authorization code is exchanged for an access token.
## isAuthCodeRevoked() : boolean
This method is called before an authorization code is exchanged for an access token by the authorization server. Return `true` if the auth code has been manually revoked before it expired. If the auth code is still valid return `false`.

View File

@ -0,0 +1,19 @@
---
layout: default
title: ClientRepositoryInterface documentation
permalink: /client-repository-interface/
---
# Client Repository Interface
## getClientEntity() : ClientEntityInterface
This method is called to validate a client's credentials.
The client secret may or may not be provided depending on the request sent by the client. If the client secret is sent it must be validated.
If the grant type is equal to `client_credentials` you should always validate the client secret.
You can use the grant type to determine if the client is permitted to use the grant type.
If the client's credentials are validated you should return an instance of `\League\OAuth2\Server\Entities\Interfaces\ClientEntityInterface`

View File

@ -0,0 +1,27 @@
---
layout: default
title: RefreshTokenRepositoryInterface documentation
permalink: /refresh-token-repository-interface/
---
# Refresh Token Repository Interface
## persistNewRefreshToken() : void
When a new refresh token is created this method will be called. You don't have to do anything here but for auditing you might want to.
The refresh token entity passed in has a number of methods you can call which contain data worth saving to a database:
* `getIdentifier() : string` this is randomly generated unique identifier (of 80+ characters in length) for the refresh token.
* `getExpiryDateTime() : \DateTime` the expiry date and time of the access token.
* `getAccessToken()->getIdentifier() : string` the linked access token's identifier.
JWT access tokens contain an expiry date and so will be rejected automatically when used. You can safely clean up expired access tokens from your database.
## revokeRefreshToken() : void
This method is called when a refresh token is used to reissue an access token. The original refresh token is revoked a new refresh token is issued.
## isRefreshTokenRevoked() : boolean
This method is called when an refresh token is used to issue a new access token. Return `true` if the refresh token has been manually revoked before it expired. If the token is still valid return `false`.

View File

@ -0,0 +1,23 @@
---
layout: default
title: ScopeRepositoryInterface documentation
permalink: /scope-repository-interface/
---
# Scope Repository Interface
## getScopeEntityByIdentifier() : ScopeEntityInterface
This method is called to validate a scope.
If the scope is valid validated you should return an instance of `\League\OAuth2\Server\Entities\Interfaces\ScopeEntityInterface`
## finalizeScopes() : ScopeEntityInterface[]
This method is called right before an access token or authorization code is created.
Given a client, grant type and optional user identifier validate the set of scopes requested are valid and optionally append additional scopes or remove requested scopes.
This method is useful for integrating with your own app's permissions system.
You must return an array of `ScopeEntityInterface` instances; either the original scopes or an updated set.

View File

@ -0,0 +1,17 @@
---
layout: default
title: UserRepositoryInterface documentation
permalink: /user-repository-interface/
---
# User Repository Interface
## getUserEntityByUserCredentials() : UserEntityInterface
This method is called to validate a user's credentials.
You can use the grant type to determine if the user is permitted to use the grant type.
You can use the client entity to determine to if the user is permitted to use the client.
If the client's credentials are validated you should return an instance of `\League\OAuth2\Server\Entities\Interfaces\UserEntityInterface`

11
requirements.md Executable file
View File

@ -0,0 +1,11 @@
---
layout: default
title: Requirements
permalink: /requirements/
---
# Requirements
In order to prevent man-in-the-middle attacks, the authorization server MUST require the use of TLS with server authentication as defined by [RFC2818](https://tools.ietf.org/html/rfc2818) for any request sent to the authorization and token endpoints. The client MUST validate the authorization server's TLS certificate as defined by [RFC6125](https://tools.ietf.org/html/rfc6125) and in accordance with its requirements for server identity authentication.
This library uses key cryptography in order to encrypt and decrypt, as well as verify the integrity of signatures. See the [installation](/installation) page for details on how to generate the keys.

View File

@ -6,158 +6,47 @@ permalink: /resource-server/securing-your-api/
# Securing your API
This library provides a PSR-7 friendly resource server middleware that can validate access tokens.
## Setup
Wherever you intialise your objects, initialize a new instance of the resource server with the storage interfaces:
Wherever you intialize your objects, initialize a new instance of the resource server with the storage interfaces:
~~~ php
$sessionStorage = new Storage\SessionStorage();
$accessTokenStorage = new Storage\AccessTokenStorage();
$clientStorage = new Storage\ClientStorage();
$scopeStorage = new Storage\ScopeStorage();
{% highlight php %}
// Init our repositories
$clientRepository = new ClientRepository();
$accessTokenRepository = new AccessTokenRepository();
$scopeRepository = new ScopeRepository();
$server = new ResourceServer(
$sessionStorage,
$accessTokenStorage,
$clientStorage,
$scopeStorage
// Path to public and private keys
$privateKeyPath = 'file://path/to/private.key';
$publicKeyPath = 'file://path/to/public.key';
// Setup the authorization server
$server = new \League\OAuth2\Server\Server(
$clientRepository,
$accessTokenRepository,
$scopeRepository,
$privateKeyPath,
$publicKeyPath
);
~~~
{% endhighlight %}
Then add the middleware to your stack:
{% highlight php %}
new \League\OAuth2\Server\Middleware\ResourceServerMiddleware($server);
{% endhighlight %}
## Implementation
## Checking for valid access tokens
The authorization header on an incoming request will automatically be validated.
Before your API responds you need to check that an access token has been presented with the request (either in the query string `?access_token=abcdef` or as an authorization header `Authorization: Bearer abcdef`).
If the access token is valid the following attributes will be set on the ServerRequest:
If youre using a framework such as Laravel or Symfony you could use a route filter to do this. With the Slim framework you would use middleware.
* `oauth_access_token_id` - the access token identifier
* `oauth_client_id` - the client identifier
* `oauth_user_id` - the user identifier represented by the access token
* `oauth_scopes` - an array of string scope identifiers
This example uses Orno\Route:
~~~ php
try {
// Check that an access token is present and is valid
$server->isValidRequest();
// A successful response
$response = $dispatcher->dispatch(
$request->getMethod(),
$request->getPathInfo()
);
} catch (\League\OAuth2\Server\Exception\OAuthException $e) {
// Catch an OAuth exception
$response = new Response(json_encode([
'error' => $e->errorType,
'message' => $e->getMessage()
]), $e->httpStatusCode);
foreach ($e->getHttpHeaders() as $header) {
$response->headers($header);
}
} catch (\Orno\Http\Exception $e) {
// A failed response (thrown by code)
$response = $e->getJsonResponse();
$response->setContent(json_encode(['status_code' => $e->getStatusCode(), 'message' => $e->getMessage()]));
} catch (\Exception $e) {
// Other server error (500)
$response = new Orno\Http\Response;
$response->setStatusCode(500);
$response->setContent(json_encode(['status_code' => 500, 'message' => $e->getMessage()]));
} finally {
// Return the response
$response->headers->set('Content-type', 'application/json');
$response->send();
}
~~~
When `$server->isValidRequest()` is called the library will run the following tasks:
* Check if an access token is present in the query string
* If not, check if an access token is contained in an `authorization` header.
* If not, throw `League\OAuth2\Server\Exception\InvalidAccessTokenException`
* Check if the access token is valid with the database
* If not, throw `League\OAuth2\Server\Exception\AccessDeniedException`
* If the access token is valid:
* Get the token's owner type (e.g. “user” or “client”) and their ID
* Get a list of any scopes that are associated with the access token
Assuming an exception isnt thrown you can then use the following functions in your API code:
* `$server->getAccessToken()->getSession()->getOwnerType()` - This will return the type of the owner of the access token. For example if a user has authorized another client to use their resources the owner type would be “user”.
* `$server->getAccessToken()->getSession()->getOwnerId()` - This will return the ID of the access token owner. You can use this to check if the owner has permission to do take some sort of action (such as retrieve a document or upload a file to a folder).
* `$server->getAccessToken()->getSession()->getClient()->getId()` - Returns the ID of the client that was involved in creating the session that the access token is linked to.
* `$server->getAccessToken()` - Returns the access token used in the request.
* `$server->getAccessToken()->hasScope()` - You can use this function to see if a specific scope (or several scopes) has been associated with the access token. You can use this to limit the contents of an API response or prevent access to an API endpoint without the correct scope.
* `$server->getAccessToken()->getScopes()` - Returns all scopes attached to the access token.
## A simple example
This example endpoint will return a users information if a valid access token is present. If the access token has the `email` scope then the user's email address will be included in the response. Likewise if the `photo` scope is available the user's photo is included.
~~~ php
$router->get('/users/{username}', function (Request $request, $args) use ($server) {
$result = (new Model\Users())->get($args['username']);
if (count($result) === 0) {
throw new NotFoundException();
}
$user = [
'username' => $result[0]['username'],
'name' => $result[0]['name']
];
if ($server->hasScope('email')) {
$user['email'] = $result[0]['email'];
}
if ($server->hasScope('photo')) {
$user['photo'] = $result[0]['photo'];
}
return new Response(json_encode($user));
});
~~~
## Limiting an endpoint to a specific owner type
In this example, only a users access token is valid:
~~~ php
if ($server->getAccessToken()->getSession()->getOwnerType() !== 'user') {
throw new Exception\AccessDeniedException;
}
~~~
## Limiting an endpoint to a specific owner type and scope
In this example, the endpoint will only respond to access tokens that are owner by client applications and that have the scope `users.list`.
~~~ php
if ($server->getAccessToken()->getSession()->getOwnerType() !== 'client' && $server->getAccessToken()->hasScope('users.list')) {
throw new Exception\AccessDeniedException;
}
~~~
You might secure an endpoint in this way to only allow specific clients (such as your applications main website) access to private APIs.
## Return resource based on access token owner
~~~ php
$photos = $model->getPhotos($server->getAccessToken()->getSession()->getOwnerId());
~~~
Hopefully you can see how easy it is to secure an API with OAuth 2.0 and how you can use scopes to limit response contents or access to endpoints.
If the authorization is invalid an instance of `OAuthServerException::accessDenied` will be thrown.

View File

@ -13,3 +13,4 @@ permalink: /terminology/
* `Grant` - A grant is a method of acquiring an access token.
* `Resource server` - A server which sits in front of protected resources (for example "tweets", users' photos, or personal data) and is capable of accepting and responsing to protected resource requests using access tokens.
* `Scope` - A permission.
* `JWT` - A JSON Web Token is a method for representing claims securely between two parties as defined in [RFC 7519](https://tools.ietf.org/html/rfc7519).