mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-15 23:32:46 +00:00
Fix login route collision after SPA cutover
Move the browser sign-in page off the backend /auth namespace so direct navigation works, update Rust and frontend redirects to /login, and add tests that lock the split between SPA login UI and backend OAuth endpoints.
This commit is contained in:
parent
eb91f2bcab
commit
e8a7bd757e
11 changed files with 71 additions and 18 deletions
|
|
@ -12,7 +12,7 @@ export async function apiFetch(path: string, options?: ApiOptions): Promise<Resp
|
|||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
window.location.href = "/auth/login";
|
||||
window.location.href = "/login";
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
|
|
|
|||
28
apps/fabro-web/app/router.test.tsx
Normal file
28
apps/fabro-web/app/router.test.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { describe, expect, test } from "bun:test";
|
||||
import type { RouteObject } from "react-router";
|
||||
|
||||
import { routes } from "./router";
|
||||
|
||||
function collectPaths(routeObjects: RouteObject[], prefix = ""): string[] {
|
||||
return routeObjects.flatMap((route) => {
|
||||
const path = route.index
|
||||
? prefix || "/"
|
||||
: route.path
|
||||
? route.path.startsWith("/")
|
||||
? route.path
|
||||
: `${prefix}/${route.path}`.replace(/\/+/g, "/")
|
||||
: prefix;
|
||||
|
||||
const childPaths = route.children ? collectPaths(route.children, path) : [];
|
||||
return route.path || route.index ? [path, ...childPaths] : childPaths;
|
||||
});
|
||||
}
|
||||
|
||||
describe("browser router", () => {
|
||||
test("uses /login for the sign-in page instead of the backend auth namespace", () => {
|
||||
const paths = collectPaths(routes);
|
||||
|
||||
expect(paths).toContain("/login");
|
||||
expect(paths).not.toContain("/auth/login");
|
||||
});
|
||||
});
|
||||
|
|
@ -90,7 +90,7 @@ export const routes: RouteObject[] = [
|
|||
indexRoute(RedirectHome),
|
||||
route("setup", Setup),
|
||||
route("setup/complete", SetupComplete),
|
||||
route("auth/login", AuthLogin),
|
||||
route("login", AuthLogin),
|
||||
{
|
||||
loader: appShellLoader,
|
||||
Component: withRouteModule({
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export async function loader() {
|
|||
await getAuthMe();
|
||||
} catch (error) {
|
||||
if (error instanceof Response && error.status === 401) {
|
||||
return redirect("/auth/login");
|
||||
return redirect("/login");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ export default function SetupComplete() {
|
|||
Authentication is ready.
|
||||
</p>
|
||||
<a
|
||||
href="/auth/login"
|
||||
href="/login"
|
||||
className="mt-6 flex w-full items-center justify-center rounded-lg bg-teal-500 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-teal-300"
|
||||
>
|
||||
Continue to sign in
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
apps/fabro-web/dist/index.html
vendored
2
apps/fabro-web/dist/index.html
vendored
|
|
@ -61,7 +61,7 @@
|
|||
<script type="module" src="/assets/chunk-sadshphz.js"></script>
|
||||
<script type="module" src="/assets/chunk-pmthkscp.js"></script>
|
||||
<script type="module" src="/assets/chunk-v61ks9f7.js"></script>
|
||||
<script type="module" src="/assets/entry-qx60vskn.js"></script>
|
||||
<script type="module" src="/assets/entry-acc7dzjg.js"></script>
|
||||
<script type="module" src="/assets/chunk-n1k68xa8.js"></script>
|
||||
<script type="module" src="/assets/chunk-rsph5pvm.js"></script>
|
||||
<script type="module" src="/assets/chunk-9t57pdty.js"></script>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"app/**/*",
|
||||
"scripts/**/*"
|
||||
],
|
||||
"exclude": ["**/*.test.ts"],
|
||||
"exclude": ["**/*.test.ts", "**/*.test.tsx"],
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"types": ["node"],
|
||||
|
|
|
|||
|
|
@ -1758,6 +1758,31 @@ mod tests {
|
|||
assert!(location.starts_with("https://github.com/login/oauth/authorize?"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn logout_redirects_to_login_page() {
|
||||
let app = test_app_with(test_db().await);
|
||||
|
||||
let response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/auth/logout")
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status(), StatusCode::SEE_OTHER);
|
||||
assert_eq!(
|
||||
response
|
||||
.headers()
|
||||
.get(axum::http::header::LOCATION)
|
||||
.and_then(|value| value.to_str().ok()),
|
||||
Some("/login")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn static_favicon_is_served() {
|
||||
let app = test_app_with(test_db().await);
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ async fn callback_github(
|
|||
let cookie_jar = parse_cookie_header(&headers);
|
||||
let stored_state = cookie_jar.get(OAUTH_STATE_COOKIE_NAME).map(|cookie| cookie.value());
|
||||
if stored_state != Some(params.state.as_str()) {
|
||||
return Redirect::to("/auth/login").into_response();
|
||||
return Redirect::to("/login").into_response();
|
||||
}
|
||||
|
||||
let Some(client_id) = settings.client_id().map(str::to_string) else {
|
||||
|
|
@ -322,7 +322,7 @@ async fn callback_github(
|
|||
.map(|web| web.auth.allowed_usernames.clone())
|
||||
.unwrap_or_default();
|
||||
if !allowed_usernames.is_empty() && !allowed_usernames.iter().any(|user| user == &profile.login) {
|
||||
return Redirect::to("/auth/login?error=unauthorized").into_response();
|
||||
return Redirect::to("/login?error=unauthorized").into_response();
|
||||
}
|
||||
|
||||
let primary_email = emails
|
||||
|
|
@ -374,7 +374,7 @@ async fn logout(State(state): State<Arc<AppState>>) -> Response {
|
|||
.build(),
|
||||
);
|
||||
}
|
||||
let mut response = Redirect::to("/auth/login").into_response();
|
||||
let mut response = Redirect::to("/login").into_response();
|
||||
append_jar_delta(response.headers_mut(), &jar);
|
||||
response
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue