Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 32 additions & 44 deletions src/api/client/session/sso/complete.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,52 +15,40 @@
background: #f4f6f8;
color: #2e3648;
}
@media(prefers-color-scheme:dark) {
body {
background: #151924;
color: #f4f6f8;
}
.card {
text-align: center;
background: #fff;
padding: 2.5rem;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, .08);
max-width: 320px;
width: 90%;
}
.card {
text-align: center;
background: #fff;
padding: 2.5rem;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, .08);
max-width: 320px;
width: 90%;
}
@media(prefers-color-scheme:dark) {
.card {
background: #222632;
box-shadow: 0 4px 20px rgba(0, 0, 0, .3);
}
.icon {
background: #0dbd8b;
color: #fff;
width: 64px;
height: 64px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
margin: 0 auto 1.5rem;
}
h1 {
font-size: 1.25rem;
margin: 0 0 .5rem;
}
p {
color: #6b7280;
margin: 0;
font-size: .95rem;
}
.icon {
background: #0dbd8b;
color: #fff;
width: 64px;
height: 64px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
margin: 0 auto 1.5rem;
}
h1 {
font-size: 1.25rem;
margin: 0 0 .5rem;
}
p {
color: #6b7280;
margin: 0;
font-size: .95rem;
}
@media(prefers-color-scheme:dark) {
p {
color: #9ca3af;
}
body { background: #151924; color: #f4f6f8; }
.card { background: #222632; box-shadow: 0 4px 20px rgba(0, 0, 0, .3); }
p { color: #9ca3af; }
}
</style>
</head>
Expand All @@ -76,7 +64,7 @@ <h1>Authentication Successful</h1>
if(window.onAuthDone) {
window.onAuthDone()
} else if(window.opener && window.opener.postMessage) {
window.opener.postMessage("authDone","*")
window.opener.postMessage("authDone", window.location.origin)
}
</script>
</body>
Expand Down
115 changes: 49 additions & 66 deletions src/api/client/session/sso/required.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,77 +13,60 @@
min-height: 100vh;
margin: 0;
background: #f4f6f8;
color: #2e3648
color: #2e3648;
}
@media(prefers-color-scheme:dark) {
body {
background: #151924;
color: #f4f6f8;
}
.card {
text-align: center;
background: #fff;
padding: 2.5rem 2rem;
border-radius: 12px;
box -shadow: 0 4px 20px rgba(0, 0, 0, .08);
max-width: 360px;
width: 90%;
}
.card {
text-align: center;
background: #fff;
padding: 2.5rem 2rem;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, .08);
max-width: 360px;
width: 90%;
}
@media(prefers-color-scheme:dark) {
.card {
background: #222632;
box-shadow: 0 4px 20px rgba(0, 0, 0, .3);
}
.icon {
font-size: 48px;
margin-bottom: 1rem;
}
h1 {
font-size: 1 .25rem;
margin: 0 0 1rem;
}
p {
color: #6b7280;
margin: 0 0 1.5rem;
font-size: .95rem;
line-height: 1.5;
}
.icon {
font-size: 48px;
margin-bottom: 1rem;
}
@media(prefers-color-scheme:dark) {
p {
color: #9ca3af
}
.btn {
display: inline-block;
background: #00A36C;
color: #fff;
text-decoration: none;
padding: 12px 24px;
border-radius: 8px;
box-shadow: 2px 2px 2px 2px black;
font-weight: 900;
font-size: 1rem;
transition: opacity .2s;
width: calc(100 % - 48px);
}
.btn:hover {
opacity: .9;
}
.warning {
margin-top: 1.5rem;
font-size: .85rem;
color: #d97706;
background: #960019;
padding: 12px;
border-radius: 8px;
}
h1 {
font-size: 1.25rem;
margin: 0 0 1rem;
}
p {
color: #6b7280;
margin: 0 0 1.5rem;
font-size: .95rem;
line-height: 1.5;
}
.btn {
display: inline-block;
background: #00A36C;
color: #fff;
text-decoration: none;
padding: 12px 24px;
border-radius: 8px;
box-shadow: 2px 2px 2px 2px black;
font-weight: 900;
font-size: 1rem;
transition: opacity .2s;
width: calc(100% - 48px);
}
.btn:hover {
opacity: .9;
}
.warning {
margin-top: 1.5rem;
font-size: .85rem;
color: #fff;
background: #960019;
padding: 12px;
border-radius: 8px;
}
@media(prefers-color-scheme:dark) {
.warning {
background: #811311;
color: #fcd34d;
}
body { background: #151924; color: #f4f6f8; }
.card { background: #222632; box-shadow: 0 4px 20px rgba(0, 0, 0, .3); }
p { color: #9ca3af; }
.warning { background: #811311; color: #fcd34d; }
}
</style>
</head>
Expand Down
48 changes: 37 additions & 11 deletions src/api/client/session/sso/uiaa.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use axum::extract::State;
use ruma::api::client::uiaa::{AuthType, UiaaInfo, get_uiaa_fallback_page};
use tuwunel_core::Result;
use tuwunel_core::{Err, Result};

use crate::Ruma;
use crate::{Ruma, oidc::url_encode};

/// # `GET /_matrix/client/v3/auth/m.login.sso/fallback/web?session={session_id}`
///
Expand All @@ -27,21 +27,47 @@ pub(crate) async fn sso_fallback_route(
|| uiaainfo.completed.contains(&AuthType::OAuth)
};

if services
.uiaa
.get_uiaa_session_by_session_id(session)
.await
.as_ref()
.is_some_and(|(_, _, uiaainfo)| completed(uiaainfo))
{
// Single DB lookup — get_uiaa_session_by_session_id does a full table scan,
// so we call it once and reuse the result for both the completion check and
// the IdP extraction that follows.
let session_data = services.uiaa.get_uiaa_session_by_session_id(session).await;

if session_data.as_ref().is_some_and(|(_, _, uiaainfo)| completed(uiaainfo)) {
let html = include_str!("complete.html");

return Ok(Response::html(html.as_bytes().to_vec()));
}

// Session is not completed yet, show the prompt to continue
// Session is not completed yet. Read the IdP that was bound to this UIAA
// session at creation time from the stored UiaaInfo params. The IdP must
// always be present — auth_uiaa only advertises m.login.sso when it can
// determine exactly one provider, so a missing IdP here is a logic error.
let idp_id: Option<String> = session_data.and_then(|(_, _, uiaainfo)| {
let raw = uiaainfo.params?;
let params: serde_json::Value = serde_json::from_str(raw.get()).ok()?;
params["m.login.sso"]["identity_providers"]
.as_array()?
.first()?["id"]
.as_str()
.map(ToOwned::to_owned)
});

// The IdP MUST have been bound at UIAA session creation time.
// If it is missing, auth_uiaa should not have advertised m.login.sso.
// Returning an error is safer than routing to an arbitrary provider.
let Some(ref idp) = idp_id else {
return Err!(Request(Forbidden(
"No SSO provider bound to this UIAA session; cannot complete re-authentication"
)));
};

let url_str = format!(
"/_matrix/client/v3/login/sso/redirect/{}?redirectUrl=uiaa:{}",
url_encode(idp),
url_encode(session)
);

let html = include_str!("required.html");
let url_str = format!("/_matrix/client/v3/login/sso/redirect?redirectUrl=uiaa:{session}");
let output = html.replace("{{url_str}}", &url_str);

Ok(Response::html(output.into_bytes()))
Expand Down
Loading
Loading