[Bug]: header auth prompting for a password [v25.2.1] #1907

Open
opened 2026-02-28 19:58:09 -06:00 by GiteaMirror · 7 comments
Owner

Originally created by @aroberts on GitHub (Mar 1, 2025).

Verified issue does not already exist?

  • I have searched and found no existing issue

What happened?

I've gotten actual running with header auth, but I'm still seeing the password prompt on app load. Diving through the issues, it looks like this should not be happening (#2781). Is this a regression, potentially related to the recent work with multi-user, or is there something I am not configuring right?
if I type anything into the password box, it takes me to the same data, and the logs indicate it's accepting my header login.

relevant config:

    environment:
      ACTUAL_LOGIN_METHOD: header
      ACTUAL_ALLOWED_LOGIN_METHODS: header
      DEBUG: "actual:config,actual-sensitive:config"

is there anything obvious missing?

How can we reproduce the issue?

Launch actual with header auth configured; visit any actual page

Where are you hosting Actual?

docker

What browsers are you seeing the problem on?

Safari, Firefox

Operating System

None

Originally created by @aroberts on GitHub (Mar 1, 2025). ### Verified issue does not already exist? - [x] I have searched and found no existing issue ### What happened? I've gotten actual running with header auth, but I'm still seeing the password prompt on app load. Diving through the issues, it looks like this should not be happening (#2781). Is this a regression, potentially related to the recent work with multi-user, or is there something I am not configuring right? if I type anything into the password box, it takes me to the same data, and the logs indicate it's accepting my header login. relevant config: ``` environment: ACTUAL_LOGIN_METHOD: header ACTUAL_ALLOWED_LOGIN_METHODS: header DEBUG: "actual:config,actual-sensitive:config" ``` is there anything obvious missing? ### How can we reproduce the issue? Launch actual with header auth configured; visit any actual page ### Where are you hosting Actual? docker ### What browsers are you seeing the problem on? Safari, Firefox ### Operating System None
GiteaMirror added the serverbug labels 2026-02-28 19:58:09 -06:00
Author
Owner

@matthewkdies commented on GitHub (Mar 12, 2025):

I've also run into this bug! Using Caddy, here's my Caddyfile configuration

actual.mattdies.com {
	import use_authelia
	import cloudflare_dns
	import local_network_only
	import log_for_app "actual"
	reverse_proxy actual:5006 {
		header_up x-actual-password "{env.ACTUAL_PASSWORD}"  # I've confirmed this is set correctly
	}
    log {
        output stdout
        level debug
        format console
    }
}

When requests are directed to Actual, they have the necessary header:

# in a debug log
"headers":{"X-Actual-Password":["<my_password>"]

However this does not work for me. In the Actual logs, I see the following:

2025-03-12T01:58:56.060Z info: GET 200 /account/needs-bootstrap
2025-03-12T01:58:56.096Z info: GET 304 /account/needs-bootstrap

It is unprotected by password, and returns the following raw JSON:

{
  "status": "ok",
  "data": {
    "bootstrapped": true,
    "loginMethod": "header",
    "availableLoginMethods": [
      {
        "method": "password",
        "active": 1,
        "displayName": "Password"
      }
    ],
    "multiuser": false
  }
}

However in the Docker Compose file, I have the following:

environment:
  ACTUAL_LOGIN_METHOD: header
  ACTUAL_TRUSTED_AUTH_PROXIES: 172.19.0.2  # IP address of Authelia

And they show up in the container as well:

$ docker exec -it actual bash
root@1f25d0a612aa:/app# env | grep ACTUAL
ACTUAL_LOGIN_METHOD=header
ACTUAL_TRUSTED_AUTH_PROXIES=172.19.0.2

For what it's worth, there is an experimental feature for OpenID: https://actualbudget.org/docs/experimental/oauth-auth/.

@matthewkdies commented on GitHub (Mar 12, 2025): I've also run into this bug! Using Caddy, here's my Caddyfile configuration ``` actual.mattdies.com { import use_authelia import cloudflare_dns import local_network_only import log_for_app "actual" reverse_proxy actual:5006 { header_up x-actual-password "{env.ACTUAL_PASSWORD}" # I've confirmed this is set correctly } log { output stdout level debug format console } } ``` When requests are directed to Actual, they have the necessary header: ``` # in a debug log "headers":{"X-Actual-Password":["<my_password>"] ``` However this does not work for me. In the Actual logs, I see the following: ``` 2025-03-12T01:58:56.060Z info: GET 200 /account/needs-bootstrap 2025-03-12T01:58:56.096Z info: GET 304 /account/needs-bootstrap ``` It is unprotected by password, and returns the following raw JSON: ```json { "status": "ok", "data": { "bootstrapped": true, "loginMethod": "header", "availableLoginMethods": [ { "method": "password", "active": 1, "displayName": "Password" } ], "multiuser": false } } ``` However in the Docker Compose file, I have the following: ```yaml environment: ACTUAL_LOGIN_METHOD: header ACTUAL_TRUSTED_AUTH_PROXIES: 172.19.0.2 # IP address of Authelia ``` And they show up in the container as well: ```bash $ docker exec -it actual bash root@1f25d0a612aa:/app# env | grep ACTUAL ACTUAL_LOGIN_METHOD=header ACTUAL_TRUSTED_AUTH_PROXIES=172.19.0.2 ``` For what it's worth, there is an experimental feature for OpenID: https://actualbudget.org/docs/experimental/oauth-auth/.
Author
Owner

@hobbit44 commented on GitHub (Mar 31, 2025):

I'm having this on a fresh install, using openid and multiuser. What I'm noticing is that when you navigate to the root url, you get redirected to /login with the "Sign in with OpenID" button. But if you navigate directly to /login, you get the password prompt and it will fail to login. You have to go back to the home to get the correct redirect state.

@hobbit44 commented on GitHub (Mar 31, 2025): I'm having this on a fresh install, using openid and multiuser. What I'm noticing is that when you navigate to the root url, you get redirected to /login with the "Sign in with OpenID" button. But if you navigate directly to /login, you get the password prompt and it will fail to login. You have to go back to the home to get the correct redirect state.
Author
Owner

@paulcoates commented on GitHub (May 17, 2025):

I'm also experiencing this issue. I've verified that my reverse proxy and actual-server environment variables are configured correctly using the following CURL request to the /account/login endpoint which returns a valid auth token... so header auth looks properly configured.

▶ curl -X POST https://actual.my.tld/account/login \
  -H "x-actual-password: mypassword"
{"status":"ok","data":{"token":"bdd7b8f0-cbe1-4242-ade6-b33fb33fb33f"}}%

However, my browser based requests are still getting redirected to /account/needs-bootstrap/. I had a look at the code for that route from the initial PR in the actualbudget/actual-server repo that added the header auth functionality and compared it to its current state.

c23cbb4b0e/packages/sync-server/src/app-account.js (L32-L45)

The call to listLoginMethods appears to get the login methods associated to accounts in the database, but I don't think 'header' will ever be returned - as the database isn't aware when that is enabled. So unless OpenID has been turned on, this will always return a single value, and getLoginMethod() never gets called like it used to.

I think that is why @hobbit44 was able to get partial resolution when openid and multiuser was enabled, as that situation would mean getLoginMethod gets called, and header auth would be identified.

I think this a a by-product of a fix in OpenID implementation (most recently #4533) that introduced the ternary logic for loginMethod. @lelemm would you have any thoughts as to whether this is on the right track?

@paulcoates commented on GitHub (May 17, 2025): I'm also experiencing this issue. I've verified that my reverse proxy and actual-server environment variables are configured correctly using the following CURL request to the `/account/login` endpoint which returns a valid auth token... so header auth looks properly configured. ``` ▶ curl -X POST https://actual.my.tld/account/login \ -H "x-actual-password: mypassword" {"status":"ok","data":{"token":"bdd7b8f0-cbe1-4242-ade6-b33fb33fb33f"}}% ``` However, my browser based requests are still getting redirected to `/account/needs-bootstrap/`. I had a look at the code for that route from the [initial PR](https://github.com/actualbudget/actual-server/pull/312/files#diff-024ac510e5825a4edf4be0b860f4abba35556d3ca0b5aad96c0499ed126f9452L25) in the actualbudget/actual-server repo that added the header auth functionality and compared it to its current state. https://github.com/actualbudget/actual/blob/c23cbb4b0e0eadec18f9d252c82e55fd7557b16e/packages/sync-server/src/app-account.js#L32-L45 The call to `listLoginMethods` appears to get the login methods associated to accounts in the database, but I don't think 'header' will ever be returned - as the database isn't aware when that is enabled. So unless OpenID has been turned on, this will always return a single value, and `getLoginMethod()` never gets called like it used to. I think that is why @hobbit44 was able to get partial resolution when openid and multiuser was enabled, as that situation would mean `getLoginMethod` gets called, and header auth would be identified. I think this a a by-product of a fix in OpenID implementation (most recently #4533) that introduced the ternary logic for `loginMethod`. @lelemm would you have any thoughts as to whether this is on the right track?
Author
Owner

@matthewkdies commented on GitHub (May 17, 2025):

@paulcoates I just wanted to make a quick response to you above comment to say that is great troubleshooting and documentation! It is really clear while also being succinct, something I strive to do in my comments as well. Hope I'm not bothering you with a ping to say this!

With all of that being said, a quick way to confirm this would be to revert the used version of Actual to one before OpenID Connect was introduced as a feature and test that. I will do this when I get home later, if I remember to haha!

@matthewkdies commented on GitHub (May 17, 2025): @paulcoates I just wanted to make a quick response to you above comment to say that is great troubleshooting and documentation! It is really clear while also being succinct, something I strive to do in my comments as well. Hope I'm not bothering you with a ping to say this! With all of that being said, a quick way to confirm this would be to revert the used version of Actual to one before OpenID Connect was introduced as a feature and test that. I will do this when I get home later, if I remember to haha!
Author
Owner

@lelemm commented on GitHub (May 19, 2025):

I'm also experiencing this issue. I've verified that my reverse proxy and actual-server environment variables are configured correctly using the following CURL request to the /account/login endpoint which returns a valid auth token... so header auth looks properly configured.

▶ curl -X POST https://actual.my.tld/account/login \
  -H "x-actual-password: mypassword"
{"status":"ok","data":{"token":"bdd7b8f0-cbe1-4242-ade6-b33fb33fb33f"}}%

However, my browser based requests are still getting redirected to /account/needs-bootstrap/. I had a look at the code for that route from the initial PR in the actualbudget/actual-server repo that added the header auth functionality and compared it to its current state.

actual/packages/sync-server/src/app-account.js

Lines 32 to 45 in c23cbb4

app.get('/needs-bootstrap', (req, res) => {
const availableLoginMethods = listLoginMethods();
res.send({
status: 'ok',
data: {
bootstrapped: !needsBootstrap(),
loginMethod:
availableLoginMethods.length === 1
? availableLoginMethods[0].method
: getLoginMethod(),
availableLoginMethods,
multiuser: getActiveLoginMethod() === 'openid',
},
});
The call to listLoginMethods appears to get the login methods associated to accounts in the database, but I don't think 'header' will ever be returned - as the database isn't aware when that is enabled. So unless OpenID has been turned on, this will always return a single value, and getLoginMethod() never gets called like it used to.

I think that is why @hobbit44 was able to get partial resolution when openid and multiuser was enabled, as that situation would mean getLoginMethod gets called, and header auth would be identified.

I think this a a by-product of a fix in OpenID implementation (most recently #4533) that introduced the ternary logic for loginMethod. @lelemm would you have any thoughts as to whether this is on the right track?

You are right, @paulcoates. But, I think there is something else making the header auth not work. I'm just not finding time to look at this.
If you want to tackle this and send it to me for review, I would gladly review it.

@lelemm commented on GitHub (May 19, 2025): > I'm also experiencing this issue. I've verified that my reverse proxy and actual-server environment variables are configured correctly using the following CURL request to the `/account/login` endpoint which returns a valid auth token... so header auth looks properly configured. > > ``` > ▶ curl -X POST https://actual.my.tld/account/login \ > -H "x-actual-password: mypassword" > {"status":"ok","data":{"token":"bdd7b8f0-cbe1-4242-ade6-b33fb33fb33f"}}% > ``` > > However, my browser based requests are still getting redirected to `/account/needs-bootstrap/`. I had a look at the code for that route from the [initial PR](https://github.com/actualbudget/actual-server/pull/312/files#diff-024ac510e5825a4edf4be0b860f4abba35556d3ca0b5aad96c0499ed126f9452L25) in the actualbudget/actual-server repo that added the header auth functionality and compared it to its current state. > > [actual/packages/sync-server/src/app-account.js](https://github.com/actualbudget/actual/blob/c23cbb4b0e0eadec18f9d252c82e55fd7557b16e/packages/sync-server/src/app-account.js#L32-L45) > > Lines 32 to 45 in [c23cbb4](/actualbudget/actual/commit/c23cbb4b0e0eadec18f9d252c82e55fd7557b16e) > > app.get('/needs-bootstrap', (req, res) => { > const availableLoginMethods = listLoginMethods(); > res.send({ > status: 'ok', > data: { > bootstrapped: !needsBootstrap(), > loginMethod: > availableLoginMethods.length === 1 > ? availableLoginMethods[0].method > : getLoginMethod(), > availableLoginMethods, > multiuser: getActiveLoginMethod() === 'openid', > }, > }); > The call to `listLoginMethods` appears to get the login methods associated to accounts in the database, but I don't think 'header' will ever be returned - as the database isn't aware when that is enabled. So unless OpenID has been turned on, this will always return a single value, and `getLoginMethod()` never gets called like it used to. > > I think that is why [@hobbit44](https://github.com/hobbit44) was able to get partial resolution when openid and multiuser was enabled, as that situation would mean `getLoginMethod` gets called, and header auth would be identified. > > I think this a a by-product of a fix in OpenID implementation (most recently [#4533](https://github.com/actualbudget/actual/pull/4533)) that introduced the ternary logic for `loginMethod`. [@lelemm](https://github.com/lelemm) would you have any thoughts as to whether this is on the right track? You are right, @paulcoates. But, I think there is something else making the header auth not work. I'm just not finding time to look at this. If you want to tackle this and send it to me for review, I would gladly review it.
Author
Owner

@pyrho commented on GitHub (May 24, 2025):

I've made an attempt at a fix, some assumptions were made.. I'm not 100% sure of what I did; worst case it serves as a base to a proper fix for someone who actually knows what they're doing.

I'm currently running a container that has my changes, with header auth working.
I built it with this command:
docker build -f packages/sync-server/docker/alpine.Dockerfile -t actual-header-auth-fix)

And here are the environment variables I have set:

      ACTUAL_LOGIN_METHOD: header
      ACTUAL_HOSTNAME: 0.0.0.0
      ACTUAL_TRUSTED_AUTH_PROXIES: 172.29.0.4/16
@pyrho commented on GitHub (May 24, 2025): I've made [an attempt at a fix](https://github.com/actualbudget/actual/pull/5054), some assumptions were made.. I'm not 100% sure of what I did; worst case it serves as a base to a proper fix for someone who actually knows what they're doing. I'm currently running a container that has my changes, with header auth working. I built it with this command: `docker build -f packages/sync-server/docker/alpine.Dockerfile -t actual-header-auth-fix`) And here are the environment variables I have set: ```env ACTUAL_LOGIN_METHOD: header ACTUAL_HOSTNAME: 0.0.0.0 ACTUAL_TRUSTED_AUTH_PROXIES: 172.29.0.4/16 ```
Author
Owner

@ky1vstar commented on GitHub (Aug 27, 2025):

I've discovered a workaround, that results in working forward auth setup via Traefik and Actual Budget in docker compose.
Make sure, that you know what you are doing since it required direct SQLite manipulations.
You need to execute following commands inside a container:

apt-get install sqlite3

# List all login methods in DB
sqlite3 /data/server-files/account.sqlite 'select * from auth;'

# Add new login method named Header
sqlite3 /data/server-files/account.sqlite 'INSERT INTO auth (method, display_name, active) VALUES ("header", "Header", 1);'

# Optionally disable password login method in Web GUI
sqlite3 /data/server-files/account.sqlite 'UPDATE auth SET active = 0 WHERE method = "password";'

# Ensure everything looks like it should
sqlite3 /data/server-files/account.sqlite 'select * from auth;'
@ky1vstar commented on GitHub (Aug 27, 2025): I've discovered a workaround, that results in working forward auth setup via Traefik and Actual Budget in docker compose. Make sure, that you know what you are doing since it required direct SQLite manipulations. You need to execute following commands inside a container: ```shell apt-get install sqlite3 # List all login methods in DB sqlite3 /data/server-files/account.sqlite 'select * from auth;' # Add new login method named Header sqlite3 /data/server-files/account.sqlite 'INSERT INTO auth (method, display_name, active) VALUES ("header", "Header", 1);' # Optionally disable password login method in Web GUI sqlite3 /data/server-files/account.sqlite 'UPDATE auth SET active = 0 WHERE method = "password";' # Ensure everything looks like it should sqlite3 /data/server-files/account.sqlite 'select * from auth;' ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/actual#1907