@@ -153,15 +153,23 @@ async def test_indieauth_flow(
153
153
data = auth_response_body .encode ("utf-8" ),
154
154
status_code = auth_response_status ,
155
155
)
156
+ if not expected_error :
157
+ httpx_mock .add_response (
158
+ url = "https://indieauth.simonwillison.net/index.php/author/simonw/" ,
159
+ method = "GET" ,
160
+ data = b'<link rel="authorization_endpoint" href="https://indieauth.simonwillison.net/auth">' ,
161
+ )
162
+ if "indieauth.simonwillison.com" in auth_response_body :
163
+ httpx_mock .add_response (
164
+ url = "https://indieauth.simonwillison.com" ,
165
+ method = "GET" ,
166
+ data = b'<link rel="authorization_endpoint" href="https://indieauth.simonwillison.net/auth">' ,
167
+ )
156
168
datasette = Datasette ([], memory = True )
157
169
app = datasette .app ()
158
170
async with httpx .AsyncClient (app = app ) as client :
159
171
# Get CSRF token
160
- csrftoken = (
161
- await client .get (
162
- "http://localhost/-/indieauth" ,
163
- )
164
- ).cookies ["ds_csrftoken" ]
172
+ csrftoken = await _get_csrftoken (client )
165
173
# Submit the form
166
174
post_response = await client .post (
167
175
"http://localhost/-/indieauth" ,
@@ -196,8 +204,12 @@ async def test_indieauth_flow(
196
204
allow_redirects = False ,
197
205
)
198
206
# This should have made a POST to https://indieauth.simonwillison.net/auth
199
- last_request = httpx_mock .get_requests ()[- 1 ]
200
- post_bits = dict (urllib .parse .parse_qsl (last_request .read ().decode ("utf-8" )))
207
+ last_post_request = [
208
+ r for r in httpx_mock .get_requests () if r .method == "POST"
209
+ ][- 1 ]
210
+ post_bits = dict (
211
+ urllib .parse .parse_qsl (last_post_request .read ().decode ("utf-8" ))
212
+ )
201
213
assert post_bits == {
202
214
"grant_type" : "authorization_code" ,
203
215
"code" : "123" ,
@@ -262,11 +274,7 @@ async def test_indieauth_errors(httpx_mock, me, bodies, expected_error):
262
274
datasette = Datasette ([], memory = True )
263
275
app = datasette .app ()
264
276
async with httpx .AsyncClient (app = app ) as client :
265
- csrftoken = (
266
- await client .get (
267
- "http://localhost/-/indieauth" ,
268
- )
269
- ).cookies ["ds_csrftoken" ]
277
+ csrftoken = await _get_csrftoken (client )
270
278
# Submit the form
271
279
post_response = await client .post (
272
280
"http://localhost/-/indieauth" ,
@@ -314,11 +322,7 @@ def raise_timeout(request, ext):
314
322
datasette = Datasette ([], memory = True )
315
323
app = datasette .app ()
316
324
async with httpx .AsyncClient (app = app ) as client :
317
- csrftoken = (
318
- await client .get (
319
- "http://localhost/-/indieauth" ,
320
- )
321
- ).cookies ["ds_csrftoken" ]
325
+ csrftoken = await _get_csrftoken (client )
322
326
# Submit the form
323
327
post_response = await client .post (
324
328
"http://localhost/-/indieauth" ,
@@ -327,3 +331,59 @@ def raise_timeout(request, ext):
327
331
allow_redirects = False ,
328
332
)
329
333
assert "Invalid IndieAuth identifier: HTTP error occurred" in post_response .text
334
+
335
+
336
+ @pytest .mark .asyncio
337
+ async def test_non_matching_authorization_endpoint (httpx_mock ):
338
+ # See https://github.com/simonw/datasette-indieauth/issues/22
339
+ httpx_mock .add_response (
340
+ url = "https://simonwillison.net" ,
341
+ data = b'<link rel="authorization_endpoint" href="https://indieauth.simonwillison.net/auth">' ,
342
+ )
343
+ httpx_mock .add_response (
344
+ url = "https://indieauth.simonwillison.net/auth" ,
345
+ method = "POST" ,
346
+ data = "me=https%3A%2F%2Fsimonwillison.net%2Fme" .encode ("utf-8" ),
347
+ )
348
+ httpx_mock .add_response (
349
+ url = "https://simonwillison.net/me" ,
350
+ data = b'<link rel="authorization_endpoint" href="https://example.com">' ,
351
+ )
352
+ datasette = Datasette ([], memory = True )
353
+ app = datasette .app ()
354
+ async with httpx .AsyncClient (app = app ) as client :
355
+ csrftoken = await _get_csrftoken (client )
356
+ # Submit the form
357
+ post_response = await client .post (
358
+ "http://localhost/-/indieauth" ,
359
+ data = {"csrftoken" : csrftoken , "me" : "https://simonwillison.net/" },
360
+ cookies = {"ds_csrftoken" : csrftoken },
361
+ allow_redirects = False ,
362
+ )
363
+ ds_indieauth = post_response .cookies ["ds_indieauth" ]
364
+ state = dict (
365
+ urllib .parse .parse_qsl (post_response .headers ["location" ].split ("?" , 1 )[1 ])
366
+ )["state" ]
367
+ # ... after redirecting back again
368
+ response = await client .get (
369
+ "http://localhost/-/indieauth/done" ,
370
+ params = {
371
+ "state" : state ,
372
+ "code" : "123" ,
373
+ },
374
+ cookies = {"ds_indieauth" : ds_indieauth },
375
+ allow_redirects = False ,
376
+ )
377
+ # This should be an error because the authorization_endpoint did not match
378
+ assert (
379
+ ""me" value resolves to a different authorization_endpoint"
380
+ in response .text
381
+ )
382
+
383
+
384
+ async def _get_csrftoken (client ):
385
+ return (
386
+ await client .get (
387
+ "http://localhost/-/indieauth" ,
388
+ )
389
+ ).cookies ["ds_csrftoken" ]
0 commit comments