fix: Bug - No login screen mobile phone on startup
Signed-out mobile visitors landing on / or /dashboard were intentionally kept on a logged-out /dashboard, so they were never greeted with a login screen on startup. Replace the imperative startup-redirect logic in App.ngOnInit with a platform-agnostic pure rule resolveUnauthenticatedStartupRedirect: non-public routes redirect to /login (with a safe returnUrl), public routes (/login, /register, /invite/...) are left alone. Mobile is no longer special-cased. - Unit: auth-navigation.rules.spec.ts - E2E: e2e/tests/mobile/mobile-login-on-startup.spec.ts (mobile viewport set before navigation; /dashboard and / both land on /login) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { UsersActions } from '../../../../store/users/users.actions';
|
||||
import {
|
||||
buildLoginReturnQueryParams,
|
||||
resolveSafeReturnUrl,
|
||||
resolveUnauthenticatedStartupRedirect,
|
||||
waitForAuthenticationOutcome
|
||||
} from './auth-navigation.rules';
|
||||
|
||||
@@ -55,6 +56,39 @@ describe('buildLoginReturnQueryParams', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveUnauthenticatedStartupRedirect', () => {
|
||||
it('sends signed-out visitors on the dashboard/root to login (no mobile exception)', () => {
|
||||
expect(resolveUnauthenticatedStartupRedirect('/dashboard')).toEqual({
|
||||
path: '/login',
|
||||
queryParams: {}
|
||||
});
|
||||
|
||||
expect(resolveUnauthenticatedStartupRedirect('/')).toEqual({
|
||||
path: '/login',
|
||||
queryParams: { returnUrl: '/' }
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves a safe returnUrl when redirecting from a protected route', () => {
|
||||
expect(resolveUnauthenticatedStartupRedirect('/servers')).toEqual({
|
||||
path: '/login',
|
||||
queryParams: { returnUrl: '/servers' }
|
||||
});
|
||||
|
||||
expect(resolveUnauthenticatedStartupRedirect('/room/abc')).toEqual({
|
||||
path: '/login',
|
||||
queryParams: { returnUrl: '/room/abc' }
|
||||
});
|
||||
});
|
||||
|
||||
it('leaves public auth/invite routes untouched', () => {
|
||||
expect(resolveUnauthenticatedStartupRedirect('/login')).toBeNull();
|
||||
expect(resolveUnauthenticatedStartupRedirect('/login?returnUrl=%2Fservers')).toBeNull();
|
||||
expect(resolveUnauthenticatedStartupRedirect('/register')).toBeNull();
|
||||
expect(resolveUnauthenticatedStartupRedirect('/invite/abc123')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('waitForAuthenticationOutcome', () => {
|
||||
it('resolves when authentication storage preparation succeeds', async () => {
|
||||
const user = {
|
||||
|
||||
@@ -18,10 +18,41 @@ export type AuthenticationOutcome =
|
||||
| { kind: 'success'; user: User }
|
||||
| { kind: 'failure'; error: string };
|
||||
|
||||
export interface UnauthenticatedStartupRedirect {
|
||||
path: string;
|
||||
queryParams: Record<string, string>;
|
||||
}
|
||||
|
||||
export function isAuthRoutePath(path: string): boolean {
|
||||
return AUTH_ROUTE_PATHS.has(path);
|
||||
}
|
||||
|
||||
/** Routes a signed-out visitor may stay on without being bounced to login. */
|
||||
export function isPublicStartupUrl(url: string): boolean {
|
||||
const path = getRoutePathFromUrl(url);
|
||||
|
||||
return isAuthRoutePath(path) || path.startsWith('/invite/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides where an unauthenticated visitor should land at startup. Signed-out
|
||||
* users are always sent to `/login` (regardless of viewport) so mobile users are
|
||||
* greeted with the login screen instead of a logged-out dashboard. Public routes
|
||||
* (`/login`, `/register`, `/invite/...`) are left untouched (return `null`).
|
||||
*/
|
||||
export function resolveUnauthenticatedStartupRedirect(
|
||||
currentUrl: string
|
||||
): UnauthenticatedStartupRedirect | null {
|
||||
if (isPublicStartupUrl(currentUrl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
path: '/login',
|
||||
queryParams: buildLoginReturnQueryParams(currentUrl)
|
||||
};
|
||||
}
|
||||
|
||||
export function getRoutePathFromUrl(url: string): string {
|
||||
if (!url) {
|
||||
return '/';
|
||||
|
||||
Reference in New Issue
Block a user