← Back to list

Building Role-Based Login Redirection in a Frappe App

In many ERP systems, redirecting every user to the same page after login creates unnecessary friction.

Nitish · 2026-05-18 13:03 · 0 claps · 3.6 min read
#frappe #erpnext #login #redirect #role-wise
Open on Medium ↗

Building Role-Based Login Redirection in a Frappe App

In many ERP systems, redirecting every user to the same page after login creates unnecessary friction.

A cashier may need the POS screen immediately. A warehouse operator may need stock operations. An accounts user may need financial dashboards.

Frappe normally redirects users to /desk, but in production systems, different roles often require completely different landing experiences.

This article explains how to build a Role Profile–based login redirection system in a Frappe app, including:

  • configurable role-wise landing pages
  • backend redirect APIs
  • frontend login interception
  • automatic /desk link patching
  • dynamic DOM handling
  • third-party app override support

The Problem

By default, Frappe sends all users to:

/desk

This works for generic use cases, but becomes inefficient in operational workflows where users repeatedly navigate to the same module after every login.

Examples:

User TypePreferred Landing PageCashier/posWarehouse User/app/stock-entryAccounts User/app/accountsDelivery Operator/app/delivery-trip

Instead of forcing users to navigate manually, we can automate the experience using Role Profiles.

Configuration Structure

Create a settings doctype such as:

App Settings

Inside it, add a child table:

Role Wise Redirection

Child Table Fields

FieldTypePurposerole_profileLink → Role ProfileRole profile to matchlanding_pathDataRedirect URL

Example Configuration

Role ProfileLanding PathCashier/posManager/app/dashboard-viewInventory User/app/stock-entry

This allows administrators to configure routing without changing code.

Overall Architecture

The flow works in two layers:

  1. Initial login redirection
  2. Post-login navigation patching

The second layer is important because Frappe still contains many internal links pointing to /desk.

Complete Flow

User submits login form
        │
        ▼
HTTP 200 "Logged In"
        │
        ▼
Verify session
        │
        ▼
Show splash screen
        │
        ▼
Call redirect API
        │
        ▼
Fetch user's Role Profile
        │
        ▼
Lookup configured landing path
        │
        ▼
Return redirect URL
        │
        ▼
window.location.href = path

Backend Redirect API

Create a whitelisted API method.

Example

import frappe
@frappe.whitelist()
def get_redirect_url_for_user():
    if frappe.session.user == "Guest":
        return {"redirect": "/login"}
    role_profile = frappe.db.get_value(
        "User Role Profile",
        {"parent": frappe.session.user},
        "role_profile"
    )
    if not role_profile:
        return {"redirect": "/desk"}
    landing_path = frappe.db.get_value(
        "Role Wise Redirection",
        {
            "parent": "App Settings",
            "role_profile": role_profile
        },
        "landing_path"
    )
    return {
        "redirect": landing_path or "/desk"
    }

Backend Logic Breakdown

The API follows a simple sequence.

Step 1 — Prevent Guest Access

if frappe.session.user == "Guest":

Unauthenticated users are redirected to:

/login

Step 2 — Fetch User Role Profile

Query:

User Role Profile

using the current logged-in user.

Step 3 — Match Configuration

Search the child table for:

role_profile = user's role profile

Step 4 — Return Redirect URL

If a mapping exists:

return {"redirect": landing_path}

Otherwise:

/desk

becomes the fallback.

Frontend Login Interception

After login succeeds, call the backend API before redirecting the user.

Example Frontend Logic

function redirect_based_on_role_profile(fallback_url) {
    frappe.call({
        method: "your_app.api.get_redirect_url_for_user",
        callback: function(r) {
            const redirect =
                (r.message && r.message.redirect)
                || fallback_url;
            window.location.href = redirect;
        },
        error: function() {
            window.location.href = fallback_url;
        }
    });
}

Why Frontend Handling Is Needed

Frappe’s default login flow redirects quickly after authentication.

Intercepting the flow allows you to:

  • validate session state
  • show loaders/splash screens
  • fetch role configuration
  • support dynamic routing

before final navigation occurs.

The Hidden Problem: /desk Links

Even after successful role-based redirection, users can still accidentally return to /desk.

Why?

Because Frappe contains many built-in references to /desk.

Examples include:

  • sidebar items
  • breadcrumbs
  • dynamically rendered menu links
  • route handlers

This creates inconsistent navigation behavior.

Solution: Patch Navigation Globally

Include a global JS file using:

app_include_js

inside hooks.py.

Example:

app_include_js = [
    "/assets/your_app/js/role_homes.js"
]

Caching Redirect URLs

Avoid repeated backend calls by caching the redirect path.

Example

var _cached_redirect = null;
function get_cached_redirect(cb) {
    if (_cached_redirect) {
        cb(_cached_redirect);
        return;
    }
    frappe.call({
        method: "your_app.api.get_redirect_url_for_user",
        callback: function(r) {
            _cached_redirect =
                r.message.redirect || "/desk";
            cb(_cached_redirect);
        }
    });
}

Patching /desk Links

Search the DOM for:

<a href="/desk">

and replace them dynamically.

Example

function patch_desk_links() {
    get_cached_redirect(function(path) {
        document.querySelectorAll('a[href="/desk"]')
            .forEach(link => {
                link.href = path;
            });
    });
}

Handling Sidebar Menu Clicks

Some Frappe menu items use:

frappe.set_route()

instead of anchor tags.

Intercept those clicks manually.

Dynamic DOM Rendering Problem

Frappe frequently re-renders parts of the interface dynamically.

That means newly added links may bypass your patches.

MutationObserver Solution

Use MutationObserver to continuously monitor DOM changes.

Example

const observer = new MutationObserver(() => {
    patch_desk_links();
});
observer.observe(document.body, {
    childList: true,
    subtree: true
});

This ensures:

  • newly rendered links are patched
  • sidebar changes remain consistent
  • SPA navigation stays controlled

Supporting Third-Party Overrides

In enterprise apps, another installed app may want full control over landing behavior.

A clean architecture should allow:

  • disabling role-wise redirects
  • hiding settings UI
  • delegating redirect logic externally

This avoids conflicting routing systems.

Benefits of This Architecture

Better User Experience

Users land directly inside their workflow.

Centralized Administration

Admins configure redirects without touching code.

Lower Friction

Operational teams save clicks on every login.

Extensible Design

The same architecture can support:

  • branch-wise routing
  • company-wise redirects
  • shift-based landing pages
  • device-aware routing
  • permission-based workspaces

Recommended File Structure

your_app/
│
├── api/
│   └── login.py
│
├── public/js/
│   └── role_homes.js
│
├── doctype/
│   └── role_wise_redirection/
│
├── hooks.py
│
└── settings/

Final Thoughts

Role-based login redirection in Frappe is more than a simple redirect.

A production-ready implementation requires coordination between:

  • authentication flow
  • frontend routing
  • backend APIs
  • dynamic DOM rendering
  • navigation interception

When implemented correctly, it creates a significantly smoother workflow experience for operational users and makes Frappe applications feel purpose-built for each department instead of generic for everyone.


메타데이터
post_id
e46caf078b45
slug
building-role-based-login-redirection-in-a-frappe-app-e46caf078b45
url
https://medium.com/@devlprnitish/building-role-based-login-redirection-in-a-frappe-app-e46caf078b45
canonical_url
https://medium.com/@devlprnitish/building-role-based-login-redirection-in-a-frappe-app-e46caf078b45
author_url
https://medium.com/@devlprnitish
status
ok
fetched_at
2026-06-09 15:37:30