diff --git a/V4-docs.md b/V4-docs.md new file mode 100644 index 00000000..7dfaa3e9 --- /dev/null +++ b/V4-docs.md @@ -0,0 +1,9 @@ +--- +layout: default +title: V4 Docs +permalink: /v4-docs/ +--- + +# V4 Docs + +Checkout the `gh-pages-v4` branch and run `jekyll serve` \ No newline at end of file diff --git a/_data/menu.yml b/_data/menu.yml index 74357333..06b90d2b 100644 --- a/_data/menu.yml +++ b/_data/menu.yml @@ -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/' diff --git a/auth-server-auth-code.md b/auth-server-auth-code.md index 759f409a..c00b3747 100755 --- a/auth-server-auth-code.md +++ b/auth-server-auth-code.md @@ -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 -

getName() ?> would like to access:

- - - -
- - -
-~~~ - - - - -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 diff --git a/auth-server-client-credentials.md b/auth-server-client-credentials.md index c57845ce..111de611 100755 --- a/auth-server-client-credentials.md +++ b/auth-server-client-credentials.md @@ -1,6 +1,6 @@ --- layout: default -title: Authorization server with client credentials grant +title: Client credentials grant permalink: /authorization-server/client-credentials-grant/ --- diff --git a/database-setup.md b/database-setup.md new file mode 100755 index 00000000..03b65626 --- /dev/null +++ b/database-setup.md @@ -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. \ No newline at end of file diff --git a/framework-integrations.md b/framework-integrations.md index 327b2666..30ee14ed 100644 --- a/framework-integrations.md +++ b/framework-integrations.md @@ -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. \ No newline at end of file