|
@@ -0,0 +1,328 @@
|
|
1
|
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
|
2
|
+<html>
|
|
3
|
+ <head>
|
|
4
|
+ <title>Poidsy - A PHP OpenID Consumer</title>
|
|
5
|
+ <link rel="stylesheet" href="style.css" type="text/css">
|
|
6
|
+ <link href="prettify.css" type="text/css" rel="stylesheet">
|
|
7
|
+ <script type="text/javascript" src="prettify.js"></script>
|
|
8
|
+ </head>
|
|
9
|
+ <body onLoad="prettyPrint();">
|
|
10
|
+ <h1><img src="openid.png" alt="OpenID logo"> Poidsy <small>A PHP OpenID Consumer</small></h1>
|
|
11
|
+ <ul id="menu">
|
|
12
|
+ <li><a href="index.html">Home</a></li>
|
|
13
|
+ <li><a href="instructions.html">Instructions</a></li>
|
|
14
|
+ <li><a href="changelog.html">Changelog</a></li>
|
|
15
|
+ </ul>
|
|
16
|
+ <div class="left">
|
|
17
|
+ <h2 class="first">Background Information</h2>
|
|
18
|
+ <p>
|
|
19
|
+ Poidsy is designed to be both easy to use and easy to integrate with
|
|
20
|
+ existing systems. This document explains the developer's interface to
|
|
21
|
+ Poidsy, and works through modifying a basic authentication script
|
|
22
|
+ to accept OpenID logins with Poidsy.
|
|
23
|
+ </p>
|
|
24
|
+ <h3>Requirements</h3>
|
|
25
|
+ <p>
|
|
26
|
+ Before you begin using Poidsy, you need to check that your environment
|
|
27
|
+ is supported. The easiest way to do this is to download a release of
|
|
28
|
+ Poidsy, and view the included test.php file in your web browser. This will
|
|
29
|
+ tell you about any problems it has encountered, and what will/will not work
|
|
30
|
+ as a result.
|
|
31
|
+ </p>
|
|
32
|
+ <h3>How it works</h3>
|
|
33
|
+ <p>
|
|
34
|
+ The bulk of the work done by Poidsy is done by the <code>processor.php</code>
|
|
35
|
+ script. This is the file that you will be including into your login page.
|
|
36
|
+ </p>
|
|
37
|
+ <p>
|
|
38
|
+ The processor is designed to be invoked in response to a form submission.
|
|
39
|
+ At present (version 0.1), it requires that <code>$_POST['openid_url']</code> is set to
|
|
40
|
+ the user-supplied identifier.
|
|
41
|
+ </p>
|
|
42
|
+ <p>
|
|
43
|
+ Once the processor has finished authenticating (or rejecting) the user's
|
|
44
|
+ identifier, it will supply the details in a session variable. Thus, to
|
|
45
|
+ actually make use of Poidsy's results, you will need to call
|
|
46
|
+ <code>session_start()</code> on your login page (if you're not already).
|
|
47
|
+ </p>
|
|
48
|
+ <p>
|
|
49
|
+ While Poidsy is authenticating an identifier, the user may be redirected
|
|
50
|
+ between the login page and their identity provider a number of times.
|
|
51
|
+ Whenever the user is redirected to the login page during this process,
|
|
52
|
+ the <code>openid.mode</code> parameter will be present.
|
|
53
|
+ </p>
|
|
54
|
+ <h2>Adding OpenID support to an existing login form</h2>
|
|
55
|
+ <p>
|
|
56
|
+ This example will walk through adding Poidsy to an existing login form.
|
|
57
|
+ The system we'll be modifying currently deals with usernames and passwords.
|
|
58
|
+ It is assumed that the Poidsy files are extracted to a <code>poidsy</code>
|
|
59
|
+ directory in the same directory as the login file.
|
|
60
|
+ </p>
|
|
61
|
+ <p>
|
|
62
|
+ The example code that we'll be modifying is included below. For the sake
|
|
63
|
+ of brevity, the specific logic of finding users, checking passwords, and
|
|
64
|
+ actually logging them in is abstracted away.
|
|
65
|
+ </p>
|
|
66
|
+<code class="prettyprint"> <?PHP
|
|
67
|
+
|
|
68
|
+ if (isset($_POST['user']) && isset($_POST['pass'])) {
|
|
69
|
+ if (passwordMatches($_POST['user'], hashPassword($_POST['pass']))) {
|
|
70
|
+ setUser($_POST['user']);
|
|
71
|
+ redirectAndExit();
|
|
72
|
+ } else {
|
|
73
|
+ define('ERROR', 'Invalid username/password combination');
|
|
74
|
+ }
|
|
75
|
+ }
|
|
76
|
+
|
|
77
|
+ if (defined('ERROR')) {
|
|
78
|
+ echo '<div class="error">ERROR: ', htmlentities(ERROR), '</div>';
|
|
79
|
+ }
|
|
80
|
+
|
|
81
|
+?>
|
|
82
|
+<p>Enter your details below to login:</p>
|
|
83
|
+<form action="login.php" method="post">
|
|
84
|
+ <label>Username: <input type="text" name="user"></label>
|
|
85
|
+ <label>Password: <input type="password" name="pass"></label>
|
|
86
|
+ <input type="submit" value="Login">
|
|
87
|
+</form>
|
|
88
|
+</code>
|
|
89
|
+ <h3>Step 1: Adding a new form</h3>
|
|
90
|
+ <p>
|
|
91
|
+ The first step is to provide users with a second form to enter their
|
|
92
|
+ OpenID identifier in. It's good practice to include a little OpenID
|
|
93
|
+ logo in the input field, so users can instantly see that it's for an
|
|
94
|
+ OpenID identifier. Poidsy handily comes with such a logo.
|
|
95
|
+ </p>
|
|
96
|
+ <p>
|
|
97
|
+ The following code is added below the existing form:
|
|
98
|
+ </p>
|
|
99
|
+<code class="prettyprint"><p>Alternatively, login using an OpenID identifier:</p>
|
|
100
|
+<form action="login.php" method="post">
|
|
101
|
+ <input type="text" name="openid_url"
|
|
102
|
+ style="background-image: url('poidsy/openid.gif') no-repeat;
|
|
103
|
+ padding-left: 20px;">
|
|
104
|
+ <input type="submit" value="Login">
|
|
105
|
+</form>
|
|
106
|
+</code>
|
|
107
|
+ <h3>Step 2: Starting a session</h3>
|
|
108
|
+ <p>
|
|
109
|
+ As described above, Poidsy uses sessions to track data and return its
|
|
110
|
+ results. As our login form currently doesn't use sessions, we need to
|
|
111
|
+ add a call to <code>session_start();</code> before we'll be using
|
|
112
|
+ Poidsy or its results.
|
|
113
|
+ </p>
|
|
114
|
+ <p>
|
|
115
|
+ The session start code therefore needs to be added before the main if statement.
|
|
116
|
+ </p>
|
|
117
|
+<code class="prettyprint"><span style="opacity: 0.5;"><?PHP</span>
|
|
118
|
+
|
|
119
|
+ session_start();
|
|
120
|
+
|
|
121
|
+<span style="opacity: 0.5;"> if (isset($_POST['user']) && isset($_POST['pass'])) {</span>
|
|
122
|
+</code>
|
|
123
|
+ <h3>Step 3: Including the processor</h3>
|
|
124
|
+ <p>
|
|
125
|
+ The next step is to include Poidsy's processor when there's processing to be
|
|
126
|
+ done. This is either when the user has just submitted the OpenID form, or
|
|
127
|
+ when they've been redirected back from their provider during the
|
|
128
|
+ authentication process.
|
|
129
|
+ </p>
|
|
130
|
+ <p>
|
|
131
|
+ We prepend the following conditions to the start of the if statement to
|
|
132
|
+ include the processor when neccessary. Note that PHP translates the
|
|
133
|
+ openid.mode argument to openid_mode.
|
|
134
|
+ </p>
|
|
135
|
+<code class="prettyprint"><span style="opacity: 0.5;"> session_start(); </span>
|
|
136
|
+
|
|
137
|
+ if (isset($_POST['openid_url']) || isset($_REQUEST['openid_mode'])) {
|
|
138
|
+
|
|
139
|
+ // OpenID login attempt
|
|
140
|
+ require('poidsy/processor.php');
|
|
141
|
+
|
|
142
|
+ } else <span style="opacity: 0.5;">if (isset($_POST['user']) && isset($_POST['pass'])) {</span>
|
|
143
|
+</code>
|
|
144
|
+ <h3>Step 4: Handling errors</h3>
|
|
145
|
+ <p>
|
|
146
|
+ Just like normal login attempts, OpenID authentications can fail for a
|
|
147
|
+ variety of reasons (such as the identifier not being a valid OpenID
|
|
148
|
+ endpoint, or the provide refusing to authenticate the client), so we
|
|
149
|
+ need to handle errors and display them to the user.
|
|
150
|
+ </p>
|
|
151
|
+ <p>
|
|
152
|
+ If an error is encountered, Poidsy will have set the
|
|
153
|
+ <code>$_SESSION['openid']['error']</code> variable with a description
|
|
154
|
+ of the problem. We simply check for this and pass it on to the user:
|
|
155
|
+ </p>
|
|
156
|
+<code class="prettyprint"><span style="opacity:0.5;">require('poidsy/processor.php');</span>
|
|
157
|
+
|
|
158
|
+ } else if (isset($_SESSION['openid']['error'])) {
|
|
159
|
+
|
|
160
|
+ // Failed OpenID login attempt
|
|
161
|
+ define('ERROR', $_SESSION['openid']['error']);
|
|
162
|
+ unset($_SESSION['openid']['error']);
|
|
163
|
+
|
|
164
|
+ <span style="opacity: 0.5;">} else if (isset($_POST['user']) && isset($_POST['pass'])) {</span>
|
|
165
|
+</code>
|
|
166
|
+ <h3>Step 5: Handling success</h3>
|
|
167
|
+ <p>
|
|
168
|
+ The final thing to do is to handle the case when a user has successfully
|
|
169
|
+ authenticated themselves using an OpenID identifier. In a typical
|
|
170
|
+ environment this will be a two-step process: creating a new account for
|
|
171
|
+ the user if they haven't previously logged in, and then logging the user
|
|
172
|
+ in to their account.
|
|
173
|
+ </p>
|
|
174
|
+ <p>
|
|
175
|
+ The following code adds another branch to our if statement:
|
|
176
|
+ </p>
|
|
177
|
+<code class="prettyprint"><span style="opacity:0.5;">unset($_SESSION['openid']['error']);</span>
|
|
178
|
+
|
|
179
|
+ } else if (isset($_SESSION['openid']['validated']) && $_SESSION['openid']['validated']) {
|
|
180
|
+
|
|
181
|
+ // OpenID authentication successful
|
|
182
|
+
|
|
183
|
+ if (!isAccount($_SESSION['openid']['identity'])) {
|
|
184
|
+
|
|
185
|
+ // Create an account if they need one, using a fake
|
|
186
|
+ // password hash so users can't login normally
|
|
187
|
+ createAccount($_SESSION['openid']['identity'], 'invalid'):
|
|
188
|
+
|
|
189
|
+ }
|
|
190
|
+
|
|
191
|
+ setUser($_SESSION['openid']['identity']);
|
|
192
|
+ unset($_SESSION['openid']['validated']);
|
|
193
|
+ redirectAndExit();
|
|
194
|
+
|
|
195
|
+ <span style="opacity: 0.5;">} else if (isset($_POST['user']) && isset($_POST['pass'])) {</span>
|
|
196
|
+</code>
|
|
197
|
+ <h2>Summary</h2>
|
|
198
|
+ <p>
|
|
199
|
+ Hopefully this document has given you a sense of how to use Poidsy.
|
|
200
|
+ It is designed to work transparently with your existing applications,
|
|
201
|
+ and as demonstrated above can be integrated in five simple steps.
|
|
202
|
+ </p>
|
|
203
|
+ <p>
|
|
204
|
+ Note that, of course, how you implement Poidsy very much depends on
|
|
205
|
+ your existing system and your requirements. If you are going to switch
|
|
206
|
+ to using just OpenID, there is obviously no need to deal with
|
|
207
|
+ password hashes as was done in this example, and you could in fact
|
|
208
|
+ not use a backend database at all for your users — just rely on
|
|
209
|
+ Poidsy to populate the $_SESSION['openid'] array.
|
|
210
|
+ </p>
|
|
211
|
+ </div>
|
|
212
|
+ <div class="right">
|
|
213
|
+ <h2 class="first">Feedback</h2>
|
|
214
|
+ <p>
|
|
215
|
+ If you're using Poidsy (or are trying to but are having
|
|
216
|
+ problems) then I'd love to hear from you. My contact details can be found
|
|
217
|
+ on my <a href="http://chris.smith.name/" rel="me">personal website</a>.
|
|
218
|
+ </p>
|
|
219
|
+ <h2>Constant reference</h2>
|
|
220
|
+ <p>
|
|
221
|
+ You can define a set of constants to control the behaviour of
|
|
222
|
+ Poidsy. These should be defined before the processor is included.
|
|
223
|
+ </p>
|
|
224
|
+ <p class="asof">From Poidsy 0.1:</p>
|
|
225
|
+ <dl>
|
|
226
|
+ <dt>OPENID_URL</dt>
|
|
227
|
+ <dd>
|
|
228
|
+ The user-supplied OpenID URL to validate. This should only be specified
|
|
229
|
+ for the initial request (not any subsequent redirects where openid.mode is
|
|
230
|
+ set). If not provided, Poidsy will use the value of $_POST['openid_url'] if it exists
|
|
231
|
+ </dd>
|
|
232
|
+ </dl>
|
|
233
|
+ <p class="asof">From Poidsy 0.2:</p>
|
|
234
|
+ <dl>
|
|
235
|
+ <dt>OPENID_THROTTLE_NUM</dt>
|
|
236
|
+ <dd>
|
|
237
|
+ The maximum number of requests to allow before throttling a user.
|
|
238
|
+ Default value is 3.
|
|
239
|
+ </dd>
|
|
240
|
+ <dt>OPENID_THROTTLE_GAP</dt>
|
|
241
|
+ <dd>
|
|
242
|
+ The number of seconds that have to ellapse between requests before the
|
|
243
|
+ user's counter is reset. Default value is 30.
|
|
244
|
+ </dd>
|
|
245
|
+ </dl>
|
|
246
|
+ <p class="asof">From Poidsy 0.3:</p>
|
|
247
|
+ <dl>
|
|
248
|
+ <dt>OPENID_SREG_REQUEST</dt>
|
|
249
|
+ <dd>
|
|
250
|
+ A comma-separated list of fields to request from the user via the simple
|
|
251
|
+ registration extension. Use of this constant implies that the user will
|
|
252
|
+ be required to manually input the data if their provider doesn't supply it.
|
|
253
|
+ </dd>
|
|
254
|
+ <dt>OPENID_SREG_OPTIONAL</dt>
|
|
255
|
+ <dd>
|
|
256
|
+ A comma-separated list of fields that the user's provider may provide, but
|
|
257
|
+ aren't required by the application.
|
|
258
|
+ </dd>
|
|
259
|
+ <dt>OPENID_SREG_POLICY</dt>
|
|
260
|
+ <dd>
|
|
261
|
+ An URL for a document that describes how the data requested using SREG is
|
|
262
|
+ going to be used.
|
|
263
|
+ </dd>
|
|
264
|
+ <dt>OPENID_NOKEYMANAGER</dt>
|
|
265
|
+ <dd>
|
|
266
|
+ Stops Poidsy using its key manager, forcing it to use dumb mode instead.
|
|
267
|
+ The value of the constant is irrelevant, only whether it is defined or not.
|
|
268
|
+ </dd>
|
|
269
|
+ <dt>OPENID_IMMEDIATE</dt>
|
|
270
|
+ <dd>
|
|
271
|
+ Forces Poidsy to try an openid_immediate request first. If not defined,
|
|
272
|
+ Poidsy will send an openid_setup request, which allows the identity provider
|
|
273
|
+ to interact with the user.</dd><dd>As of 0.4, if this is set to <code>true</code>,
|
|
274
|
+ Poidsy will error if the provider requires interaction, otherwise Poidsy
|
|
275
|
+ will send a setup request. Previous behaviour always errored.
|
|
276
|
+ </dd>
|
|
277
|
+ <dt>OPENID_TRUSTROOT</dt>
|
|
278
|
+ <dd>
|
|
279
|
+ The trust root to send to providers. Used to tell users what site they're
|
|
280
|
+ logging into. Should be a fully qualified URL that encompasses the URL of
|
|
281
|
+ the login script. Defaults to the current URL. </dd><dd>As of 0.5, this may
|
|
282
|
+ be truncated in order to ensure it is above the return_to URL.
|
|
283
|
+ </dd>
|
|
284
|
+ <dt>OPENID_ALLOWUSER</dt>
|
|
285
|
+ <dd>
|
|
286
|
+ If defined, allows usernames (and passwords) in identity URLs. These are
|
|
287
|
+ stripped by default to prevent users using the same identity with multiple
|
|
288
|
+ (bogus, unused) usernames.
|
|
289
|
+ </dd>
|
|
290
|
+ <dt>OPENID_ALLOWQUERY</dt>
|
|
291
|
+ <dd>
|
|
292
|
+ If defined, allows queries (the part of the URL after the "?" character) in
|
|
293
|
+ identity URLs. These are stripped by default to prevent users from using
|
|
294
|
+ the same identity with multiple (unused) query strings.
|
|
295
|
+ </dd>
|
|
296
|
+ </dl>
|
|
297
|
+ <h2>Error codes</h2>
|
|
298
|
+ <p>
|
|
299
|
+ As of Poidsy 0.3, each error has an associated errorcode that defines
|
|
300
|
+ the type of error. These error codes are:
|
|
301
|
+ </p>
|
|
302
|
+ <dl>
|
|
303
|
+ <dt style="text-decoration: line-through;">autherror</dt>
|
|
304
|
+ <dd>Error when trying to POST data to authenticate a request in dumb mode, probably because the provider went down. Removed as of Poidsy 0.4.</dd>
|
|
305
|
+ <dt>cancelled</dt>
|
|
306
|
+ <dd>The user or their identity provider cancelled the request for some reason</dd>
|
|
307
|
+ <dt>diffid</dt>
|
|
308
|
+ <dd>The provider validated a different identity to the one Poidsy requested. Indicates that the provider is probably broken.</dd>
|
|
309
|
+ <dt>diffreturnto</dt>
|
|
310
|
+ <dd>The provider gave a different return_to URL to the one the user arrived at</dd>
|
|
311
|
+ <dt>discovery</dt>
|
|
312
|
+ <dd>There was an error during discovery (site down, unsupported protocol, etc)</dd>
|
|
313
|
+ <dt>noauth</dt>
|
|
314
|
+ <dd>The signature of the provider's response didn't match the response. This could mean that something is broken, or that someone is trying one of a variety of attacks.</dd>
|
|
315
|
+ <dt>noimmediate</dt>
|
|
316
|
+ <dd>OPENID_IMMEDIATE was defined but the request couldn't be completed immediately</dd>
|
|
317
|
+ <dt>nonce</dt>
|
|
318
|
+ <dd>The nonce used by Poidsy didn't match what it was expecting. This normally means the user tried to go to an expired page, or someone was trying to perform a replay attack.</dd>
|
|
319
|
+ <dt>notvalid</dt>
|
|
320
|
+ <dd>The identity isn't valid (no openid.server link found)</dd>
|
|
321
|
+ <dt>perror</dt>
|
|
322
|
+ <dd>The provider returned an error at some stage</dd>
|
|
323
|
+ <dt>throttled</dt>
|
|
324
|
+ <dd>The user is being throttled by Poidsy. See the description of the OPENID_THROTTLE constants above.</dd>
|
|
325
|
+ </dl>
|
|
326
|
+ </div>
|
|
327
|
+ </body>
|
|
328
|
+</html>
|