clean & improvements
This commit is contained in:
parent
ea8100a764
commit
8675240084
104
app.py
104
app.py
@ -1,44 +1,43 @@
|
|||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
import jwt
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from authlib.integrations.base_client import OAuthError
|
from authlib.integrations.base_client import OAuthError
|
||||||
from authlib.integrations.flask_client import OAuth
|
from authlib.integrations.flask_client import OAuth
|
||||||
|
|
||||||
from flask import Flask, request, redirect, Response, session, render_template, url_for, make_response
|
from flask import Flask, request, redirect, session, render_template, url_for, make_response
|
||||||
|
|
||||||
# TODO add client export json in project & git
|
# TODO add client export json in project & git
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
# TODO still useful ?
|
# key used to encore session cookie
|
||||||
app.config['SECRET_KEY'] = 'onelogindemopytoolkit'
|
app.config['SECRET_KEY'] = 'client-oidc'
|
||||||
|
|
||||||
issuer="https://id.vilanet.fr/realms/vilanet"
|
idp_url = "https://id.vilanet.fr/realms/vilanet"
|
||||||
|
|
||||||
|
client_url = "http://localhost:5001"
|
||||||
client_id = "client-oidc"
|
client_id = "client-oidc"
|
||||||
client_secret = "BqWWnuj5JkgZZWEaXuR8bprEx53lqGxC"
|
client_secret = "BqWWnuj5JkgZZWEaXuR8bprEx53lqGxC"
|
||||||
|
|
||||||
|
resource_server_url = 'http://localhost:5002'
|
||||||
|
|
||||||
# To manage OIDC flow for UI client
|
# To manage OIDC flow for UI client
|
||||||
oauth = OAuth(app=app)
|
oauth = OAuth(app=app)
|
||||||
oauth.register(
|
oauth.register(
|
||||||
name="keycloak",
|
name="keycloak",
|
||||||
client_id=client_id,
|
client_id=client_id,
|
||||||
client_secret=client_secret,
|
client_secret=client_secret,
|
||||||
server_metadata_url=f'{issuer}/.well-known/openid-configuration',
|
server_metadata_url=f'{idp_url}/.well-known/openid-configuration',
|
||||||
client_kwargs={
|
client_kwargs={'scope': 'openid', 'code_challenge_method': 'S256'}
|
||||||
'scope': 'openid',
|
|
||||||
'code_challenge_method': 'S256',
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api")
|
@app.route("/api")
|
||||||
def api():
|
def api():
|
||||||
response = ''
|
return make_response(redirect(resource_server_url + "/api?" + urllib.parse.urlencode({'callbackUrl': client_url})))
|
||||||
if 'access_token' in request.cookies:
|
|
||||||
access_token = request.cookies['access_token']
|
|
||||||
response = requests.get('http://localhost:5002/api?callbackUrl=http%3A%2F%2Flocalhost%3A5001', headers={'Authorization': f'Bearer {access_token}'})
|
|
||||||
else:
|
|
||||||
response = requests.get('http://localhost:5002/api?callbackUrl=http%3A%2F%2Flocalhost%3A5001')
|
|
||||||
return Response(response=response.text, status=response.status_code, headers=dict(response.headers))
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/auth")
|
@app.route("/auth")
|
||||||
@ -46,30 +45,31 @@ def auth():
|
|||||||
try:
|
try:
|
||||||
token_response = oauth.keycloak.authorize_access_token()
|
token_response = oauth.keycloak.authorize_access_token()
|
||||||
except OAuthError as e:
|
except OAuthError as e:
|
||||||
return redirect('/?error=access_denied')
|
return redirect('/?' + urllib.parse.urlencode({'error': e}))
|
||||||
response = make_response(redirect('/'))
|
response = make_response(redirect('/'))
|
||||||
access_token = token_response['access_token']
|
access_token = token_response['access_token']
|
||||||
if access_token:
|
if access_token:
|
||||||
response.set_cookie('access_token', access_token, httponly=True)
|
response.set_cookie('access_token', access_token, httponly=True, domain='localhost')
|
||||||
refresh_token = token_response['refresh_token']
|
refresh_token = token_response['refresh_token']
|
||||||
if refresh_token:
|
if refresh_token:
|
||||||
response.set_cookie('refresh_token', refresh_token, httponly=True)
|
response.set_cookie('refresh_token', refresh_token, httponly=True, domain='localhost')
|
||||||
id_token = token_response['id_token']
|
id_token = token_response['id_token']
|
||||||
if id_token:
|
if id_token:
|
||||||
response.set_cookie('id_token', id_token, httponly=True)
|
response.set_cookie('id_token', id_token, httponly=True, domain='localhost')
|
||||||
if token_response['userinfo']:
|
if token_response['userinfo']:
|
||||||
session['name'] = token_response['userinfo']['name']
|
if 'name' in token_response['userinfo']:
|
||||||
session['email'] = token_response['userinfo']['email']
|
session['userinfo_name'] = token_response['userinfo']['name']
|
||||||
|
if 'email' in token_response['userinfo']:
|
||||||
|
session['userinfo_email'] = token_response['userinfo']['email']
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route("/", methods=['GET', 'POST'])
|
@app.route("/", methods=['GET', 'POST'])
|
||||||
def index():
|
def index():
|
||||||
attributes = False
|
auth_error = False
|
||||||
paint_logout = False
|
userinfo_name = False
|
||||||
not_auth_warn = False
|
userinfo_email = False
|
||||||
user_name = False
|
token_data = False
|
||||||
user_email = False
|
|
||||||
if 'sso' in request.args:
|
if 'sso' in request.args:
|
||||||
redirect_uri = url_for('auth', _external=True)
|
redirect_uri = url_for('auth', _external=True)
|
||||||
return oauth.keycloak.authorize_redirect(redirect_uri)
|
return oauth.keycloak.authorize_redirect(redirect_uri)
|
||||||
@ -77,7 +77,7 @@ def index():
|
|||||||
if 'refresh_token' in request.cookies:
|
if 'refresh_token' in request.cookies:
|
||||||
# propagate logout to Keycloak
|
# propagate logout to Keycloak
|
||||||
refresh_token = request.cookies['refresh_token']
|
refresh_token = request.cookies['refresh_token']
|
||||||
requests.post(f'{issuer}/protocol/openid-connect/logout', data={
|
requests.post(f'{idp_url}/protocol/openid-connect/logout', data={
|
||||||
"client_id": client_id,
|
"client_id": client_id,
|
||||||
"client_secret": client_secret,
|
"client_secret": client_secret,
|
||||||
"refresh_token": refresh_token,
|
"refresh_token": refresh_token,
|
||||||
@ -86,33 +86,45 @@ def index():
|
|||||||
response.set_cookie('access_token', '', expires=0)
|
response.set_cookie('access_token', '', expires=0)
|
||||||
response.set_cookie('refresh_token', '', expires=0)
|
response.set_cookie('refresh_token', '', expires=0)
|
||||||
response.set_cookie('id_token', '', expires=0)
|
response.set_cookie('id_token', '', expires=0)
|
||||||
if 'name' in session:
|
if 'userinfo_name' in session:
|
||||||
del session['name']
|
del session['userinfo_name']
|
||||||
if 'email' in session:
|
if 'userinfo_email' in session:
|
||||||
del session['email']
|
del session['userinfo_email']
|
||||||
return response
|
return response
|
||||||
elif 'error' in request.args:
|
elif 'error' in request.args:
|
||||||
not_auth_warn = True
|
auth_error = request.args['error']
|
||||||
|
|
||||||
|
if 'userinfo_name' in session:
|
||||||
|
userinfo_name = session['userinfo_name']
|
||||||
|
|
||||||
|
if 'userinfo_email' in session:
|
||||||
|
userinfo_email = session['userinfo_email']
|
||||||
|
|
||||||
# it is OK to use ID token to display user info on client side
|
# it is OK to use ID token to display user info on client side
|
||||||
# is it not OK to use access token on client side
|
# is it not OK to use access token on client side
|
||||||
if 'id_token' in request.cookies:
|
if 'id_token' in request.cookies:
|
||||||
paint_logout = True
|
id_token = request.cookies['id_token']
|
||||||
attributes = {'id_token': [request.cookies['id_token']]}.items()
|
jwks_uri = f"{idp_url}/protocol/openid-connect/certs"
|
||||||
|
jwks_client = jwt.PyJWKClient(jwks_uri)
|
||||||
if 'name' in session:
|
key = jwks_client.get_signing_key_from_jwt(id_token)
|
||||||
user_name = session['name']
|
try:
|
||||||
|
token_data = jwt.decode(
|
||||||
if 'email' in session:
|
id_token,
|
||||||
user_email = session['email']
|
key.key,
|
||||||
|
algorithms=["RS256"],
|
||||||
|
audience=client_id,
|
||||||
|
options={'verify_signature': False, 'verify_aud': True}
|
||||||
|
)
|
||||||
|
auth_error = False
|
||||||
|
except Exception as e:
|
||||||
|
auth_error = e
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
'index.html',
|
'index.html',
|
||||||
attributes=attributes,
|
auth_error=auth_error,
|
||||||
not_auth_warn=not_auth_warn,
|
userinfo_name=userinfo_name,
|
||||||
paint_logout=paint_logout,
|
userinfo_email=userinfo_email,
|
||||||
user_name=user_name,
|
token_data=token_data,
|
||||||
user_email=user_email,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
authlib
|
authlib
|
||||||
flask
|
flask
|
||||||
requests
|
requests
|
||||||
|
urllib.parse
|
||||||
|
PyJWT
|
||||||
|
cryptography
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if not_auth_warn %}
|
|
||||||
<div class="alert alert-danger" role="alert">Not authorized</div>
|
|
||||||
{% else %}
|
|
||||||
<p>The API content</p>
|
|
||||||
{% endif %}
|
|
||||||
<a href="/" class="btn btn-dark">Back</a>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -2,39 +2,31 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% if not_auth_warn %}
|
{% if auth_error %}
|
||||||
<div class="alert alert-danger" role="alert">Not authenticated</div>
|
<div class="alert alert-danger" role="alert">Not authenticated: {{ auth_error }}</div>
|
||||||
|
{% else %}
|
||||||
|
{% if userinfo_name and userinfo_email %}
|
||||||
|
<div class="alert alert-success" role="alert">Welcome {{ userinfo_name }} ({{ userinfo_email }}) !</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if token_data %}
|
||||||
{% if user_name and user_email %}
|
|
||||||
<div class="alert alert-success" role="alert">Welcome {{ user_name }} ({{ user_email }}) !</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if paint_logout %}
|
|
||||||
{% if attributes %}
|
|
||||||
<p>You have the following attributes:</p>
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<th>Name</th><th>Values</th>
|
<th>Name</th><th>Values</th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for attr in attributes %}
|
{% for key in token_data %}
|
||||||
<tr><td>{{ attr.0 }}</td>
|
<tr>
|
||||||
<td><ul class="list-unstyled">
|
<td>{{ key }}</td>
|
||||||
{% for val in attr.1 %}
|
<td>{{ token_data[key] }}</td>
|
||||||
<li>{{ val }}</li>
|
</tr>
|
||||||
{% endfor %}
|
|
||||||
</ul></td></tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
|
||||||
<div class="alert alert-danger" role="alert">You don't have any attributes</div>
|
|
||||||
{% endif %}
|
|
||||||
<a href="?slo" class="btn btn-danger">Logout</a>
|
<a href="?slo" class="btn btn-danger">Logout</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="?sso" class="btn btn-primary">Login</a>
|
<a href="?sso" class="btn btn-primary">Login</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<a href="/api" class="btn btn-secondary">Call API</a>
|
<a href="/api" class="btn btn-secondary">Call API</a>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user