1
|
|
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).
|