Do not burn auth codes by authenticating before requesting an access

token

This broke authorization for endpoints that only allow a code to be
used once (e.g. Known).

A side effect is that authorization no longer falls back to authentication
if the token endpoint does not exist or returns an error.
This commit is contained in:
Kyle Mahan 2016-01-27 07:24:55 -08:00
parent cc9c7dfd21
commit 9fdd45a0de
3 changed files with 37 additions and 14 deletions

View File

@ -1,6 +1,14 @@
# Change Log # Change Log
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## 0.2.5 - 2016-01-27
### Changed
- Bugfix: authorization_handler was burning the auth code by
delegating to authentication_handler. This broke authorization for
endpoints that only allow codes to be used once. A side effect of
this is that authorization no longer falls back to authentication
when there is no token_endpoint or the token_endpoint request fails.
## 0.2.4 - 2015-12-13 ## 0.2.4 - 2015-12-13
### Changed ### Changed
- Replace `next_url` parameter with more general `state` - Replace `next_url` parameter with more general `state`

View File

@ -224,28 +224,37 @@ class MicropubClient:
return AuthResponse(me=confirmed_me, state=state) return AuthResponse(me=confirmed_me, state=state)
def _handle_authorize_response(self): def _handle_authorize_response(self):
authenticate_response = self._handle_authenticate_response()
code = flask.request.args.get('code') code = flask.request.args.get('code')
wrapped_state = flask.request.args.get('state') wrapped_state = flask.request.args.get('state')
me = flask.request.args.get('me') me = flask.request.args.get('me')
redirect_uri = flask.url_for(flask.request.endpoint, _external=True) redirect_uri = flask.url_for(flask.request.endpoint, _external=True)
if authenticate_response.error: if wrapped_state and '|' in wrapped_state:
return authenticate_response csrf_token, state = wrapped_state.split('|', 1)
else:
csrf_token = state = None
if not csrf_token:
return AuthResponse(
state=state, error='no CSRF token in response')
if csrf_token != flask.session.get('_micropub_csrf_token'):
return AuthResponse(
state=state, error='mismatched CSRF token')
token_url, micropub_url = self._discover_endpoints(me)[1:] token_url, micropub_url = self._discover_endpoints(me)[1:]
if not token_url or not micropub_url: if not token_url or not micropub_url:
# successfully auth'ed user, no micropub endpoint # successfully auth'ed user, no micropub endpoint
return AuthResponse( return AuthResponse(
me=authenticate_response.me, me=me,
state=authenticate_response.state, state=state,
error='no micropub endpoint found.') error='no micropub endpoint found.')
# request an access token # request an access token
token_data = { token_data = {
'code': code, 'code': code,
'me': authenticate_response.me, 'me': me,
'redirect_uri': redirect_uri, 'redirect_uri': redirect_uri,
'client_id': self.client_id, 'client_id': self.client_id,
'state': wrapped_state, 'state': wrapped_state,
@ -260,26 +269,29 @@ class MicropubClient:
if token_response.status_code != 200: if token_response.status_code != 200:
return AuthResponse( return AuthResponse(
me=authenticate_response.me, me=me,
state=authenticate_response.state, state=state,
error='bad response from token endpoint: {}' error='bad response from token endpoint: {}'
.format(token_response)) .format(token_response))
tdata = parse_qs(token_response.text) tdata = parse_qs(token_response.text)
if 'access_token' not in tdata: if 'access_token' not in tdata:
return AuthResponse( return AuthResponse(
me=authenticate_response.me, me=me,
state=authenticate_response.state, state=state,
error='response from token endpoint missing access_token: {}' error='response from token endpoint missing access_token: {}'
.format(tdata)) .format(tdata))
# success! # success!
access_token = tdata.get('access_token')[0] access_token = tdata.get('access_token')[0]
confirmed_me = tdata.get('me')[0]
confirmed_scope = tdata.get('scope')[0]
return AuthResponse( return AuthResponse(
me=authenticate_response.me, me=confirmed_me,
micropub_endpoint=micropub_url, micropub_endpoint=micropub_url,
access_token=access_token, access_token=access_token,
state=authenticate_response.state) scope=confirmed_scope,
state=state)
def _discover_endpoints(self, me): def _discover_endpoints(self, me):
me_response = requests.get(me) me_response = requests.get(me)
@ -311,15 +323,18 @@ class AuthResponse:
micropub_endpoint (string): The endpoint to POST micropub requests to. micropub_endpoint (string): The endpoint to POST micropub requests to.
access_token (string): The authorized user's micropub access token. access_token (string): The authorized user's micropub access token.
state (string): The optional state that was passed to authorize. state (string): The optional state that was passed to authorize.
scope (string): The scope that comes with the micropub access token
error (string): describes the error encountered if any. It is possible error (string): describes the error encountered if any. It is possible
that the authentication step will succeed but the access token step that the authentication step will succeed but the access token step
will fail, in which case me will be non-None, and error will describe will fail, in which case me will be non-None, and error will describe
this condition. this condition.
""" """
def __init__(self, me=None, micropub_endpoint=None, def __init__(self, me=None, micropub_endpoint=None,
access_token=None, state=None, error=None): access_token=None, state=None, scope=None,
error=None):
self.me = me self.me = me
self.micropub_endpoint = micropub_endpoint self.micropub_endpoint = micropub_endpoint
self.access_token = access_token self.access_token = access_token
self.next_url = self.state = state self.next_url = self.state = state
self.scope = scope
self.error = error self.error = error

View File

@ -11,7 +11,7 @@ from setuptools import setup
setup( setup(
name='Flask-Micropub', name='Flask-Micropub',
version='0.2.4', version='0.2.5',
url='https://github.com/kylewm/flask-micropub/', url='https://github.com/kylewm/flask-micropub/',
license='BSD', license='BSD',
author='Kyle Mahan', author='Kyle Mahan',