From 9e53b8814b69f1768e1229708b45dc63409c6b24 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Mon, 21 Oct 2024 10:40:23 +0200 Subject: [PATCH] Add logout with id_token_hint --- client/src/stores/user.ts | 19 +------- env_secrets/caprover_myvbv-prod.env | Bin 1104 -> 936 bytes env_secrets/local_chrigu.env | Bin 3076 -> 3228 bytes server/config/settings/base.py | 2 + server/config/urls.py | 4 +- server/vbv_lernwelt/core/views.py | 8 +--- server/vbv_lernwelt/course_session/views.py | 4 +- .../dashboard/graphql/types/dashboard.py | 4 +- server/vbv_lernwelt/dashboard/views.py | 5 +-- .../iam/tests/test_permissions.py | 4 +- server/vbv_lernwelt/importer/services.py | 4 ++ server/vbv_lernwelt/sso/urls.py | 5 +++ server/vbv_lernwelt/sso/views.py | 42 +++++++++++++++--- 13 files changed, 53 insertions(+), 48 deletions(-) diff --git a/client/src/stores/user.ts b/client/src/stores/user.ts index 1f41d130..d8413709 100644 --- a/client/src/stores/user.ts +++ b/client/src/stores/user.ts @@ -5,14 +5,6 @@ import { directUpload } from "@/services/files"; import dayjs from "dayjs"; import { defineStore } from "pinia"; -let logoutRedirectUrl = import.meta.env.VITE_LOGOUT_REDIRECT || "/"; - -if (import.meta.env.VITE_OAUTH_API_BASE_URL) { - logoutRedirectUrl = `${ - import.meta.env.VITE_OAUTH_API_BASE_URL - }logout/?post_logout_redirect_uri=${window.location.origin}&client_id=iterativ`; -} - const AVAILABLE_LANGUAGES = ["de", "fr", "it"]; export type AvailableLanguages = "de" | "fr" | "it"; @@ -150,16 +142,7 @@ export const useUserStore = defineStore({ handleLogout() { Object.assign(this, initialUserState); - itPost("/api/core/logout/", {}).then(() => { - let redirectUrl; - - if (logoutRedirectUrl !== "") { - redirectUrl = logoutRedirectUrl; - } else { - redirectUrl = "/"; - } - window.location.href = redirectUrl; - }); + window.location.href = `${window.location.origin}/sso/logout`; }, async fetchUser() { const data: any = await itGetCached("/api/core/me/"); diff --git a/env_secrets/caprover_myvbv-prod.env b/env_secrets/caprover_myvbv-prod.env index e57d1105820c4157d6805e596db4d19d48ddd0ef..13eec0c7af7f289e86933cd2d5ed3ecacf4f4f93 100644 GIT binary patch literal 936 zcmV;Z16TY2M@dveQdv+`07|TA**CljzJa^mtbb5r0@^FJu8AbgAQLEh^I$GXr0VFb zy2=xIC_17|*5)&)uqN44=8yL*QE&-irF&T2G@+H*AUjP5N*~yvNmTI);_KYZ7_NPL zm4zT2v}ko>*PD$`ft>!wFVczh3!lg1F!JO^oa7n~(LwKD6LFy`jYDMvlL>ZwEt1Yp z0ebagqw;5VR(0@>knx1$mw@jrBcY_j7XfQ)MkDwX9xJG#YrCHSXF7+;e?-jOt^(2N z<7+VME3pn15zxaBcXiclGI)7QS_#pVA4UIO=Bs?lPu3P+r1K4vNPH%jgu!Y zZQ;o>!@!r91wD0$ySrnTYY&{z<&d|Be<2l0qmm5=86v*la&2-P0n^`>z0)A^4{geZ zC5RcNG-n}lSJe^BIh(FB5Kky{sZ`71b7WY<-9Fmtq|XCfnUsV%&;+8Tc<~0*poj*Xu?K{I+Ta*2 zyqIdUzRuYN=z8;T*d2>`ArLYqOISde7;xY?g)>`xxNv@L5$wkJa5LUu_NTcc(7CEP zK6=MhCD%%q{PXy_9lyl1y=MeN-<`;+^+1x>atfd>Bt&BOUb+&!h?ctklny#;Wywd; z6)Q0KIzHv=fgSK++nv1RmFX~z;v>q$$=ppV?|+SM4KOOgKPn7eaG;>vOwQ||63&c9 zC$Hz!n!6xn^hf=iWX4$$GqS}D;S9O{+p$b=p!43sw6W@Z02b0kg?g_sBD*EVYT)piO?lN?16 z8>M-c)L~xV<7zOUX7uis+pRx-6np&68!m|?M=VM$uN#pQ$8@^F&DZ`UcCPbE`O=^2 KN|WtcxiCk}^V1Lj literal 1104 zcmV-W1h4x5M@dveQdv+`0LjQybL%@FTDC2zfrE&XHL3-iQ2pJoIu{W!8Ei)|@kn56 zLhus`AmlXB7hD-g#s=WXYT|M0J{?12a|{-~u_|JMC%Vl~mi4j~mdicxa7+3xKnF8h zNbItt&rSN8^cD)Us35Kxy=?t=G0>p#zFk+O-lPk?0~eim>6kL&qJ93^rv#un$5P!^ z5v?y?$a#BfzqCT5(|mTc=m}^RmkwW-^rG@C4XJ&1w*Y0lN*ZLa|e)04N>WbFhHmep;3k5)5Pfq^ujO-_qQFzqMGK0w!#P8ra z_6-y>rOi&Etb2`1L!l|y@XOX7Jc)+ZGlwj70QEre+H(Jr0bl^Ps!}DTj@7ZjR zV*7{BffksAS4Sg?3o%>UhmgMAoZllzuU}Qbb!YES8{YcKM|gJtWkOK8E>29Y8F`6o z$8}zMPuy>kUHu@SHj{?bqQzVl(s#;uwZC0**e%m?Y91_q3ZgD8qZ4d-?2Kyi?m~#0 zO~DCA!Rc%HH1lMU8((B&a368iT6=^1G>bj9pYQ@Jc!CBSIuK3~G&jn@<%v+6hCeq& zNINUX`)9nJxpiy$k)&E*iEcLNgs-0jo>7u=Tj`DC-2b!afA2`bNai~1UnGohjc7Jn zfFewFQU4ZQ5j#*d_pJ`kX7MJLhd6mZ$y)O5BL6{J20z!jsU| z`n>b@tK4B<#OJ>!-nhLENS5q^Qb>r8LNG5DhkX^y7+PCn|6W9twMuLARY;* z8}M)q$sf&}a*|ZQI$OHqLlfz)QPLjMBmeIA+e+n4E=?naayUBm@nLGwyje)Cwa7V6 z1~u4Rhya}fqU0#1Ge7s#7^7v(4DdD{5QGAcS&lSn*o9`wn1GIw#@p+3sk+P8&n|vn zdG8|Q)V8UyF)8BK$wN??Q@)v2tY6*$OtdRU&!8Yr!Kw?H7*xsqr4*UrpNX4z5mrbo zK7l?uHW`eR)zzLXFgkL2hFP!?KtG|2gP#r;ZgC#Np39E|*tvC8_(GZDb#EI&wev;O WNb!G9O*e1Lkl}avHD90I35!JW@FceY diff --git a/env_secrets/local_chrigu.env b/env_secrets/local_chrigu.env index f8d1f1fe4f0132d5dbb4f28bb4b5795d71fab916..6c686f355533be7d8abcc43fab9ee13481ddd522 100644 GIT binary patch literal 3228 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 SG|qQ$W User: logger.debug( "create_or_update_user", @@ -544,6 +545,9 @@ def create_or_update_user( user.update_additional_json_data({"intermediate_sso_id": intermediate_sso_id}) init_notification_settings(user) + if id_token: + user.update_additional_json_data({"id_token": id_token}) + user.set_unusable_password() user.save() diff --git a/server/vbv_lernwelt/sso/urls.py b/server/vbv_lernwelt/sso/urls.py index ba607c3f..9713edfe 100644 --- a/server/vbv_lernwelt/sso/urls.py +++ b/server/vbv_lernwelt/sso/urls.py @@ -12,4 +12,9 @@ urlpatterns = [ django_view_authentication_exempt(views.authorize_signin), name="authorize", ), + path( + r"logout/", + django_view_authentication_exempt(views.logout), + name="logout", + ), ] diff --git a/server/vbv_lernwelt/sso/views.py b/server/vbv_lernwelt/sso/views.py index a665d7af..bae5040b 100644 --- a/server/vbv_lernwelt/sso/views.py +++ b/server/vbv_lernwelt/sso/views.py @@ -5,6 +5,7 @@ import structlog as structlog from authlib.integrations.base_client import OAuthError from django.conf import settings from django.contrib.auth import login as dj_login +from django.contrib.auth import logout as dj_logout from django.shortcuts import redirect from sentry_sdk import capture_exception @@ -97,7 +98,7 @@ def authorize_signin(request): capture_exception(e) return redirect("/") - id_token = decode_jwt(jwt_token["id_token"]) + id_token_decoded = decode_jwt(jwt_token["id_token"]) state = json.loads( base64.urlsafe_b64decode(request.GET.get("state").encode()).decode() @@ -108,17 +109,44 @@ def authorize_signin(request): logger.debug( f"SSO Authorize (course={course}, next={next_url}", - sso_authorize_id_token=id_token, + sso_authorize_id_token=id_token_decoded, ) user = create_or_update_user( - email=id_token.get("email", ""), - sso_id=id_token.get("oid"), - first_name=id_token.get("given_name", ""), - last_name=id_token.get("family_name", ""), - intermediate_sso_id=id_token.get("sub"), + email=id_token_decoded.get("email", ""), + sso_id=id_token_decoded.get("oid"), + first_name=id_token_decoded.get("given_name", ""), + last_name=id_token_decoded.get("family_name", ""), + intermediate_sso_id=id_token_decoded.get("sub"), + id_token=jwt_token["id_token"], ) dj_login(request, user) return get_redirect_uri(user=user, course=course, next_url=next_url) + + +def logout(request): + user_data = ( + request.user.additional_json_data if request.user.is_authenticated else {} + ) + dj_logout(request) + + redirect_uri = getattr(settings, "OAUTH_LOGOUT_REDIRECT_URI", "/") + + # Handle scenarios when SSO-related data is missing, assume local login + if not user_data.get("intermediate_sso_id"): + logger.debug("SSO Logout", extra={"mode": "intermediate_sso_id_not_set"}) + return redirect("/") + + # Temporary solution if id_token is not set in the user data, can be removed after rollout + 2 weeks + id_token = user_data.get("id_token", "") + if not id_token: + logger.debug("SSO Logout", extra={"mode": "id_token_not_set"}) + return redirect(f"{redirect_uri}&client_id=iterativ") + + # Handle scenarios when SSO-related data is present or redirect_uri is not set + if not redirect_uri: + logger.debug("SSO Logout", extra={"mode": "redirect_uri_not_set"}) + return redirect("/") + return redirect(f"{redirect_uri}&id_token_hint={id_token}")