#!/usr/bin/env python3 """ OIDC Test Client Demonstriert den vollständigen Authorization Code Flow """ from flask import Flask, request, redirect, session, render_template_string import requests from urllib.parse import urlencode import secrets app = Flask(__name__) app.secret_key = secrets.token_hex(32) app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' app.config['SESSION_COOKIE_SECURE'] = False # OIDC Server Konfiguration OIDC_ISSUER = "http://localhost:5000" CLIENT_ID = "test-client" CLIENT_SECRET = "test-secret" REDIRECT_URI = "http://localhost:8080/callback" # In-Memory Store für OAuth States (überlebt Debug-Reloads nicht, aber reicht für Tests) oauth_states = {} # HTML Template HOME_TEMPLATE = """ OIDC Test Client

🧪 OIDC Test Client

Test-Anwendung für den Authorization Code Flow

{% if user_info %} {% else %} {% endif %}
""" @app.route('/') def index(): """Startseite""" user_info = session.get('user_info') tokens = session.get('tokens') return render_template_string( HOME_TEMPLATE, user_info=user_info, tokens=tokens ) @app.route('/login') def login(): """Startet den Authorization Flow""" # State für CSRF-Protection generieren state = secrets.token_urlsafe(32) # State in In-Memory Store speichern oauth_states[state] = True # Authorization URL bauen auth_params = { 'client_id': CLIENT_ID, 'redirect_uri': REDIRECT_URI, 'response_type': 'code', 'scope': 'openid profile email', 'state': state } auth_url = f"{OIDC_ISSUER}/authorize?{urlencode(auth_params)}" return redirect(auth_url) @app.route('/callback') def callback(): """Callback Endpoint - empfängt Authorization Code""" # Authorization Code und State aus Query Params code = request.args.get('code') state = request.args.get('state') # State validieren (CSRF-Protection) if not state or state not in oauth_states: return f"Invalid state parameter. State: {state}, Valid states: {list(oauth_states.keys())}", 400 # State verbrauchen (einmalige Verwendung) del oauth_states[state] if not code: error = request.args.get('error') error_description = request.args.get('error_description', '') return f"Authorization failed: {error} - {error_description}", 400 # Authorization Code gegen Tokens tauschen token_data = { 'grant_type': 'authorization_code', 'code': code, 'redirect_uri': REDIRECT_URI, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET } try: token_response = requests.post(f"{OIDC_ISSUER}/token", data=token_data) token_response.raise_for_status() tokens = token_response.json() except requests.RequestException as e: return f"Token exchange failed: {str(e)}", 500 # UserInfo mit Access Token abrufen try: userinfo_response = requests.get( f"{OIDC_ISSUER}/userinfo", headers={'Authorization': f"Bearer {tokens['access_token']}"} ) userinfo_response.raise_for_status() user_info = userinfo_response.json() except requests.RequestException as e: return f"UserInfo request failed: {str(e)}", 500 # In Session speichern session['tokens'] = tokens session['user_info'] = user_info # Zurück zur Startseite return redirect('/') @app.route('/logout') def logout(): """Logout - löscht Session""" session.clear() return redirect('/') if __name__ == '__main__': print("=" * 60) print("🧪 OIDC Test Client gestartet") print("=" * 60) print(f"Client URL: http://localhost:8080") print(f"OIDC Server: {OIDC_ISSUER}") print(f"Callback URI: {REDIRECT_URI}") print("=" * 60) print("\nÖffne im Browser: http://localhost:8080") print("=" * 60) app.run(host='0.0.0.0', port=8080, debug=True)