-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexample_basic.py
166 lines (132 loc) · 5.64 KB
/
example_basic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Flask + Requests-OAuthlib example.
This example demonstrates how to integrate a server application with
Authentiq Connect, using standard OAuth 2.0. It uses the popular
requests-oauthlib module to make this trivial in Flask.
As with all plain OAuth 2.0 integrations, we use the UserInfo endpoint to
retrieve the user profile after authorization. Check out our native
AuthentiqJS snippet or an OpenID Connect library to optimise this.
"""
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals
)
import os
import oauthlib.oauth2.rfc6749.errors as oauth2_errors
import requests
from flask import Flask, abort, jsonify, redirect, request, session, url_for
from requests_oauthlib import OAuth2Session
class Config(object):
"""
Flask configuration container.
"""
DEBUG = True
TESTING = False
SECRET_KEY = "aicahquohzieRah5ZooLoo3a"
AUTHENTIQ_BASE = "https://connect.authentiq.io/"
AUTHORIZE_URL = AUTHENTIQ_BASE + "authorize"
TOKEN_URL = AUTHENTIQ_BASE + "token"
USERINFO_URL = AUTHENTIQ_BASE + "userinfo"
# The following app is registered at Authentiq Connect.
CLIENT_ID = os.environ.get("CLIENT_ID", "examples-flask-basic")
CLIENT_SECRET = os.environ.get("CLIENT_SECRET", "ed25519")
# Personal details requested from the user. See the "scopes_supported" key in
# the following JSON document for an up to date list of supported scopes:
#
# https://connect.authentiq.io/.well-known/openid-configuration
#
REQUESTED_SCOPES = ["openid", "aq:name", "email", "aq:push"]
PORT = 8000
REDIRECT_URL = "http://localhost:%d/authorized" % PORT
app = Flask(__name__)
app.config.from_object(Config)
@app.route("/")
def index():
# Check if redirect_uri matches with the one registered with the
# example client.
assert url_for("authorized", _external=True) == REDIRECT_URL, (
"For this demo to work correctly, please make sure it is hosted "
"on localhost, so that the redirect URL is exactly " +
REDIRECT_URL + "."
)
# Initialise an authentication session. Here we pass in scope and
# redirect_uri explicitly, though when omitted defaults will be taken
# from the registered client.
authentiq = OAuth2Session(
client_id=CLIENT_ID,
scope=REQUESTED_SCOPES,
redirect_uri=url_for("authorized", _external=True),
)
# Build the authorization URL and retrieve some client state.
authorization_url, state = authentiq.authorization_url(AUTHORIZE_URL)
# Save state to match it in the response.
session["state"] = state
# Redirect to the Authentiq Connect authentication endpoint.
return redirect(authorization_url)
@app.route("/authorized")
def authorized():
"""
OAuth 2.0 redirection point.
"""
# Pass in our client side crypto state; requests-oauthlib will
# take care of matching it in the OAuth2 response.
authentiq = OAuth2Session(CLIENT_ID, state=session.get("state"))
try:
error = request.args["error"]
oauth2_errors.raise_from_error(error, request.args)
except KeyError:
pass
except oauth2_errors.OAuth2Error as e:
code = e.status_code or 400
description = "Provider returned: " + (e.description or e.error)
abort(code, description=description)
try:
# Use our client_secret to exchange the authorization code for a
# token. Requests-oauthlib parses the redirected URL for us.
# The token will contain the access_token, a refresh_token, and the
# scope the end-user consented to.
token = authentiq.fetch_token(TOKEN_URL,
client_secret=CLIENT_SECRET,
authorization_response=request.url)
app.logger.info("Received token: %s" % token)
# The incoming request looks flaky, let's not handle it further.
except oauth2_errors.OAuth2Error as e:
description = "Request to token endpoint failed: " + \
(e.description or e.error)
abort(code=e.status_code or 400, description=description)
# The HTTP request to the token endpoint failed.
except requests.exceptions.HTTPError as e:
code = e.response.status_code or 502
description = "Request to token endpoint failed: " + e.response.reason
abort(code, description=description)
# Now we can use the access_token to retrieve an OpenID Connect
# compatible UserInfo structure from the provider. Once again,
# requests-oauthlib adds a valid Authorization header for us.
#
# Note that this request can be optimized out if using an OIDC or
# native Authentiq Connect client.
try:
userinfo = authentiq.get(USERINFO_URL).json()
# The HTTP request to the UserInfo endpoint failed.
except requests.exceptions.HTTPError as e:
abort(code=e.response.status_code or 502,
description="Request to userinfo endpoint failed: " +
e.response.reason)
except ValueError as e:
abort(code=502,
description="Could not decode userinfo response: " + e.message)
# Here you would save the identity information in database or session
# and sign the user in. For now just display the USerInfo structure.
# Use userinfo["sub"] as the user's UUID within a single sign-on sector.
return jsonify(userinfo)
if __name__ == "__main__":
if app.debug:
import os
# Allow insecure oauth2 when debugging
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
# Explicitly set `host=localhost` in order to get the correct redirect_uri.
app.run(host="localhost", port=PORT)