# Fix Jupyter server token and password login failures
Use this when the browser shows `Invalid credentials`, loops back to the login
page, or a token URL that used to work stops working. Token auth and password
auth are separate mechanisms configured in separate places — classify which one
you're fighting before changing anything.
## 1. Classify: token problem vs password problem vs ignored config
**A. A token URL fails:** tokens are generated fresh at every server start. A
bookmarked token, or a token from `jupyter server list` output copied before a
restart, is dead. Get the current one (step 2).
**B. The password form rejects you** (`Invalid credentials`, and the server log
shows `401 POST /login`): the server isn't reading the password you think it is
(step 3).
**C. You expected password auth but get a token prompt (or vice versa):** the
password was written to a config file the running server doesn't read. The
classic cause is setting it for the wrong server generation (step 3).
## 2. Token lookup: find the current one
```bash
jupyter server list
```
This lists your running servers with their current token URLs — copy the full
URL including the token query parameter. Two things that routinely confuse:
- The token in the auto-opened browser tab is a **one-time** token; the
persistent token is the one printed in the terminal at server startup.
- `jupyter server list` only sees servers started by the same user. A token
from another user's (or root's) server will never work for yours.
## 3. Password setup: set it the supported way, in the file the server reads
```bash
jupyter server password
```
This prompts twice and writes an argon2 hash into `jupyter_server_config.json`
(under an IdentityProvider section holding a hashed-password value that starts
with `argon2:`). The plaintext is never stored. `jupyter server --generate-config`
creates the `~/.jupyter/jupyter_server_config.py` file if you prefer Python config.
Why a password you set still gives `Invalid credentials`:
- **Wrong server generation.** `jupyter notebook password` (Notebook 6 era)
writes a NotebookApp password into `jupyter_notebook_config.json` — the new
jupyter_server ignores it entirely. Use `jupyter server password`.
- **Stale config lines.** Old `c.NotebookApp.password` or `c.ServerApp.password`
lines in config files are ignored by jupyter_server 2.x, which reads the
hashed password from `jupyter_server_config.json` (the `IdentityProvider`
section). Remove the stale lines.
- **Wrong config directory.** Check `jupyter --config-dir`; the `JUPYTER_CONFIG_DIR`
environment variable overrides it (handy in containers — set it to a directory
you bake the config file into).
- To hash programmatically instead of interactively: `from jupyter_server.auth
import passwd` and call `passwd()`.
## 4. Remote and proxied servers
- Bind all interfaces: `jupyter lab --ip` with the all-interfaces address (config key `c.ServerApp.ip`).
- Open the URL **with the token** from `jupyter server list` or the terminal;
opening plain `/lab` just redirects to the login page.
- Behind a reverse proxy with a subpath: a login loop where the POST goes to the
wrong path is a proxy/base_url mismatch, not a bad password. Set
`c.ServerApp.base_url` to the subpath the proxy serves.
## 5. Deliberately disabling auth (local-only machines)
In `jupyter_server_config.py`, setting `c.ServerApp.token` to an empty string
disables token authentication. Only do this on a machine you trust that isn't
reachable from the network — never on a shared or network-exposed server.
## 6. Checklist for the login failure
1. Reproduce: note whether it's a token URL, the password form, or a login
loop; check the server log for `401 POST /login`.
2. Token retrieval: pull the current token URL from `jupyter server list`; discard any
token from before the last server restart.
3. Password configuration: set it with `jupyter server password`; confirm the server reads
`jupyter_server_config.json` (check `jupyter --config-dir`); delete stale
NotebookApp/ServerApp password lines.
4. Remote: `--ip`, full token URL, and `base_url` behind proxies.
5. Never disable auth on a network-exposed server.