Environment
- Node:
20.19.4
- passport-apple:
2.0.2
- @types/passport-apple:
2.0.3
- express:
5.1.0
- @nestjs/passport:
11.0.5
Overview
Inside AppleStrategy.authenticate() (passport-apple 2.0.2), the code attempts to merge req.body into req.query. Starting with Express v5, req.query is implemented as a getter, so the assigned value is ignored and remains an empty object. This causes the OAuth flow to fail because required params are never forwarded.
Steps to Reproduce
-
Send a POST request for Apple login.
- Apple auth data is in
req.body. req.query is empty object.
-
Call AppleStrategy.authenticate().
-
Inside authenticate(), run req.query = { ...req.query, ...req.body } to merge query/body data.
- In Express v5,
req.query is a getter, so the assignment is ignored and it stays empty.
- Even if
req.body has data, it is effectively discarded.
-
Call OAuth2Strategy.authenticate() with an empty req.query.
-
Authentication fails.
Detail
- Express.js v4
req.query (empty object)
- Express.js v5
req.query (empty object)
[Object: null prototype] {}
Proposed Fixes
-
Do not assign to req.query directly. Use Object.defineProperty() to set query instead.
- We patched the library locally with this approach and confirmed it fixes the issue.
- Caveat: This change targets Express v5’s behavior; other frameworks may react differently.
-
Create a new req object (or a shallow clone) and pass that to OAuth2Strategy.authenticate.
- Less efficient (extra object creation), but framework-agnostic.
Patch Example
// src/strategy.js
Strategy.prototype.authenticate = function (req, options) {
// Workaround instead of reimplementing authenticate function
req.query = { ...req.query, ...req.body };
if(req.body && req.body.user){
req.appleProfile = JSON.parse(req.body.user)
}
OAuth2Strategy.prototype.authenticate.call(this, req, options);
};
// src/strategy.js
Strategy.prototype.authenticate = function (req, options) {
// Workaround instead of reimplementing authenticate function
Object.defineProperty(req, 'query', {
value: { ...req.query, ...req.body },
writable: true,
configurable: true,
enumerable: true,
});
if(req.body && req.body.user){
req.appleProfile = JSON.parse(req.body.user)
}
OAuth2Strategy.prototype.authenticate.call(this, req, options);
};
References
Environment
20.19.42.0.22.0.35.1.011.0.5Overview
Inside
AppleStrategy.authenticate()(passport-apple2.0.2), the code attempts to mergereq.bodyintoreq.query. Starting with Express v5,req.queryis implemented as a getter, so the assigned value is ignored and remains an empty object. This causes the OAuth flow to fail because required params are never forwarded.Steps to Reproduce
Send a POST request for Apple login.
req.body.req.queryis empty object.Call
AppleStrategy.authenticate().Inside
authenticate(), runreq.query = { ...req.query, ...req.body }to merge query/body data.req.queryis a getter, so the assignment is ignored and it stays empty.req.bodyhas data, it is effectively discarded.Call
OAuth2Strategy.authenticate()with an emptyreq.query.Authentication fails.
Detail
req.query(empty object)req.query(empty object)Proposed Fixes
Do not assign to
req.querydirectly. UseObject.defineProperty()to setqueryinstead.Create a new
reqobject (or a shallow clone) and pass that toOAuth2Strategy.authenticate.Patch Example
References