Skip to content

Feature: allow the built-in login page to be replaced from the custom directory #723

Description

@shuans

Summary

Please allow the built-in login page to be replaced by a custom Vue component from the custom/ directory, the same way customization.customPages lets you add pages.

Current behavior

/login is hardcoded in the SPA router (spa/src/router/index.ts) and points at @/views/LoginView.vue. No configuration option changes it. The only login-page extension points are loginPageInjections (panelHeader / underInputs / underLoginButton), loginBackgroundImage, loginBackgroundPosition, loginPromptHTML and demoCredentials — all of which add to the built-in page rather than replacing it.

Adding a customPages entry with path: '/login' does not work. I checked why, against 3.29.0 and vue-router 4.3.0 / 4.5.1 / 4.6.4 — there are four independent blockers:

  1. customPages routes are emitted at the /* IMPORTANT:ADMINFORTH ROUTES */ placeholder in spa/src/router/index.ts, which sits after the built-in routes. The built-in /login is therefore registered first.
  2. vue-router 4 keeps the first matcher for a duplicate path. resolve('/login') returns the built-in component and the custom one is unreachable — I verified this on all three vue-router versions above, with the routes in both orders.
  3. Redirects target the route name: router.push({ name: 'login' }) in App.vue and in spa/src/utils/utils.ts. registerCustomPages names a custom page name: '<path>', i.e. '/login', so name: 'login' never matches it.
  4. There is no config key for a login component.

Why this matters

The login page is the first screen every user sees and the one teams most often want to restructure: split/centered layouts, tenant or workspace selection, SSO and passkey buttons, a language switcher, custom copy, marketing or legal panels next to the form. loginPageInjections covers three insertion points, so anything structural — replacing the heading and the inputs, two-column layouts, a different field order — is out of reach.

The only alternative today is patching AdminForth's own spa/src/views/LoginView.vue and spa/src/router/index.ts. That is exactly the maintenance burden teams try to avoid: the patch is keyed to an exact package version, so it silently stops applying on the next release, and the failure mode is a login page that quietly reverts to the stock one.

Custom pages already prove the mechanism works. The login page is the notable exception.

Proposed API

Any of these would solve it. The first is the smallest:

customization: {
  // a component in ./custom that replaces views/LoginView.vue
  loginPage: '@@/MyLogin.vue',
}

or a customPages-shaped declaration, so meta comes along:

customization: {
  loginPage: {
    component: { file: '@@/MyLogin.vue', meta: { sidebarAndHeader: 'none' } },
  },
}

Requirements:

  • when unset, behaviour is unchanged;
  • the route keeps path: '/login' and name: 'login', so every router.push({ name: 'login' }), the existing beforeEnter guard and the 401 redirect keep working;
  • no new plumbing needed — custom/ is already aliased to @@ and copied into the SPA sources, so this can reuse whatever customPages uses to emit component: () => import('...');
  • please document the contract a custom login page has to implement (at minimum POST /login with { username, password, rememberMe } and what to do with the response), or export the built-in LoginView.vue so it can be wrapped and extended instead of reimplemented from scratch — the latter would make this far less risky for anyone who still wants the stock flow plus their own layout;
  • keep loginPageInjections, loginBackgroundImage, loginBackgroundPosition and loginPromptHTML working, so projects that only need the small hooks are unaffected.

Workaround today

Patching the route in spa/src/router/index.ts:

-      component: () => import('@/views/LoginView.vue'),
+      component: () => import('@@/MyLogin.vue'),

This does work — same path, same name: 'login', and @@ resolves to src/custom via the vite alias in spa/vite.config.ts. But it is a patch against the installed package, which is the whole problem.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions