import requests import jwt from authlib.integrations.base_client import OAuthError from authlib.integrations.flask_client import OAuth from flask import Flask, request, redirect, session, render_template, url_for, make_response # TODO add client export json in project & git app = Flask(__name__) # TODO still useful ? app.config['SECRET_KEY'] = 'onelogindemopytoolkit' issuer="https://id.vilanet.fr/realms/vilanet" client_id="client-oidc" client_secret="BqWWnuj5JkgZZWEaXuR8bprEx53lqGxC" # To manage OIDC flow for UI client oauth = OAuth(app=app) oauth.register( name="keycloak", client_id=client_id, client_secret=client_secret, server_metadata_url=f'{issuer}/.well-known/openid-configuration', client_kwargs={ 'scope': 'openid', 'code_challenge_method': 'S256', } ) @app.route("/api") def api(): not_auth_warn = True # is it OK to use access token to check API authorization on server side # it is not OK to use ID token to check API authorization on server side if 'accessToken' in request.args: access_token = request.args['accessToken'] jwks_uri = f"{issuer}/protocol/openid-connect/certs" jwks_client = jwt.PyJWKClient(jwks_uri) key = jwks_client.get_signing_key_from_jwt(access_token) data = jwt.decode(access_token, key.key, algorithms=["RS256"], audience='client-oidc', options={'verify_signature': True, 'verify_aud': True}) print(data) not_auth_warn = False # TODO verify token and check role return render_template( 'api.html', not_auth_warn=not_auth_warn, ) @app.route("/auth") def auth(): try: token_response = oauth.keycloak.authorize_access_token() except OAuthError as e: return redirect('/?error=access_denied') response = make_response(redirect('/')) access_token = token_response['access_token'] if access_token: response.set_cookie('accessToken', access_token, httponly=True) refresh_token = token_response['refresh_token'] if refresh_token: response.set_cookie('refreshToken', refresh_token, httponly=True) id_token = token_response['id_token'] if id_token: response.set_cookie('idToken', id_token, httponly=True) if token_response['userinfo']: session['name'] = token_response['userinfo']['name'] session['email'] = token_response['userinfo']['email'] return response @app.route("/", methods=['GET', 'POST']) def index(): attributes = False access_token = False paint_logout = False not_auth_warn = False user_name = False user_email = False 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: # propagate logout to Keycloak refresh_token = request.cookies['refresh_token'] requests.post(f'{issuer}/protocol/openid-connect/logout', data={ "client_id": client_id, "client_secret": client_secret, "refresh_token": refresh_token, }) response = make_response(redirect('/')) response.set_cookie('accessToken', '', expires=0) response.set_cookie('refreshToken', '', expires=0) response.set_cookie('idToken', '', expires=0) if 'name' in session: del session['name'] if 'email' in session: del session['email'] return response elif 'error' in request.args: not_auth_warn = True # 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 if 'idToken' in request.cookies: paint_logout = True attributes = {'idToken': [request.cookies['idToken']]}.items() if 'name' in session: user_name = session['name'] if 'email' in session: user_email = session['email'] if 'accessToken' in request.cookies: access_token = request.cookies['accessToken'] return render_template( 'index.html', attributes=attributes, access_token=access_token, not_auth_warn=not_auth_warn, paint_logout=paint_logout, user_name=user_name, user_email=user_email, ) if __name__ == "__main__": app.run(host='127.0.0.1', port=5001, debug=True)