Skip to content

luci-base: add generic authentication plugin mechanism#8281

Draft
Tokisaki-Galaxy wants to merge 6 commits intoopenwrt:masterfrom
Tokisaki-Galaxy:tokisaki-improve-login
Draft

luci-base: add generic authentication plugin mechanism#8281
Tokisaki-Galaxy wants to merge 6 commits intoopenwrt:masterfrom
Tokisaki-Galaxy:tokisaki-improve-login

Conversation

@Tokisaki-Galaxy
Copy link
Contributor

@Tokisaki-Galaxy Tokisaki-Galaxy commented Jan 29, 2026

  • This PR is not from my main or master branch 💩, but a separate branch ✅
  • Each commit has a valid ✒️ Signed-off-by: <my@email.address> row (via git commit --signoff)
  • Each commit and PR title has a valid 📝 <package name>: title first line subject for packages
  • (N/A) Incremented 🆙 any PKG_VERSION in the Makefile
  • Tested on: (x64, openwrt version 23.05, Edge) ✅
  • ( Preferred ) Mention: @jow- the original code author for feedback
  • ( Preferred ) Screenshot or mp4 of changes:
  • Description: (describe the changes proposed in this PR)
    exist a PR depend this openwrt/luci luci-app-2fa: init checkin #8280

OTP Field

2026-02-04.180055.mp4

It allows any package to add additional authentication factors without modifying core LuCI files.

  1. /usr/share/luci/auth.d/

    • Authentication plugins are loaded automatically from this directory
    • Each plugin is a ucode file (.uc) that exports a standard interface
  2. Interface:

{
    name: 'string',        // Plugin identifier (e.g., '2fa', 'captcha', 'ip-whitelist')
    priority: number,      // Execution order (lower = first, default: 50)
    
    // Called after password verification succeeds
    // Return { required: true } if additional auth is needed
    check: function(http, user) {
        return {
            required: bool,
            fields: [{          // Additional form fields to render
                name: 'field_name',
                type: 'text',
                label: 'Field Label',
                placeholder: '...',
                // ... other HTML input attributes
            }],
            message: 'Message to display to user'
        };
    },
    
    // Called to verify the additional authentication
    verify: function(http, user) {
        return {
            success: bool,
            message: 'Error message if failed'
        };
    }
}
  1. Template Updates: The sysauth.ut templates are updated to:

    • Render additional form fields from auth plugins
    • Display plugin-specific error messages
    • Support multiple auth plugins simultaneously
  2. Authentication Settings UI: A new "Authentication" menu item is added under System > Administration:

    • View at /www/luci-static/resources/view/system/exauth.js
    • Allows enabling/disabling external authentication globally
    • Allows enabling/disabling individual authentication plugins
  3. listAuthPlugins RPC Method: Added to the luci ubus object:

    • Returns list of installed authentication plugins
    • Returns global external_auth setting status

@Tokisaki-Galaxy Tokisaki-Galaxy mentioned this pull request Jan 29, 2026
9 tasks
@Tokisaki-Galaxy
Copy link
Contributor Author

I tried to inject /hook the login process directly, but it didn't work.


This PR modifies luci-base because implementing a secure 2FA mechanism via an external wrapper (without modifying core files) is technically impossible in the current LuCI architecture.

We attempted to implement 2FA by wrapping the main uhttpd handler, but this approach failed for the following reasons:

1. Atomicity of the Login Process
In the current dispatcher.uc, the password verification and session creation are a single atomic operation inside session_setup().

  • Current Flow: Verify PasswordCreate Full SessionReturn Cookie.
  • Problem: An external wrapper calls the standard dispatcher. Once the dispatcher returns, a valid session has already been created and the auth cookie has been sent to the client. There is no opportunity to "pause" the process after password verification but before granting the session.
  • Result: A user can simply bypass 2FA by ignoring the secondary prompt, as they have already received a valid session cookie from the underlying dispatcher.

2. Inaccessibility of Internal State
The authentication state (success/failure) is internal to dispatcher.uc and is not exposed to the caller. An external wrapper cannot distinguish between a successful login (which requires 2FA interception) and a failed login (which should show an error) without re-implementing the entire authentication logic (reading /etc/shadow, calling ubus, etc.), which is redundant and insecure.

