Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/19' into release-1.0.0
Browse files Browse the repository at this point in the history
Forward port #19
  • Loading branch information
weierophinney committed Mar 15, 2018
2 parents f441c30 + f2ea781 commit 6994359
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 20 deletions.
13 changes: 7 additions & 6 deletions bin/generate-keys.php
Expand Up @@ -13,6 +13,7 @@

$filePrivateKey = dirname(__DIR__) . '/data/private.key';
$filePublicKey = dirname(__DIR__) . '/data/public.key';
$fileEncryptionKey = dirname(__DIR__) . '/data/encryption.key';

// Generate public/private keys with OpenSSL
$config = [
Expand All @@ -24,14 +25,14 @@
$res = openssl_pkey_new($config);
openssl_pkey_export($res, $privateKey);
file_put_contents($filePrivateKey, $privateKey);
printf("Private key stored in:\n%s\n", $filePrivateKey);

// Public key
$publicKey = openssl_pkey_get_details($res);
file_put_contents($filePublicKey, $publicKey["key"]);

printf("Private key stored in:\n%s\n", $filePrivateKey);
printf("Public key stored in:\n%s\n", $filePublicKey);
printf(
"Encryption key (copy & paste in config/oauth2.php):\n%s\n",
base64_encode(random_bytes(32))
);

// Encryption key
$encKey = base64_encode(random_bytes(32));
file_put_contents($fileEncryptionKey, sprintf("<?php return '%s';", $encKey));
printf("Encryption key stored in:\n%s\n", $fileEncryptionKey);
14 changes: 7 additions & 7 deletions config/oauth2.php
@@ -1,5 +1,5 @@
<?php
/**
/**
* To generate a private key run this command:
* openssl genrsa -out private.key 1024
*
Expand All @@ -10,14 +10,14 @@
* @see http://php.net/manual/en/class.dateinterval.php
*/
return [
'private_key' => __DIR__ . '/../data/private.key',
'public_key' => __DIR__ . '/../data/public.key',
'encryption_key' => 'iALlwJ1sH77dmFCJFo+pMdM6Af4bF/hCca1EDDx7MwE=',
'access_token_expire' => 'P1D', // 1 day in DateInterval format
'private_key' => __DIR__ . '/../data/private.key',
'public_key' => __DIR__ . '/../data/public.key',
'encryption_key' => require __DIR__ . '/../data/encryption.key',
'access_token_expire' => 'P1D', // 1 day in DateInterval format
'refresh_token_expire' => 'P1M', // 1 month in DateInterval format
'auth_code_expire' => 'PT10M', // 10 minutes in DateInterval format
'auth_code_expire' => 'PT10M', // 10 minutes in DateInterval format
'pdo' => [
'dsn' => '',
'dsn' => '',
'username' => '',
'password' => ''
]
Expand Down
5 changes: 2 additions & 3 deletions data/.gitignore
@@ -1,3 +1,2 @@
private.key
public.key
oauth2.sqlite
*.key
*.sqlite
9 changes: 9 additions & 0 deletions data/oauth2_test.sql
@@ -0,0 +1,9 @@
INSERT INTO oauth_clients (name, secret, redirect, personal_access_client, password_client)
VALUES ('client_test', '$2y$10$fFlZTo2Syqa./0JJ2QKV4O/Nfi9cqDMcwHBkN/WMcRLLlaxYUP2CK', '/redirect', 1, 1),
('client_test2', '$2y$10$fFlZTo2Syqa./0JJ2QKV4O/Nfi9cqDMcwHBkN/WMcRLLlaxYUP2CK', '/redirect', 0, 0);

INSERT INTO oauth_users (username, password)
VALUES ('user_test', '$2y$10$DW12wQQvr4w7mQ.uSmz37OQkKcIZrRZnpXWoYue7b5v8E/pxvsAru');

INSERT INTO oauth_scopes (id)
VALUES ('test');
76 changes: 76 additions & 0 deletions docs/book/grant/auth_code.md
@@ -0,0 +1,76 @@
# Authorization code

The authorization code is used to authenticate a web application with a
third-party service (e.g., imagine you built a web application that needs to
consume the API of Facebook). You can authenticate your application using the
third-party server with a 4-step flow as illustrated in this diagram:

![Authorization code diagram](auth_code.png)

The web application sends a request (including the `client_id` and the
`redirect_uri`) to the authorization server asking for an Authorization code (1).
The authorization server shows an Allow/Deny page to the end-user requesting
authorization for access. If the user clicks on "Allow", the server sends the
authorization code to the web application using the `redirect_uri` (2).
The web application can now perform a token request, passing the `client_id`,
the `redirect_uri`, the `client_secret`, and the authentication code to prove
that it is authorized to perform the request (3). The authorization server sends
the access token in response if the request is valid (4).

## Request the authorization code

The client sends the following parameters via query string arguments to the
authorization server:

- `response_type` = code.
- `client_id` with the client identifer.
- `redirect_uri` with the URI to which to redirect the client following
successful authorization. This parameter is optional, but if it is not sent,
the user will be redirected to a default location on completion.
- `scope` with a space-delimited list of requested scope permissions.
- `state` with a Cross-Site Request Forgery (CSRF) token. This parameter is
optional, but highly recommended. You can store the value of the CSRF token in
the user’s session to be validated in the next step.

The user will then be asked to login to the authorization server and approve the
client request. If the user approves the request they will be redirected to the
redirect URI with the following parameters in the query string arguments:

- `code` with the authorization code.
- `state` with the CSRF parameter sent in the original request. You can compare
this value with the one stored in the user’s session.

## Request the access token

The client sends a POST request to the authorization server with the following
parameters:

- `grant_type` = authorization_code.
- `client_id` with the client’s ID.
- `client_secret` with the client’s secret.
- `redirect_uri` with the previous client redirect URI.
- `code` with the authorization code as returned in the authorization code
request (as detailed in the previous section).

The authorization server responds with a JSON payload similar to the following:

```json
{
"token_type" : "Bearer",
"expires_in" : "3600",
"refresh_token" : "YWYwNjhmNmZmMDhmZjkyOGJj...",
"access_token" : "eyJ0eXAiOiJKV1Q..."
}
```

The values are as follows:

- The `token_type` is the type of generated token (here, and generally,
"Bearer").
- The `expires_in` value is an integer representing the time-to-live (in
seconds) of the access token.
- The `refresh_token` is a token that can be used to refresh the `access_token`
when expired.
- The `access_token` contains a JSON Web Token (JWT) signed with the
authorization server’s private key. This token must be used in the
`Authorization` request HTTP header on subsequent requests.
Binary file added docs/book/grant/auth_code.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions docs/book/grant/client_credentials.md
@@ -0,0 +1,31 @@
# Client credentials

The client credentials grant is used in machine-to-machine scenarios: for
example, a client making API requests that do not require a user's permission.

The client sends a `POST` request with following body parameters to the
authorization server:

- `grant_type` = client_credentials.
- `client_id` with the client's ID.
- `client_secret` with the client's secret.
- `scope` with a space-delimited list of requested scope permissions.

The authorization server responds with a JSON payload as follows:

```json
{
"token_type" : "Bearer",
"expires_in" : "3600",
"access_token" : "eyJ0eXAiOiJKV1Q..."
}
```

The values returned are as follows:

- The `token_type` is the type of generated token (here, and generally, Bearer).
- `expires_in` is an integer representing the time-to-live (in seconds) of the
access token.
- The `access_token` contains a JSON Web Token (JWT) signed with the
authorization server’s private key. This token must be used in the
`Authorization` request HTTP header in subsequent requests.
35 changes: 35 additions & 0 deletions docs/book/grant/implicit.md
@@ -0,0 +1,35 @@
# Implicit grant

The implicit grant is similar to the [authorization code](auth_code.md) grant,
with two differences: it's used for user-agent-based clients (e.g. single page
applications) that cannot store a secret in a secure way; additionally, the
authorization server returns the access token directly, without the need of an
authorization code.

The client sends the following parameter via a query string argument to the
authorization server:

- `response_type` = token.
- `client_id`, with the client’s ID.
- `redirect_uri`, with the URI to which to redirect the client after completing
authorization. This parameter is optional; if not provided, however, the user
will be redirected to a default location.
- `scope`, with a space-delimited list of requested scope permissions.
- `state`, with a Cross-Site Request Forgery (CSRF) token. This parameter is
optional but highly recommended. You can store the value of CSRF token in the
user’s session to be validated in the next step.

The user will then be asked to login to the authorization server and approve the
client request. If the user approves the request they will be redirected to the
redirect URI with the following parameters in the query string arguments:

- `token_type` = Bearer.
- `expires_in`, an integer representing the time-to-live (in seconds) of the
access token.
- `access_token`, the access token represented by a JSON Web Token (JWT) signed
with the authorization server’s private key.
- `state`, with the CSRF parameter sent in the original request. You can compare
this value with the one stored in the user’s session.

Refresh tokens are not to be issued for `implicit` grants. This is a security
restriction coming from the OAuth2 specification, [RFC 6749](https://tools.ietf.org/html/rfc6749#page-35).
34 changes: 34 additions & 0 deletions docs/book/grant/password.md
@@ -0,0 +1,34 @@
# Password

This use case can be used to authenticate an API with user's password grant.
The typical scenario includes a Login web page with username and password that
is used to authenticate against a first-party API. Password grant is only
appropriate for **trusted clients**. If you build your own website as a client
of your API, then this is a great way to handle logging in.

The client sends a POST request with following parameters:

- `grant_type` = password;
- `client_id` with 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 responds with a JSON as follows:

```json
{
"token_type" : "Bearer",
"expires_in" : "3600",
"refresh_token" : "YWYwNjhmNmZmMDhmZjkyOGJj...",
"access_token" : "eyJ0eXAiOiJKV1Q..."
}
```

The `token_type` is the type of generated token (Bearer). The `expires_in` is
an integer representing the TTL (in seconds) of the access token.
The `refresh_token` a token that can be used to refresh the `access_token` when
expired.
The `access_token` contains a `JWT` signed with the authorization server’s
private key. This token must be used in the `Authorization` request HTTP header.
39 changes: 39 additions & 0 deletions docs/book/grant/refresh_token.md
@@ -0,0 +1,39 @@
# Refresh token

The OAuth2 framework provides the ability to _refresh_ the access token,
generating a new one with a new lifetime. This action can be performed using
the `refresh_token` value, if present in the access token response.

To request a token refresh, the client needs to send a `POST` request with
the following parameters:

- `grant_type` = refresh_token.
- `refresh_token` with the refresh token.
- `client_id` with 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_ scope; you may never _expand_ scope during a refresh
operation.

The authorization server responds with a JSON payload as follows:

```json
{
"token_type" : "Bearer",
"expires_in" : "3600",
"refresh_token" : "YWYwNjhmNmZmMDhmZjkyOGJj...",
"access_token" : "eyJ0eXAiOiJKV1Q..."
}
```

The values are as follows:

- The `token_type` is the type of generated token (here, and generally, Bearer).
- `expires_in` is an integer representing the time-to-live (in seconds) of the
access token.
- The `refresh_token` a token that can be used to refresh the `access_token`
when expired.
- The `access_token` contains a JSON Web Token (JWT) signed with the
authorization server’s private key. This token must be used in the
`Authorization` request HTTP header on all subsequent requests.

0 comments on commit 6994359

Please sign in to comment.