client-oidc/app.py

138 lines
4.6 KiB
Python
Raw Permalink Normal View History

2024-06-26 18:10:40 +00:00
import urllib.parse
import jwt
2024-06-20 10:12:05 +00:00
import requests
from authlib.integrations.base_client import OAuthError
2024-06-20 10:12:05 +00:00
from authlib.integrations.flask_client import OAuth
2024-06-26 18:10:40 +00:00
from flask import Flask, request, redirect, session, render_template, url_for, make_response
2024-06-18 21:37:30 +00:00
2024-06-24 16:56:08 +00:00
# TODO add client export json in project & git
2024-06-18 21:37:30 +00:00
app = Flask(__name__)
2024-06-26 18:10:40 +00:00
# key used to encore session cookie
app.config['SECRET_KEY'] = 'client-oidc'
idp_url = "https://id.vilanet.fr/realms/vilanet"
client_url = "http://localhost:5001"
client_id = "client-oidc"
client_secret = "BqWWnuj5JkgZZWEaXuR8bprEx53lqGxC"
2024-06-18 21:37:30 +00:00
2024-06-26 18:10:40 +00:00
resource_server_url = 'http://localhost:5002'
2024-06-20 10:12:05 +00:00
# To manage OIDC flow for UI client
2024-06-20 10:12:05 +00:00
oauth = OAuth(app=app)
oauth.register(
name="keycloak",
client_id=client_id,
client_secret=client_secret,
2024-06-26 18:10:40 +00:00
server_metadata_url=f'{idp_url}/.well-known/openid-configuration',
client_kwargs={'scope': 'openid', 'code_challenge_method': 'S256'}
2024-06-20 10:12:05 +00:00
)
2024-06-27 16:14:17 +00:00
@app.route("/service1")
def service1():
return make_response(redirect(resource_server_url + "/api/v1/service1?" + urllib.parse.urlencode({'callbackUrl': client_url})))
@app.route("/service2")
def service2():
return make_response(redirect(resource_server_url + "/api/v1/service2?" + urllib.parse.urlencode({'callbackUrl': client_url})))
2024-06-20 10:12:05 +00:00
@app.route("/auth")
def auth():
try:
token_response = oauth.keycloak.authorize_access_token()
except OAuthError as e:
2024-06-26 18:10:40 +00:00
return redirect('/?' + urllib.parse.urlencode({'error': e}))
response = make_response(redirect('/'))
access_token = token_response['access_token']
if access_token:
2024-06-26 18:10:40 +00:00
response.set_cookie('access_token', access_token, httponly=True, domain='localhost')
refresh_token = token_response['refresh_token']
if refresh_token:
2024-06-26 18:10:40 +00:00
response.set_cookie('refresh_token', refresh_token, httponly=True, domain='localhost')
id_token = token_response['id_token']
if id_token:
2024-06-26 18:10:40 +00:00
response.set_cookie('id_token', id_token, httponly=True, domain='localhost')
if token_response['userinfo']:
2024-06-26 18:10:40 +00:00
if 'name' in token_response['userinfo']:
session['userinfo_name'] = token_response['userinfo']['name']
if 'email' in token_response['userinfo']:
session['userinfo_email'] = token_response['userinfo']['email']
return response
2024-06-20 10:12:05 +00:00
2024-06-18 21:37:30 +00:00
@app.route("/", methods=['GET', 'POST'])
def index():
2024-06-26 18:10:40 +00:00
auth_error = False
userinfo_name = False
userinfo_email = False
token_data = False
2024-06-20 10:12:05 +00:00
if 'sso' in request.args:
redirect_uri = url_for('auth', _external=True)
return oauth.keycloak.authorize_redirect(redirect_uri)
elif 'slo' in request.args:
if 'refresh_token' in request.cookies:
2024-06-20 10:12:05 +00:00
# propagate logout to Keycloak
refresh_token = request.cookies['refresh_token']
2024-06-26 18:10:40 +00:00
requests.post(f'{idp_url}/protocol/openid-connect/logout', data={
"client_id": client_id,
"client_secret": client_secret,
"refresh_token": refresh_token,
2024-06-20 10:12:05 +00:00
})
response = make_response(redirect('/'))
2024-06-25 16:53:25 +00:00
response.set_cookie('access_token', '', expires=0)
response.set_cookie('refresh_token', '', expires=0)
response.set_cookie('id_token', '', expires=0)
2024-06-26 18:10:40 +00:00
if 'userinfo_name' in session:
del session['userinfo_name']
if 'userinfo_email' in session:
del session['userinfo_email']
return response
elif 'error' in request.args:
2024-06-26 18:10:40 +00:00
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']
2024-06-20 10:12:05 +00:00
# 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
2024-06-25 16:53:25 +00:00
if 'id_token' in request.cookies:
2024-06-26 18:10:40 +00:00
id_token = request.cookies['id_token']
jwks_uri = f"{idp_url}/protocol/openid-connect/certs"
jwks_client = jwt.PyJWKClient(jwks_uri)
key = jwks_client.get_signing_key_from_jwt(id_token)
try:
token_data = jwt.decode(
id_token,
key.key,
algorithms=["RS256"],
audience=client_id,
options={'verify_signature': False, 'verify_aud': True}
)
auth_error = False
except Exception as e:
auth_error = e
2024-06-18 21:37:30 +00:00
return render_template(
'index.html',
2024-06-26 18:10:40 +00:00
auth_error=auth_error,
userinfo_name=userinfo_name,
userinfo_email=userinfo_email,
token_data=token_data,
2024-06-18 21:37:30 +00:00
)
if __name__ == "__main__":
app.run(host='127.0.0.1', port=5001, debug=True)