feat: Send account selection prompt to IDP when account switch link is clicked

This commit is contained in:
Ginger committed 2026-07-02 16:34:38 -04:00
1 parent e2fe166d63
commit e649af33b9
3 files changed
+21 -7

No files matched your search

+7 -3
View File
@@ -26,7 +26,7 @@ use crate::{
oidc::{OIDC_SESSION_ID_KEY, OidcSession, OidcSessionState},
},
response,
session::{LoginQuery, LoginTarget, User, UserSession},
session::{LoginIntent, LoginQuery, LoginTarget, User, UserSession},
template,
};
@@ -74,7 +74,7 @@ struct LoginForm {
async fn route_login(
State(services): State<crate::State>,
Extension(context): Extension<TemplateContext>,
Expect(Query(LoginQuery { next, reauthenticate })): Expect<Query<LoginQuery>>,
Expect(Query(LoginQuery { next, reauthenticate, intent })): Expect<Query<LoginQuery>>,
session_store: Session,
user: User<true>,
PostForm(form): PostForm<LoginForm>,
@@ -84,7 +84,11 @@ async fn route_login(
let login_type = if services.oidc.enabled() {
let (session, redirect_url) = services
.oidc
.begin_session(reauthenticate.then(|| CoreAuthPrompt::Consent))
.begin_session(match intent {
| Some(LoginIntent::SwitchAccounts) => Some(CoreAuthPrompt::SelectAccount),
| _ if reauthenticate => Some(CoreAuthPrompt::Consent),
| _ => None,
})
.await;
session_store
+3 -2
View File
@@ -17,7 +17,7 @@ use crate::{
components::{Avatar, AvatarType, ClientScopes},
},
response,
session::{LoginQuery, LoginTarget, User},
session::{LoginIntent, LoginQuery, LoginTarget, User},
template,
};
@@ -129,7 +129,8 @@ async fn route_authorization_code(
context,
serde_urlencoded::to_string(LoginQuery {
next: Some(LoginTarget::AuthorizationCode(query)),
reauthenticate: false,
intent: Some(LoginIntent::SwitchAccounts),
..Default::default()
})
.unwrap(),
user_id,
+11 -2
View File
@@ -24,6 +24,8 @@ pub(crate) struct LoginQuery {
pub next: Option<LoginTarget>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub reauthenticate: bool,
#[serde(default)]
pub intent: Option<LoginIntent>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
@@ -65,6 +67,12 @@ impl LoginTarget {
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum LoginIntent {
SwitchAccounts,
}
/// An extractor that fetches the authenticated user.
pub(crate) struct User<const ALLOW_LOCKED: bool = false>(Option<UserSession>);
@@ -107,7 +115,7 @@ impl<const ALLOW_LOCKED: bool> User<ALLOW_LOCKED> {
} else {
Err(WebError::LoginRequired(LoginQuery {
next: Some(or_else),
reauthenticate: false,
..Default::default()
}))
}
}
@@ -122,12 +130,13 @@ impl<const ALLOW_LOCKED: bool> User<ALLOW_LOCKED> {
Err(WebError::LoginRequired(LoginQuery {
next: Some(or_else),
reauthenticate: true,
..Default::default()
}))
}
} else {
Err(WebError::LoginRequired(LoginQuery {
next: Some(or_else),
reauthenticate: false,
..Default::default()
}))
}
}