1 |
286092dc
|
Assos Assos
|
|
2 |
|
|
This module is a complement to the module [[https://drupal.org/project/oauth2_server][oauth2_server]].
|
3 |
|
|
|
4 |
|
|
*Note:* The modules oauth2_server and oauth2_client have conflicts
|
5 |
|
|
with the module [[https://drupal.org/project/oauth2][oauth2]], so they should not be installed at the same
|
6 |
|
|
time.
|
7 |
|
|
|
8 |
|
|
* How to use it
|
9 |
|
|
|
10 |
|
|
Define oauth2 clients in your code like this:
|
11 |
|
|
#+BEGIN_EXAMPLE
|
12 |
|
|
/**
|
13 |
|
|
* Implements hook_oauth2_clients().
|
14 |
|
|
*/
|
15 |
|
|
function MYMODULE_oauth2_clients() {
|
16 |
|
|
$server_url = 'https://oauth2_server.example.org';
|
17 |
|
|
$client_url = 'https://oauth2_client.example.org';
|
18 |
|
|
|
19 |
|
|
// user-password flow
|
20 |
|
|
$oauth2_clients['test1'] = array(
|
21 |
|
|
'token_endpoint' => $server_url . '/oauth2/token',
|
22 |
|
|
'auth_flow' => 'user-password',
|
23 |
|
|
'client_id' => 'test1',
|
24 |
|
|
'client_secret' => 'test1',
|
25 |
|
|
'username' => 'user1',
|
26 |
|
|
'password' => 'user1',
|
27 |
|
|
);
|
28 |
|
|
|
29 |
|
|
// client-credentials flow
|
30 |
|
|
$oauth2_clients['test2'] = array(
|
31 |
|
|
'token_endpoint' => $server_url . '/oauth2/token',
|
32 |
|
|
'auth_flow' => 'client-credentials',
|
33 |
|
|
'client_id' => 'test2',
|
34 |
|
|
'client_secret' => 'test2',
|
35 |
|
|
);
|
36 |
|
|
|
37 |
|
|
// server-side flow
|
38 |
|
|
$oauth2_clients['test3'] = array(
|
39 |
|
|
'token_endpoint' => $server_url . '/oauth2/token',
|
40 |
|
|
'auth_flow' => 'server-side',
|
41 |
|
|
'client_id' => 'test1',
|
42 |
|
|
'client_secret' => 'test1',
|
43 |
|
|
'authorization_endpoint' => $server_url . '/oauth2/authorize',
|
44 |
|
|
'redirect_uri' => $client_url . '/oauth2/authorized',
|
45 |
|
|
);
|
46 |
|
|
|
47 |
|
|
return $oauth2_clients;
|
48 |
|
|
}
|
49 |
|
|
#+END_EXAMPLE
|
50 |
|
|
|
51 |
|
|
Then use them like this:
|
52 |
|
|
#+BEGIN_EXAMPLE
|
53 |
|
|
try {
|
54 |
|
|
$oauth2_client = oauth2_client_load('test1');
|
55 |
|
|
$access_token = $oauth2_client->getAccessToken();
|
56 |
|
|
}
|
57 |
|
|
catch (Exception $e) {
|
58 |
|
|
drupal_set_message($e->getMessage(), 'error');
|
59 |
|
|
}
|
60 |
|
|
#+END_EXAMPLE
|
61 |
|
|
|
62 |
|
|
The only thing that oauth2_client does is to get an access_token
|
63 |
|
|
from the oauth2_server, so that it can be used for accessing web
|
64 |
|
|
services.
|
65 |
|
|
|
66 |
|
|
|
67 |
|
|
* More about using it
|
68 |
|
|
|
69 |
|
|
Another form of usage is like this:
|
70 |
|
|
#+BEGIN_EXAMPLE
|
71 |
|
|
$oauth2_config = array(
|
72 |
|
|
'token_endpoint' => $server_url . '/oauth2/token',
|
73 |
|
|
'auth_flow' => 'user-password',
|
74 |
|
|
'client_id' => 'test1',
|
75 |
|
|
'client_secret' => '12345',
|
76 |
|
|
'username' => $username,
|
77 |
|
|
'password' => $password,
|
78 |
|
|
);
|
79 |
|
|
try {
|
80 |
|
|
$oauth2_client = new OAuth2\Client($oauth2_config, $client_id);
|
81 |
|
|
$access_token = $oauth2_client->getAccessToken();
|
82 |
|
|
}
|
83 |
|
|
catch (Exception $e) {
|
84 |
|
|
drupal_set_message($e->getMessage(), 'error');
|
85 |
|
|
}
|
86 |
|
|
#+END_EXAMPLE
|
87 |
|
|
|
88 |
|
|
|
89 |
|
|
* Custom usage
|
90 |
|
|
|
91 |
|
|
Sometimes (or rather often) oauth2 servers have special requirements
|
92 |
|
|
that are different from the OAuth2 standard and different from other
|
93 |
|
|
oauth2 implementations. This client cannot possibly cover all these
|
94 |
|
|
special requirements. In such a case, a possible solution can be to
|
95 |
|
|
extend the class *OAuth2\Client* like this:
|
96 |
|
|
#+BEGIN_EXAMPLE
|
97 |
|
|
<?php
|
98 |
|
|
namespace OAuth2;
|
99 |
|
|
|
100 |
|
|
class MyClient extends Client {
|
101 |
|
|
protected function getToken($data) {
|
102 |
|
|
// Implement the custom logic that is needed by the oauth2 server.
|
103 |
|
|
}
|
104 |
|
|
}
|
105 |
|
|
#+END_EXAMPLE
|
106 |
|
|
|
107 |
|
|
And then use it like this:
|
108 |
|
|
#+BEGIN_EXAMPLE
|
109 |
|
|
try {
|
110 |
|
|
$oauth2_client = new OAuth2\MyClient($oauth2_config);
|
111 |
|
|
$access_token = $oauth2_client->getAccessToken();
|
112 |
|
|
}
|
113 |
|
|
catch (Exception $e) {
|
114 |
|
|
drupal_set_message($e->getMessage(), 'error');
|
115 |
|
|
}
|
116 |
|
|
#+END_EXAMPLE
|
117 |
|
|
|
118 |
|
|
|
119 |
|
|
* How it works
|
120 |
|
|
|
121 |
|
|
An access token and its related data are stored on the session
|
122 |
|
|
($_SESSION['oauth2_client']['token'][$client_id]), so that it can be
|
123 |
|
|
reused while it is not expired yet. The data that are stored for
|
124 |
|
|
each token are: access_token, expires_in, token_type, scope,
|
125 |
|
|
refresh_token and expiration_time. They are the values that come
|
126 |
|
|
from the oauth2 server, except the last one, which is calculated as
|
127 |
|
|
(REQUEST_TIME + expires_in).
|
128 |
|
|
|
129 |
|
|
When the token has expired (expiration_time > time() + 10), a new
|
130 |
|
|
token is requested from the oauth2 server, using the refresh_token.
|
131 |
|
|
If the refresh token fails for some reason (maybe refresh_token
|
132 |
|
|
expired or any other reason), then the whole process of
|
133 |
|
|
authorization is performed from the beginning.
|
134 |
|
|
|
135 |
|
|
For the client-credentials and user-password authorization flows
|
136 |
|
|
this does not involve a user interaction with the oauth2 server.
|
137 |
|
|
|
138 |
|
|
However, for the server-side flow the user has to authorize again
|
139 |
|
|
the application. This is done in these steps, first the user is
|
140 |
|
|
redirected to the oauth2 server to authorize the application again,
|
141 |
|
|
from there it is redirected back to the application with an
|
142 |
|
|
authorization code, then the application uses the authorization code
|
143 |
|
|
to request a new access token.
|
144 |
|
|
|
145 |
|
|
In order to remember the part of the client application that
|
146 |
|
|
initiated the authorization request, a session variable is used:
|
147 |
|
|
$_SESSION['oauth2_client']['redirect'][$state]. Then, drupal_goto()
|
148 |
|
|
is used to jump again to that path of the application.
|
149 |
|
|
|
150 |
|
|
|
151 |
|
|
* Integrating with other oauth2 clients
|
152 |
|
|
|
153 |
|
|
Other oauth2 clients for Drupal can integrate with oauth2_client.
|
154 |
|
|
This means that they can use the same client that is registered on
|
155 |
|
|
the oauth2_server for the oauth2_client.
|
156 |
|
|
|
157 |
|
|
The oauth2_server sends the authorization reply to the redirect_uri
|
158 |
|
|
that is registered for the client. If this client has been
|
159 |
|
|
registered for being used by the module oauth2_client, then its
|
160 |
|
|
redirect_uri is like this:
|
161 |
|
|
https://server.example.org/oauth2/authorized . A reply sent to this
|
162 |
|
|
redirect_uri will be routed to the callback function supplied by
|
163 |
|
|
oauth2_client. So, in general, the other oauth2 clients cannot use
|
164 |
|
|
the same client_id and client_secret that are registered in the
|
165 |
|
|
server. They will have to register their own client_id,
|
166 |
|
|
client_secret and redirect_uri.
|
167 |
|
|
|
168 |
|
|
However this is not very convenient. That's why oauth2_client allows
|
169 |
|
|
the other oauth2 clients to use the same client_id and
|
170 |
|
|
client_secret, but the reply has to pass through oauth2_client,
|
171 |
|
|
since redirect_uri sends it there.
|
172 |
|
|
|
173 |
|
|
It works like this: Suppose that another oauth2 client starts the
|
174 |
|
|
authentication workflow. On the parameters of the request it sets
|
175 |
|
|
redirect_uri to the one belonging to oauth2_client (since this is
|
176 |
|
|
the one that is reckognized and accepted by the server). However at
|
177 |
|
|
the same time it notifies oauth2_client that the reply of this
|
178 |
|
|
request should be forwarded to it. It does it by calling the
|
179 |
|
|
function: oauth2_client_set_redirect($state, $redirect).
|
180 |
|
|
|
181 |
|
|
The parameter $state is the random parameter that is used on the
|
182 |
|
|
authentication url in order to mittigate CSRF attacks. In this case
|
183 |
|
|
it is used as a key for identifying the authentication request. The
|
184 |
|
|
parameter $redirect is an associative array that contains the keys:
|
185 |
|
|
- uri: the uri of the oauth2 client that is requesting a
|
186 |
|
|
redirect
|
187 |
|
|
- params: associative array of other parameters that should be
|
188 |
|
|
appended to the uri, along with the $_REQUEST comming from the
|
189 |
|
|
server
|
190 |
|
|
|
191 |
|
|
Once another oauth2 client that has been successfully authenticated
|
192 |
|
|
and has received an access_token, it can share it with the
|
193 |
|
|
oauth2_client, so that oauth2_client does not have to repeat the
|
194 |
|
|
authentication process again. It can be done by calling the
|
195 |
|
|
function: oauth2_client_set_token($client_id, $token). |