Current Behavior
Description
The documentation for custom authentication tokens states:
By default, it will use the Authorization http header and expect a Bearer type
token - passing in just the value of the token to the function. If it is not a
Bearer type token, then the full value of the Authorization header will be
passed to the function, containing both type and value.
This implies that any value in the Authorization header will eventually reach the
user-defined tokens() function. This is not the case.
Root Cause
Authentication is handled in needsPermission() in
packages/node_modules/@node-red/editor-api/lib/auth/index.js, which runs passport
strategies in this order:
passport.authenticate(['bearer', 'tokens', 'anon'], { session: false })
The bearer strategy is provided by the third-party package
passport-http-bearer.
Looking at its implementation in lib/strategy.js:
var parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
if (/^Bearer$/i.test(scheme)) {
token = credentials;
}
} else {
return this.fail(400); // ← hard 400, chain stops here
}
If the Authorization header is present but not in exactly Bearer <token> format,
passport-http-bearer calls this.fail(400), which immediately returns a 400
response to the client. The passport strategy chain is aborted — the tokens and
anon strategies never run, and the user-defined tokens() function is never called.
Impact
Any reverse proxy or integration that forwards a non-Bearer Authorization header
(e.g. a raw token, Basic auth, or a custom scheme) will receive a 400 response,
regardless of whether their tokens() function would have accepted it. The docs
explicitly promise this use case is supported.
Steps to Reproduce
- Configure
adminAuth with a tokens function that logs its input and accepts any value:
adminAuth: {
tokens: function(token) {
console.log("tokens() called with:", token);
return Promise.resolve({ username: "admin", permissions: "*" });
}
}
- Send a request with a non-Bearer
Authorization header:
curl -H "Authorization: myrawtoken" http://localhost:1880/settings
- Observe that the response is 400, and
tokens() is never called.
Expected Behaviour
Per the documentation, the full value of the Authorization header should be passed
to the tokens() function when it is not a Bearer type token.
Actual Behaviour
A 400 is returned immediately by passport-http-bearer before the tokens() function
is ever invoked.
Environment
Expected Behavior
No response
Steps To Reproduce
No response
Example flow
Environment
- Node-RED version:
- Node.js version:
- npm version:
- Platform/OS:
- Browser:
Current Behavior
Description
The documentation for custom authentication tokens states:
This implies that any value in the
Authorizationheader will eventually reach theuser-defined
tokens()function. This is not the case.Root Cause
Authentication is handled in
needsPermission()inpackages/node_modules/@node-red/editor-api/lib/auth/index.js, which runs passportstrategies in this order:
The
bearerstrategy is provided by the third-party packagepassport-http-bearer.Looking at its implementation in
lib/strategy.js:If the
Authorizationheader is present but not in exactlyBearer <token>format,passport-http-bearercallsthis.fail(400), which immediately returns a 400response to the client. The passport strategy chain is aborted — the
tokensandanonstrategies never run, and the user-definedtokens()function is never called.Impact
Any reverse proxy or integration that forwards a non-Bearer
Authorizationheader(e.g. a raw token,
Basicauth, or a custom scheme) will receive a 400 response,regardless of whether their
tokens()function would have accepted it. The docsexplicitly promise this use case is supported.
Steps to Reproduce
adminAuthwith atokensfunction that logs its input and accepts any value:Authorizationheader:curl -H "Authorization: myrawtoken" http://localhost:1880/settingstokens()is never called.Expected Behaviour
Per the documentation, the full value of the
Authorizationheader should be passedto the
tokens()function when it is not aBearertype token.Actual Behaviour
A 400 is returned immediately by
passport-http-bearerbefore thetokens()functionis ever invoked.
Environment
Expected Behavior
No response
Steps To Reproduce
No response
Example flow
Environment