Redirect to login on 401 and return afterwards
The SPA now sends the user to a new /login page whenever an API call answers 401, remembering the page they were on. The OIDC callback always lands back on the configured post-login redirect, so the saved page is restored client-side once a session exists. - auth: save/restore the return path in sessionStorage; defer the navigation so the failing request can finish first - api: centralize 401 handling (me() still treats 401 as logged out) - login page + shared login card, extracted from the lobby - guard async signal writes so an in-flight request cannot touch a page unmounted by the redirect
This commit is contained in:
+33
-1
@@ -1,22 +1,27 @@
|
||||
//! Application entry point.
|
||||
mod api;
|
||||
mod auth;
|
||||
mod components;
|
||||
mod model;
|
||||
mod pages;
|
||||
mod ws;
|
||||
|
||||
use sycamore::prelude::*;
|
||||
use sycamore_router::{HistoryIntegration, Route, Router};
|
||||
use sycamore_router::{navigate, HistoryIntegration, Route, Router};
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
use pages::game::GamePage;
|
||||
use pages::history::HistoryPage;
|
||||
use pages::leaderboard::LeaderboardPage;
|
||||
use pages::lobby::LobbyPage;
|
||||
use pages::login::LoginPage;
|
||||
|
||||
#[derive(Route, Clone)]
|
||||
enum AppRoutes {
|
||||
#[to("/")]
|
||||
Lobby,
|
||||
#[to("/login")]
|
||||
Login,
|
||||
#[to("/game/<id>")]
|
||||
Game { id: String },
|
||||
#[to("/history")]
|
||||
@@ -27,6 +32,31 @@ enum AppRoutes {
|
||||
NotFound,
|
||||
}
|
||||
|
||||
/// After the OIDC callback lands the SPA back on the configured
|
||||
/// post-login redirect, forward the user to the page they were on before
|
||||
/// being sent to the login page (see [`auth`]) — but only once a session
|
||||
/// exists, so a cancelled login never bounces into a stale page.
|
||||
fn restore_page_after_login() {
|
||||
let Some(path) = auth::saved_return_to() else {
|
||||
return;
|
||||
};
|
||||
spawn_local(async move {
|
||||
match api::me().await {
|
||||
Ok(Some(_)) => {
|
||||
auth::clear_return_to();
|
||||
if auth::current_path() != path {
|
||||
navigate(&path);
|
||||
}
|
||||
}
|
||||
// Not logged in: keep the target for the next successful
|
||||
// login (a reload of the login page must not lose it).
|
||||
Ok(None) => {}
|
||||
// The probe failed (offline?): keep the target, too.
|
||||
Err(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
sycamore::render(|| {
|
||||
@@ -38,6 +68,7 @@ fn main() {
|
||||
div(class="app") {
|
||||
(match route.get_clone() {
|
||||
AppRoutes::Lobby => view! { LobbyPage() },
|
||||
AppRoutes::Login => view! { LoginPage() },
|
||||
AppRoutes::Game { id } => view! { GamePage(id=id) },
|
||||
AppRoutes::History => view! { HistoryPage() },
|
||||
AppRoutes::Leaderboard => view! { LeaderboardPage() },
|
||||
@@ -60,4 +91,5 @@ fn main() {
|
||||
)
|
||||
}
|
||||
});
|
||||
restore_page_after_login();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user