oauth2-server/auth-server-auth-code.md

166 lines
6.8 KiB
Markdown
Raw Normal View History

2014-10-01 03:14:18 +05:30
---
layout: default
2016-03-24 19:25:10 +05:30
title: Authorization code grant
2014-10-01 03:14:18 +05:30
permalink: /authorization-server/auth-code-grant/
---
2016-03-24 19:25:10 +05:30
# Authorization code grant
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
The authorization code grant should be very familiar if you've ever signed into a web app using your Facebook or Google account.
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
## Flow
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
### Part One
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
The client will redirect the user to the authorization server with the following parameters in the query string:
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
* `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
2016-04-17 16:33:45 +05:30
* `state` with a [CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) token. This parameter is optional but highly recommended. You should store the value of the CSRF token in the user's session to be validated when they return.
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
All of these parameters will be validated by the authorization server.
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
The user will then be asked to login to the authorization server and approve the client.
2014-10-01 03:14:18 +05:30
2016-05-19 16:47:03 +05:30
If the user approves the client they will be redirected from the authorisation server to the client's redirect URI with the following parameters in the query string:
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
* `code` with the authorization code
2016-04-17 16:33:45 +05:30
* `state` with the state parameter sent in the original request. You should compare this value with the value stored in the user's session to ensure the authorization code obtained is in response to requests made by this client rather than another client application.
2014-11-08 22:35:18 +05:30
2016-03-24 19:25:10 +05:30
### Part Two
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
The client will now send a POST request to the authorization server with the following parameters:
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
* `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
2016-05-19 17:51:04 +05:30
* `code` with the authorization code from the query string **(remember to url decode it first)**
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
The authorization server will respond with a JSON object containing the following properties:
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
* `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.
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
## Setup
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
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
2016-04-17 16:33:45 +05:30
$clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface
$scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface
$accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface
$authCodeRepository = new AuthCodeRepository(); // instance of AuthCodeRepositoryInterface
$refreshTokenRepository = new RefreshTokenRepository(); // instance of RefreshTokenRepositoryInterface
2016-03-24 19:25:10 +05:30
2016-03-29 13:35:49 +05:30
$privateKey = 'file://path/to/private.key';
2016-04-17 16:33:45 +05:30
//$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase
2016-03-29 13:35:49 +05:30
$publicKey = 'file://path/to/public.key';
2016-03-24 19:25:10 +05:30
// Setup the authorization server
2016-04-17 17:46:40 +05:30
$server = new \League\OAuth2\Server\AuthorizationServer(
2016-03-24 19:25:10 +05:30
$clientRepository,
$accessTokenRepository,
$scopeRepository,
2016-03-29 13:35:49 +05:30
$privateKey,
$publicKey
2016-03-24 19:25:10 +05:30
);
2016-04-17 16:33:45 +05:30
$grant = new \League\OAuth2\Server\Grant\AuthCodeGrant(
$authCodeRepository,
$refreshTokenRepository,
new \DateInterval('PT10M') // authorization codes will expire after 10 minutes
);
$grant->setRefreshTokenTTL(new \DateTime('P1M')); // refresh tokens will expire after 1 month
// Enable the authentication code grant on the server
2016-03-24 19:25:10 +05:30
$server->enableGrantType(
2016-04-17 16:33:45 +05:30
$grant,
new \DateInterval('PT1H') // access tokens will expire after 1 hour
2016-03-24 19:25:10 +05:30
);
{% endhighlight %}
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
## Implementation
2014-10-01 03:14:18 +05:30
2016-04-17 16:33:45 +05:30
_Please note: These examples here demonstrate usage with the Slim Framework; Slim is not a requirement to use this library, you just need something that generates PSR7-compatible HTTP requests and responses._
2016-04-10 21:34:24 +05:30
The client will redirect the user to an authorization endpoint.
2014-10-01 03:14:18 +05:30
2016-03-24 19:25:10 +05:30
{% highlight php %}
2016-04-10 21:34:24 +05:30
$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
2016-04-17 16:33:45 +05:30
2016-04-17 17:46:40 +05:30
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
2016-04-17 16:33:45 +05:30
2016-03-24 19:25:10 +05:30
try {
2016-04-17 16:33:45 +05:30
2016-04-10 21:34:24 +05:30
// Validate the HTTP request and return an AuthorizationRequest object.
$authRequest = $server->validateAuthorizationRequest($request);
// The auth request object can be serialized and saved into a user's session.
// You will probably want to redirect the user at this point to a login endpoint.
// Once the user has logged in set the user on the AuthorizationRequest
2016-04-17 16:33:45 +05:30
$authRequest->setUser(new UserEntity()); // an instance of UserEntityInterface
2016-04-10 21:34:24 +05:30
// At this point you should redirect the user to an authorization page.
// This form will ask the user to approve the client and the scopes requested.
// Once the user has approved or denied the client update the status
// (true = approved, false = denied)
$authRequest->setAuthorizationApproved(true);
// Return the HTTP redirect response
return $server->completeAuthorizationRequest($authRequest, $response);
} catch (OAuthServerException $exception) {
2016-04-17 16:33:45 +05:30
// All instances of OAuthServerException can be formatted into a HTTP response
2016-03-24 19:25:10 +05:30
return $exception->generateHttpResponse($response);
2016-04-10 21:34:24 +05:30
2016-03-24 19:25:10 +05:30
} catch (\Exception $exception) {
2016-04-17 16:33:45 +05:30
// Unknown exception
2016-03-24 19:25:10 +05:30
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
2016-04-17 16:33:45 +05:30
2014-10-01 03:14:18 +05:30
}
});
2016-03-24 19:25:10 +05:30
{% endhighlight %}
2014-10-01 03:14:18 +05:30
2016-04-10 21:34:24 +05:30
The client will request an access token using an authorization code so create an `/access_token` endpoint.
2016-03-24 20:17:43 +05:30
{% highlight php %}
2016-04-10 21:34:24 +05:30
$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
2016-03-24 20:17:43 +05:30
2016-04-17 17:46:40 +05:30
/* @var \League\OAuth2\Server\AuthorizationServer $server */
$server = $app->getContainer()->get(AuthorizationServer::class);
2016-03-24 20:17:43 +05:30
2016-04-10 21:34:24 +05:30
try {
2016-04-17 16:33:45 +05:30
// Try to respond to the request
2016-04-10 21:34:24 +05:30
return $server->respondToAccessTokenRequest($request, $response);
2016-03-24 20:17:43 +05:30
2016-04-10 21:34:24 +05:30
} catch (\League\OAuth2\Server\Exception\OAuthServerException $exception) {
2016-04-17 16:33:45 +05:30
// All instances of OAuthServerException can be formatted into a HTTP response
2016-04-10 21:34:24 +05:30
return $exception->generateHttpResponse($response);
2016-04-17 16:33:45 +05:30
2016-04-10 21:34:24 +05:30
} catch (\Exception $exception) {
2016-04-17 16:33:45 +05:30
// Unknown exception
2016-04-10 21:34:24 +05:30
$body = new Stream('php://temp', 'r+');
$body->write($exception->getMessage());
return $response->withStatus(500)->withBody($body);
}
});
2016-03-24 20:17:43 +05:30
{% endhighlight %}