|
12 | 12 |
|
13 | 13 | from flask import Blueprint, redirect, url_for, session, flash, current_app, request, render_template |
14 | 14 | from authlib.integrations.flask_client import OAuth |
| 15 | +from authlib.integrations.base_client import MismatchingStateError |
15 | 16 |
|
16 | 17 | from app import db |
17 | 18 |
|
@@ -171,6 +172,11 @@ def _handle_google_callback(): |
171 | 172 |
|
172 | 173 | try: |
173 | 174 | token = oauth.google.authorize_access_token() |
| 175 | + except MismatchingStateError: |
| 176 | + current_app.logger.info('Google state mismatch — stale session, retrying OAuth') |
| 177 | + log_login_failure("stale_session", provider="google", severity="INFO") |
| 178 | + db.session.commit() |
| 179 | + return redirect(url_for('auth.login')) |
174 | 180 | except Exception as e: |
175 | 181 | current_app.logger.error(f'Google OAuth error: {e}') |
176 | 182 | log_login_failure("oauth_error", provider="google") |
@@ -217,6 +223,14 @@ def _handle_keycloak_callback(): |
217 | 223 |
|
218 | 224 | try: |
219 | 225 | token = oauth.keycloak.authorize_access_token() |
| 226 | + except MismatchingStateError: |
| 227 | + # Stale session: user's Flask session expired but Keycloak SSO session |
| 228 | + # was still active, so the callback state doesn't match. This is benign |
| 229 | + # — just re-initiate the OAuth flow and it will succeed on the retry. |
| 230 | + current_app.logger.info('Keycloak state mismatch — stale session, retrying OAuth') |
| 231 | + log_login_failure("stale_session", provider="keycloak", severity="INFO") |
| 232 | + db.session.commit() |
| 233 | + return redirect(url_for('auth.login')) |
220 | 234 | except Exception as e: |
221 | 235 | current_app.logger.error(f'Keycloak OAuth error: {e}') |
222 | 236 | log_login_failure("oauth_error", provider="keycloak") |
|
0 commit comments