This commit is contained in:
Alex Bilbie 2016-03-24 13:55:10 +00:00
parent 317678cee4
commit a120586a98
6 changed files with 121 additions and 179 deletions

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

@ -3,12 +3,14 @@ Getting Started:
Terminology: '/terminology/'
Requirements: '/requirements/'
Installation: '/installation/'
#Framework Integrations: '/framework-integrations/'
Database Setup: '/database-setup/'
Framework Integrations: '/framework-integrations/'
V4 Docs: '/V4-docs/'
Authorization Server:
'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/'
'Authorization Code Grant': '/authorization-server/auth-code-grant/'
# 'Implict Grant': '/authorization-server/auth-code-grant/'
# 'Refresh Token Grant': '/authorization-server/refresh-token-grant/'
# 'Creating custom grants': '/authorization-server/custom-grants/'

View File

@ -1,198 +1,117 @@
---
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);
$authCodeGrant = new \League\OAuth2\Server\Grant\AuthCodeGrant();
$server->addGrantType($authCodeGrant);
~~~
// 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 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 `/access_token` 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 {
$authParams = $server->getGrantType('authorization_code')->checkAuthorizeParams();
} 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;
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 %}
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`.
try {
$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()
);
}
});
~~~
### Notes
* You could combine the sign-in form and authorize form into one form

View File

@ -1,6 +1,6 @@
---
layout: default
title: Authorization server with client credentials grant
title: Client credentials grant
permalink: /authorization-server/client-credentials-grant/
---

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.

View File

@ -6,5 +6,6 @@ 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.