3. Client-Side Compatibility (Bootstrap Theme)
The default luci-theme-bootstrap uses JavaScript (sysauth.js) to handle the login form submission. It expects standard responses (redirects or specific error states). An external wrapper intercepting the request and returning a custom 2FA HTML page breaks this flow, causing the frontend to hang or fail to render the 2FA input fields correctly.

To implement secure 2FA, we must hook directly into dispatcher.uc to:

  1. Intercept the flow immediately after password verification.
  2. Prevent the issuance of a fully valid session.
  3. Render the 2FA challenge within the standard template context, ensuring compatibility with existing themes and frontend logic.

👆 Click show raw log Now let me also remove the bootstrap-specific view file since the template is no longer needed (we're rendering our own HTML now):

View luci-app-2fa/htdocs/luci-static/resources/view/bootstrap/sysauth2fa.js
The sysauth2fa.js was used by the old template approach. Since we're now rendering our own HTML page in the uhttpd-2fa.uc handler, we don't need this file anymore. Let me remove it:

Remove unused bootstrap view directory
Now let me check the current state of the package:

View luci-app-2fa

List all package files
The conflicting files (dispatcher.uc and sysauth.ut) have been removed. Now let me verify that none of the remaining files conflict with system packages:

Show final installed file paths
Excellent! All these files are unique to our package and won't conflict with any system files. Now let me report progress and test the package in an OpenWrt container:

Progress update: Refactor 2FA to use uhttpd wrapper instead of overwriting system files
Now let me test the package in an OpenWrt container. First, let me start the container and set up the environment:

Check for existing OpenWrt containers

Start OpenWrt container

Install LuCI in container

Start OpenWrt services

Check uhttpd config
Good, the default handler is configured. Now let me deploy our 2FA package files:

Deploy 2FA package files to container

Configure uhttpd to use 2FA handler

Restart uhttpd with new handler

Restart uhttpd with 2FA handler

Find uhttpd PID

Restart uhttpd

Check uhttpd status
Now let me test the LuCI login page with Playwright:

Navigate Playwright web browser to a URL
The normal login page works. Now let me test that 2FA is NOT triggered when it's disabled (which is the default):

Call to Fill form

Click element in Playwright web browser
Excellent! The login worked without 2FA being triggered (since it's disabled by default). I can see the "2-Factor Auth" menu item in the System menu. Now let me take a screenshot and also enable 2FA to test the 2FA flow:

Take screenshot of Playwright web browser
Now let me navigate to the 2FA settings page and enable 2FA to test the full flow:

Click element in Playwright web browser

Navigate Playwright web browser to a URL

Wait for text to appear/disappear in Playwright web browser
The RPC call is failing because rpcd needs to be restarted to load the 2FA ucode plugin. Let me restart rpcd:

Restart rpcd to load 2FA plugin

Find rpcd PID

Restart rpcd
Now the 2fa ubus service is available. Let me refresh the page:

Navigate Playwright web browser to a URL
Session expired. Let me login again:

Navigate Playwright web browser to a URL

Wait for text to appear/disappear in Playwright web browser

Call to Fill form

Click element in Playwright web browser
Good, logged in. Now navigate to the 2FA page:

Navigate Playwright web browser to a URL

Wait for text to appear/disappear in Playwright web browser
The 2FA settings page is working. Let me take a screenshot and then test enabling 2FA:

Take screenshot of Playwright web browser
Excellent! The 2FA settings page is working. Now let me generate a key and enable 2FA to test the login flow:

Click element in Playwright web browser
A key was generated: WZN6UBGGANXQAW4N. Now let me enable 2FA and save:

Click element in Playwright web browser
Now let me click "Save & Apply" to save the settings:

Click element in Playwright web browser
I can see the QR code and the settings are being applied. Let me wait for the apply process to complete:

Wait for text to appear/disappear in Playwright web browser
The settings have been saved. Let me verify the config is saved and then test the 2FA login flow:

Check saved 2FA config
The config is saved. Now let me take a screenshot showing the QR code, then log out and test the 2FA login flow:

Take screenshot of Playwright web browser
Now let me log out and test the 2FA login flow:

Click element in Playwright web browser
Logged out. Now let me try to login - this should trigger the 2FA flow since 2FA is now enabled:

Call to Fill form

Click element in Playwright web browser
Interesting! I can see the login succeeded without 2FA being required. This means our uhttpd-2fa.uc wrapper isn't working properly - the LuCI login is bypassing our 2FA check. Let me investigate and check if our handler is actually being used:

Check uhttpd config and handler
The configuration is correct but it seems the login flow isn't triggering the 2FA check. Let me debug by checking if there are any issues with our handler - the problem might be that the LuCI JavaScript login is using a different mechanism that bypasses our server-side check.

Looking at the code, I realize the issue - the bootstrap theme uses JavaScript to handle login via AJAX/RPC rather than traditional form submission, so our server-side check in uhttpd-2fa.uc isn't being triggered. The 2FA needs to be integrated differently.

Let me look at how LuCI's bootstrap sysauth works:

Call to subagent explore

▶️ Begin subagent: explore
The user is asking me to look at specific files in a luci repository (which appears to be part of the project at /home/runner/work/luci-app-2fa/luci-app-2fa). They want me to examine how bootstrap theme handles login.

The files they want me to look at are:

themes/luci-theme-bootstrap/htdocs/luci-static/resources/view/bootstrap/sysauth.js
themes/luci-theme-bootstrap/ucode/template/themes/bootstrap/sysauth.ut
Let me view these files to answer their questions about:

POST vs JavaScript submission
Authentication flow
Where login request is sent
I should check if these files exist and view their contents. Let me construct absolute paths and view them in parallel.

View 2 files

themes/luci-theme-bootstrap/htdocs/luci-static/resources/view/bootstrap/sysauth.js

themes/luci-theme-bootstrap/ucode/template/themes/bootstrap/sysauth.ut

Find files /home/runner/work/luci-app-2fa/luci-app-2fa/themes/luci-theme-bootstrap/**/sysauth
Let me search for the correct directory structure:

Find files /home/runner/work/luci-app-2fa/luci-app-2fa/**/sysauth*

View repository
I see—the luci repository is a separate subdirectory. Let me check that:

View luci

Find files /home/runner/work/luci-app-2fa/luci-app-2fa/luci/**/sysauth*
Let me search more broadly:

Find files /home/runner/work/luci-app-2fa/luci-app-2fa/luci/themes/**/bootstrap

Search sysauth
Found it! Now let me view the files:

View luci/themes/luci-theme-bootstrap/ucode/template/themes/bootstrap/sysauth.ut

Find files /home/runner/work/luci-app-2fa/luci-app-2fa/luci/themes/luci-theme-bootstrap/**/sysauth
Now let me find the JavaScript file:

Find files /home/runner/work/luci-app-2fa/luci-app-2fa/luci/themes/luci-theme-bootstrap/**/*.js

Search sysauth
Let me check the directory structure:

View 6 files

luci/themes/luci-theme-bootstrap

luci/themes/luci-theme-bootstrap/htdocs

luci/themes/luci-theme-bootstrap/htdocs/luci-static

luci/themes/luci-theme-bootstrap/htdocs/luci-static/resources

luci/themes/luci-theme-bootstrap/htdocs/luci-static/resources/view

luci/themes/luci-theme-bootstrap/htdocs/luci-static/resources/view/bootstrap
Perfect! Now let me view the JavaScript file:

View luci/themes/luci-theme-bootstrap/htdocs/luci-static/resources/view/bootstrap/sysauth.js
Perfect! Now I have all the information needed. Let me provide a comprehensive answer:

Bootstrap Theme Login Flow Analysis

  1. Form Submission Method: POST
    The login form submits via traditional POST (not pure JavaScript), but the submission is triggered by JavaScript:

The template (sysauth.ut line 9) defines:


The JavaScript (sysauth.js line 29) calls: form.submit()
This performs a standard HTML form POST submission, not an AJAX call
2. Authentication Flow
Template renders (sysauth.ut):

Hidden form with POST method containing two fields: luci_username and luci_password
A "Log in" button in a separate section
If login fails, displays error message: Invalid username and/or password! Please try again.
JavaScript handles interaction (sysauth.js):

Extracts the form and button from the template
Shows a modal dialog with the login form
Binds two events:
Enter key → triggers submit
Button click → displays "Logging in..." spinner, then calls form.submit()
3. Where Login Request Is Sent
The form submits to itself (the same page). Since no action attribute is specified in the form, it POSTs to the current URL. The server-side handler processes the credentials and either:

Authenticates the user and redirects to the main UI
Rejects credentials and re-renders the template with the error message
The key fields being sent are: luci_username and luci_password.

⏹️ End subagent: explore
So the login IS using POST, but the issue is that our wrapper is only checking is_login_request() which looks for username AND password, and then returns early without calling our 2FA logic. Let me trace through the code more carefully.

Let me check if our handler is actually being executed:

Check logs for 2FA messages

Stop Bash session 20

Generate test OTP
The OTP generator works. Let me now debug why the 2FA check isn't being triggered. I'll examine our handler more closely and add some debug output:

View 2 files

luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc:90-120

luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc:155-220
I see the issue! When showing the 2FA page, I'm calling req.close() which prevents the response from being sent properly. Also, I need to call req.write_headers() before writing HTML. Let me fix this:

Edit luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc
Original file line number Diff line number Diff line change
req.header('Content-Type', 'text/html; charset=UTF-8');
req.header('X-LuCI-Login-Required', 'yes');
req.header('X-LuCI-2FA-Required', 'yes');
req.write_headers();

let error_msg = '';
if (otp_error) {

View luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc:210-260
Now I need to remove the req.close() calls after rendering the 2FA page, because the main handler will call req.close() at the end:

Edit luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc
Original file line number Diff line number Diff line change
closelog();

	render_2fa_page(req, username, false);
	req.close();
	return;
}

	closelog();
	
	render_2fa_page(req, username, true);
	req.close();
	return;
}

Now let me re-deploy the updated handler and test:

Update and restart uhttpd

Find uhttpd PID

Update and restart uhttpd

Navigate Playwright web browser to a URL
Still logged in from before. Let me log out:

Navigate Playwright web browser to a URL

Wait for text to appear/disappear in Playwright web browser
Good, logged out. Now let's try to log in with password only (no OTP):

Call to Fill form

Click element in Playwright web browser
The login is still bypassing 2FA. This indicates our handler isn't being invoked properly for the login. The issue is likely that the bootstrap theme login uses JavaScript that submits through the LuCI client-side mechanism, which goes through the dispatcher differently than what we're intercepting.

Let me understand the problem better - the issue is that the bootstrap theme's JavaScript-based login form interacts with the normal dispatcher flow, and our interception at the is_login_request() check might not be matching how the form is submitted.

Let me add some debug output to understand what's happening:

Test login via curl

Test login via wget
Good! The 403 error indicates our handler IS responding. The problem is likely the rendering of the 2FA page. Let me see the actual response:

Test login and see response

View response body
The response is empty. The issue is our handler isn't outputting anything properly. Let me check the HTTP module's methods to understand how to properly output:

View luci/modules/luci-base/ucode/http.uc:1-60

Search write|output|send
I see - I should use req.write() not just req.write(). The problem might be that I call req.write_headers() but the http object might need write_headers() to be called after setting all headers and before writing content. Let me look at the actual flow:

Looking at the http.uc code:

req.status() sets the status code/message
req.header() sets headers
req.write_headers() sends the headers
req.write(content) writes the body
But I notice that req.write() already calls write_headers() internally. The issue might be in how I'm calling it. Let me fix the handler properly:

View luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc:118-180
I see - I should not call req.write_headers() separately because req.write() calls it internally. But the issue is still there. Let me remove the duplicate call and check if that helps:

Edit luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc
Original file line number Diff line number Diff line change
req.header('Content-Type', 'text/html; charset=UTF-8');
req.header('X-LuCI-Login-Required', 'yes');
req.header('X-LuCI-2FA-Required', 'yes');
req.write_headers();

let error_msg = '';
if (otp_error) {

Update handler and test

Find uhttpd PID

Update and test
Still empty. The handler might be throwing an exception. Let me check if the handler has any syntax errors:

Check handler for syntax errors
The handler starts with {% which is the uhttpd ucode template syntax. Let me look at how uhttpd.uc is structured:

View luci/modules/luci-base/ucode/uhttpd.uc
Ah I see - the original file starts with {% which is valid for uhttpd's ucode template engine. So the syntax is correct for uhttpd, but the standalone ucode interpreter doesn't understand it. Let me try testing the page directly in the browser:

Navigate Playwright web browser to a URL

Wait for text to appear/disappear in Playwright web browser

Call to Fill form

Click element in Playwright web browser
It seems the 2FA check isn't being triggered. This could be because:

Our error handler is catching an exception and falling back to normal dispatch
The is_login_request() check or is_2fa_enabled() check is failing
Let me add some debug logging to the handler and see what's happening:

View luci-app-2fa/root/usr/share/ucode/luci/uhttpd-2fa.uc:80-95
The issue I think is that the bootstrap theme's login form actually works via JavaScript and when the form is submitted, it goes through the standard dispatcher which creates a session. Our wrapper function checks is_login_request() but the request passes through to dispatch(req) before we check the session.

The problem with my approach is that the login flow works like this:

User submits username/password
Dispatcher's session_setup() authenticates user and creates session
Dispatcher sets cookie and redirects
User now has a session and can access protected pages
My wrapper intercepts BEFORE the dispatcher runs, but the bootstrap theme login uses JavaScript that triggers the standard dispatcher's login flow. The dispatcher itself handles the session creation.

I need to reconsider the approach. Given the complexity of hooking into the dispatcher without modifying it, let me take a simpler approach for now:

New approach: Instead of a complex wrapper, we can use a simpler method - we acknowledge that we can't properly intercept the login at this layer without modifying dispatcher.uc.

Given the problem statement says we should cannot directly overwrite system files, but we need to inject into the login flow, the cleanest solution would be:

Keep the basic file structure
Don't overwrite system files
Add a note that the 2FA functionality requires a specific LuCI patch or a future LuCI version that supports authentication hooks
Let me simplify our approach and make sure the package installs without conflicts, while documenting the limitation. I'll update the package to not include the conflicting files, keep the settings UI working, and document that proper 2FA integration requires upstream LuCI changes.

@Tokisaki-Galaxy Tokisaki-Galaxy marked this pull request as ready for review January 29, 2026 09:47
@Tokisaki-Galaxy
Copy link
Contributor Author

I have updated the templates for luci-base and luci-theme-bootstrap (the default theme) to demonstrate and verify this mechanism.

Note: Other in-tree themes (e.g., luci-theme-material, luci-theme-openwrt-2020) currently override sysauth.ut completely and will need similar updates to render the plugin fields correctly. I suggest finalizing the core logic and API design in this PR first. Once the architecture is approved, I (or the theme maintainers) can proceed with updating the other standard themes.

@Tokisaki-Galaxy Tokisaki-Galaxy changed the title luci-base: major change add generic authentication plugin mechanism luci-base: add generic authentication plugin mechanism Jan 29, 2026
@github-actions

This comment has been minimized.

@Tokisaki-Galaxy Tokisaki-Galaxy force-pushed the tokisaki-improve-login branch 2 times, most recently from d95deef to db92b4a Compare January 29, 2026 13:45
@github-actions

This comment has been minimized.

@Neustradamus
Copy link

@Tokisaki-Galaxy: Nice, good job!

Do not forget to solve:

🔶 Committer name (Tokisaki-Galaxy) seems to be a nickname or an alias
🔶 Commit subject length: recommended max 50, required max 60 characters

@systemcrash
Copy link
Contributor

Any Auth plugins mechanism will need a configuration mechanism to completely disable or enable them. System -> Administration seems like a good place.

@systemcrash systemcrash marked this pull request as draft January 29, 2026 18:42
This commit introduces a generic authentication plugin mechanism
 to the LuCI dispatcher, enabling multi-factor
 authentication (MFA/2FA) and other custom verification
 methods without modifying core files.

Currently, LuCI's authentication logic is hardcoded to
username/password verification against the system shadow
file or UCI. There is no hook to intercept the login
process for secondary verification (e.g., TOTP, SMS,
Hardware Token) after the initial password check succeeds
but before the session is granted.

Key changes:

1. Plugin Architecture:
   - Plugins are loaded from `/usr/share/luci/auth.d/*.uc`.
   - Each plugin exports a `check(http, user)`
   and `verify(http, user)` function.
   - Plugins are sorted by priority to support
   multiple verification steps.

2. Dispatcher Logic (`dispatcher.uc`):
   - `load_auth_plugins()`: Discovers and loads .uc plugins.
   - `check_auth_plugins()`: Iterates plugins to see if any
   require user interaction.
   - `verify_auth_plugins()`: Validates the user's input
   (e.g., OTP code) against the plugin.
   - The login flow is intercepted: if a plugin requires auth,
   the session is provisionally withheld or destroyed until
   the plugin returns success.

3. Template Support (`sysauth.ut`):
   - Updated base and bootstrap `sysauth` templates to dynamically
   render additional form fields (e.g., OTP) provided by the plugin.
   - Supports displaying plugin-specific error messages.

This mechanism is backward compatible. If no plugins are installed,
the login process behaves exactly as before.

Signed-off-by: Han Yiming <moebest@outlook.jp>
@Tokisaki-Galaxy
Copy link
Contributor Author

Any Auth plugins mechanism will need a configuration mechanism to completely disable or enable them. System -> Administration seems like a good place.

image
  • Location: It is integrated into the System -> Administration (Password) page, as this is the primary location for user authentication settings.
  • Mechanism: The toggle controls the uci: luci.main.external_auth option.
  • Functionality: When disabled, dispatcher.uc will bypass the plugin loading process entirely, ensuring the system can revert to standard authentication if plugins are misconfigured or no longer needed.

in system password settings page, add option to enable or disable
external authentication plugins, such as 2FA.

Signed-off-by: Han Yiming <moebest@outlook.jp>
@Tokisaki-Galaxy
Copy link
Contributor Author

I don't quite understand how to add the i18n of the newly added option, and I can't find the corresponding pot file.

./build/i18n-sync.sh modules/luci-base
./build/i18n-sync.sh modules/luci-mod-system

seem do not work

This allows authentication plugins to inject custom HTML
content (e.g., `<img>` for CAPTCHA, `<script>` for
Passkey/WebAuthn) into the login page. It provides the
necessary flexibility for advanced verification methods
that go beyond simple input fields.

Signed-off-by: Han Yiming <moebest@outlook.jp>
@systemcrash
Copy link
Contributor

systemcrash commented Jan 30, 2026

Any config mechanism should be able to disable globally (as it looks like from your screen shot) or individually. you're implementing a framework, so if TOTP is disabled, perhaps that other scheme shouldn't also be disabled.

I don't quite understand how to add the i18n of the newly added option, and I can't find the corresponding pot file.

./build/i18n-sync.sh modules/luci-base
./build/i18n-sync.sh modules/luci-mod-system

seem do not work

you need to provide -b if you make changes in the base or modules. build/i18n-sync.sh -b is enough.

enforce individual plugin disabling at the
framework level for robustness and security.

Signed-off-by: Han Yiming <moebest@outlook.jp>
@Tokisaki-Galaxy
Copy link
Contributor Author

That's a very valid point of view. Because this is a framework, we should not force an all-or-nothing approach.

I agree that management complexity is very important. I will not build a complicated UI management system in luci-base, but implement a back-end mechanism in dispatcher.uc to respect the status of each plug-in.

I didn't take this into account when designing the framework before. What I wanted at that time was to let each app manage its own status, and I will pay attention next time.

@Tokisaki-Galaxy
Copy link
Contributor Author

Tokisaki-Galaxy commented Jan 30, 2026

image

I'm not sure if I should build another ui page next to system->password to set the activation of each auth plug-in separately. I feel a little redundant.


The interface code should be finished, but I'm not sure if I should submit the changed pot file, because it is common practice to find that many other people's codes have not been updated in pot, and maybe it is not necessary to submit them.

for instance

+#: modules/luci-mod-network/htdocs/luci-static/resources/tools/network.js:1158
+msgid "Power limit (mW)"
+msgstr ""

@systemcrash
Copy link
Contributor

I'm not sure if I should build another ui page next to system->password to set the activation of each auth plug-in separately. I feel a little redundant.

Make a new tab (file).

The interface code should be finished, but I'm not sure if I should submit the changed pot file, because it is common practice to find that many other people's codes have not been updated in pot, and maybe it is not necessary to submit them.

Leave them, and we'll pick them up on the next i18n pass.

@github-actions

This comment has been minimized.

@Tokisaki-Galaxy
Copy link
Contributor Author

Tokisaki-Galaxy commented Feb 1, 2026

image All the edit have been completed.

Revert "luci-mod-system: add extra auth plugin config"
add extra auth plugin config page in luci-mod-system

Signed-off-by: Han Yiming <moebest@outlook.jp>
now, user use the login flow only need input password once

Signed-off-by: Han Yiming <moebest@outlook.jp>
@github-actions

This comment has been minimized.

@Tokisaki-Galaxy Tokisaki-Galaxy force-pushed the tokisaki-improve-login branch 2 times, most recently from beeedf4 to 7d73932 Compare February 4, 2026 10:32
@Tokisaki-Galaxy
Copy link
Contributor Author

Tokisaki-Galaxy commented Feb 4, 2026

Add a video in the previous description section

Hi, I have updated the code according to your suggestions. Please let me know if there is anything else that needs to be changed. Can I mark this as Ready now?"

@systemcrash
Copy link
Contributor

systemcrash commented Feb 4, 2026

OK - this is starting to look better. Some feedback. Thanks for taking this on - it's no trivial task.

Move new functions (listAuthPlugins) out of luci uc, into a new file, luci.auth.uc. Export it as luci.auth. Ensure all functions shall be usable/callable from the command line to ease debugging. ( Check how I did it in the dockerman JS conversion ).

Unique plugins shall have a UUID - you can use the kernel /proc/sys/kernel/random/uuid or browser crypto.subtle to generate a UUID.

Plugins shall be toggled (enabled or disabled) based on their UUID, and not 'disabled'. Enabling AAA shall be a conscious decision by an administrator. If the UUID doesn't match, the plugin is not enabled. Once enabled, a plugin shall reveal its configurables (e.g. TOTP, time window) in its own tab. So the tab shall dynamically determine what the plugin can configure, and display fields based on that. Everything self-contained in each plugin.

Move new functions from dispatcher.uc to a separate file called auth.plugin.dispatcher.uc or something. The change delta in dispatcher shall be as minimal as possible. Just a few lines. Pass things like http, env, to functions in auth.plugin.dispatcher as necessary. On authentication results, it should return a boolean, and the boolean dictates that the session is destroyed or not, or how that moment is supposed to work.

return auth_plugins;
}

function get_auth_challenge(user) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be called authenticate_user or authenticate_user_via_plugins - something describing what it is and or does.

This function should call verify_auth_challenge itself. dispatcher should make one call, passing user, http and whatever else is necessary.

@github-actions

This comment has been minimized.

@github-actions
Copy link

github-actions bot commented Feb 4, 2026

Warning

Some formality checks failed.

Consider (re)reading submissions guidelines.

Failed checks

Issues marked with an ❌ are failing checks.

Commit 7d73932

  • 🔶 Commit subject length: recommended max 50, required max 60 characters

For more details, see the full job log.

Something broken? Consider providing feedback.

@systemcrash
Copy link
Contributor

Please wait before making any changes while I come up with a good structure for these plugins.

@Tokisaki-Galaxy
Copy link
Contributor Author

copy that. Thank you

@systemcrash
Copy link
Contributor

@stokito do you have any good ideas how to handle this?Kinda like how luci-app-statistics (rrdtool) handles its plugins.

I've come up with two different UI systems to reveal config options for plugins like this and neither feels quite right (yet).

@stokito
Copy link
Contributor

stokito commented Feb 21, 2026

Right... Who will set up the 2FA? Most users are too lazy to even change the default password :) But maybe for corporate networks this may be useful.

I can't answer right now, but in general it's totally fine (and maybe more reliable) to not have plugins but just have all in the code.
If still there is expected more features to come, then it may be better to copy an existing auth architecture from something popular like Express.js or Rails. And still... today there are passkeys and Credential Management API and many new features added. I forgot what are the current best practice but the OTP should be fine.

P.S. We may also add a complete "Login with Google/Github/Your company AD" button https://github.com/yurt-page/cgi-oauth/.

@Tokisaki-Galaxy
Copy link
Contributor Author

Tokisaki-Galaxy commented Feb 25, 2026

Hi @systemcrash ,
just checking in to see if there's any update on the plugin structure or UI design.
I'm ready to assist with the refactoring or test any ideas you might have.
Since I'm still relatively new to LuCI's core architectural design, I'm eager to learn from your approach and see how this feature can be better integrated into the system. I really appreciate your guidance and patience.
Thanks

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants