|
| 1 | +from urllib.parse import urlencode, parse_qs |
| 2 | + |
| 3 | +import flask |
| 4 | +from flask import Blueprint, redirect |
| 5 | +from flask import current_app |
| 6 | +from flask import jsonify |
| 7 | +from flask.helpers import make_response |
| 8 | +from flask.templating import render_template |
| 9 | +from oic.oic.message import TokenErrorResponse, UserInfoErrorResponse, EndSessionRequest |
| 10 | + |
| 11 | +from pyop.access_token import AccessToken, BearerTokenError |
| 12 | +from pyop.exceptions import InvalidAuthenticationRequest, InvalidAccessToken, InvalidClientAuthentication, OAuthError, \ |
| 13 | + InvalidSubjectIdentifier, InvalidClientRegistrationRequest |
| 14 | +from pyop.util import should_fragment_encode |
| 15 | + |
| 16 | +oidc_provider_views = Blueprint('oidc_provider', __name__, url_prefix='') |
| 17 | + |
| 18 | + |
| 19 | +@oidc_provider_views.route('/') |
| 20 | +def index(): |
| 21 | + return 'Hello world!' |
| 22 | + |
| 23 | + |
| 24 | +@oidc_provider_views.route('/registration', methods=['POST']) |
| 25 | +def registration_endpoint(): |
| 26 | + try: |
| 27 | + response = current_app.provider.handle_client_registration_request(flask.request.get_data().decode('utf-8')) |
| 28 | + return make_response(jsonify(response.to_dict()), 201) |
| 29 | + except InvalidClientRegistrationRequest as e: |
| 30 | + return make_response(jsonify(e.to_dict()), status=400) |
| 31 | + |
| 32 | + |
| 33 | +@oidc_provider_views.route('/authentication', methods=['GET']) |
| 34 | +def authentication_endpoint(): |
| 35 | + # parse authentication request |
| 36 | + try: |
| 37 | + auth_req = current_app.provider.parse_authentication_request(urlencode(flask.request.args), |
| 38 | + flask.request.headers) |
| 39 | + except InvalidAuthenticationRequest as e: |
| 40 | + current_app.logger.debug('received invalid authn request', exc_info=True) |
| 41 | + error_url = e.to_error_url() |
| 42 | + if error_url: |
| 43 | + return redirect(error_url, 303) |
| 44 | + else: |
| 45 | + # show error to user |
| 46 | + return make_response('Something went wrong: {}'.format(str(e)), 400) |
| 47 | + |
| 48 | + # automagic authentication |
| 49 | + authn_response = current_app.provider.authorize(auth_req, 'test_user') |
| 50 | + response_url = authn_response.request(auth_req['redirect_uri'], should_fragment_encode(auth_req)) |
| 51 | + return redirect(response_url, 303) |
| 52 | + |
| 53 | + |
| 54 | +@oidc_provider_views.route('/.well-known/openid-configuration') |
| 55 | +def provider_configuration(): |
| 56 | + return jsonify(current_app.provider.provider_configuration.to_dict()) |
| 57 | + |
| 58 | + |
| 59 | +@oidc_provider_views.route('/jwks') |
| 60 | +def jwks_uri(): |
| 61 | + return jsonify(current_app.provider.jwks) |
| 62 | + |
| 63 | + |
| 64 | +@oidc_provider_views.route('/token', methods=['POST']) |
| 65 | +def token_endpoint(): |
| 66 | + try: |
| 67 | + token_response = current_app.provider.handle_token_request(flask.request.get_data().decode('utf-8'), |
| 68 | + flask.request.headers) |
| 69 | + return jsonify(token_response.to_dict()) |
| 70 | + except InvalidClientAuthentication as e: |
| 71 | + current_app.logger.debug('invalid client authentication at token endpoint', exc_info=True) |
| 72 | + error_resp = TokenErrorResponse(error='invalid_client', error_description=str(e)) |
| 73 | + response = make_response(error_resp.to_json(), 401) |
| 74 | + response.headers['Content-Type'] = 'application/json' |
| 75 | + response.headers['WWW-Authenticate'] = 'Basic' |
| 76 | + return response |
| 77 | + except OAuthError as e: |
| 78 | + current_app.logger.debug('invalid request: %s', str(e), exc_info=True) |
| 79 | + error_resp = TokenErrorResponse(error=e.oauth_error, error_description=str(e)) |
| 80 | + response = make_response(error_resp.to_json(), 400) |
| 81 | + response.headers['Content-Type'] = 'application/json' |
| 82 | + return response |
| 83 | + |
| 84 | + |
| 85 | +@oidc_provider_views.route('/userinfo', methods=['GET', 'POST']) |
| 86 | +def userinfo_endpoint(): |
| 87 | + try: |
| 88 | + response = current_app.provider.handle_userinfo_request(flask.request.get_data().decode('utf-8'), |
| 89 | + flask.request.headers) |
| 90 | + return jsonify(response.to_dict()) |
| 91 | + except (BearerTokenError, InvalidAccessToken) as e: |
| 92 | + error_resp = UserInfoErrorResponse(error='invalid_token', error_description=str(e)) |
| 93 | + response = make_response(error_resp.to_json(), 401) |
| 94 | + response.headers['WWW-Authenticate'] = AccessToken.BEARER_TOKEN_TYPE |
| 95 | + response.headers['Content-Type'] = 'application/json' |
| 96 | + return response |
| 97 | + |
| 98 | + |
| 99 | +def do_logout(end_session_request): |
| 100 | + try: |
| 101 | + current_app.provider.logout_user(end_session_request=end_session_request) |
| 102 | + except InvalidSubjectIdentifier as e: |
| 103 | + return make_response('Logout unsuccessful!', 400) |
| 104 | + |
| 105 | + redirect_url = current_app.provider.do_post_logout_redirect(end_session_request) |
| 106 | + if redirect_url: |
| 107 | + return redirect(redirect_url, 303) |
| 108 | + |
| 109 | + return make_response('Logout successful!') |
| 110 | + |
| 111 | + |
| 112 | +@oidc_provider_views.route('/logout', methods=['GET', 'POST']) |
| 113 | +def end_session_endpoint(): |
| 114 | + if flask.request.method == 'GET': |
| 115 | + # redirect from RP |
| 116 | + end_session_request = EndSessionRequest().deserialize(urlencode(flask.request.args)) |
| 117 | + flask.session['end_session_request'] = end_session_request.to_dict() |
| 118 | + return render_template('logout.jinja2') |
| 119 | + else: |
| 120 | + form = parse_qs(flask.request.get_data().decode('utf-8')) |
| 121 | + if 'logout' in form: |
| 122 | + return do_logout(EndSessionRequest().from_dict(flask.session['end_session_request'])) |
| 123 | + else: |
| 124 | + return make_response('You chose not to logout') |
0 commit comments