From 270b99f0632c44af523ff79ee642072f07f91af8 Mon Sep 17 00:00:00 2001 From: Aarnav Tale Date: Sun, 8 Mar 2026 01:21:11 -0500 Subject: [PATCH] fix: show linked user in onboarding + pending approval screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Onboarding now shows which Headscale user was auto-linked - Members (no ui_access) see a 'Pending Approval' page instead of being silently logged out — their session stays valid - Sign out button on the pending page so users can switch accounts Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-019cce57-c9e1-7732-9709-8288127573a9 --- app/layouts/shell.tsx | 37 ++++++++++++++++++++++++++++----- app/routes/users/onboarding.tsx | 14 ++++++++++++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/app/layouts/shell.tsx b/app/layouts/shell.tsx index eb0c85d..5ec00be 100644 --- a/app/layouts/shell.tsx +++ b/app/layouts/shell.tsx @@ -1,5 +1,7 @@ -import { Outlet, redirect } from "react-router"; +import { Form, Outlet, redirect } from "react-router"; +import Button from "~/components/Button"; +import Card from "~/components/Card"; import Footer from "~/components/Footer"; import Header from "~/components/Header"; import { Capabilities } from "~/server/web/roles"; @@ -25,10 +27,6 @@ export async function loader({ request, context }: Route.LoaderArgs) { const api = context.hsApi.getRuntimeClient(apiKey); const check = context.auth.can(principal, Capabilities.ui_access); - if (!check && principal.kind === "oidc" && !request.url.endsWith("/onboarding")) { - throw new Error("You do not have permission to access the UI"); - } - const user = principal.kind === "oidc" ? { @@ -55,6 +53,7 @@ export async function loader({ request, context }: Route.LoaderArgs) { settings: context.auth.can(principal, Capabilities.read_feature), }, onboarding: request.url.endsWith("/onboarding"), + pendingApproval: !check && principal.kind === "oidc", healthy: await api.isHealthy(), }; } catch { @@ -67,6 +66,34 @@ export async function loader({ request, context }: Route.LoaderArgs) { } export default function Shell({ loaderData }: Route.ComponentProps) { + if (loaderData.pendingApproval && !loaderData.onboarding) { + return ( + <> +
+
+
+ + Pending Approval + + Your account has been created but you don't have access to the UI yet. An + administrator needs to assign you a role before you can continue. + + + If you believe this is a mistake, please contact your administrator. + +
+ +
+
+
+
+