salesforce / tough-cookie

RFC6265 Cookies and CookieJar for Node.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Store's putCookie never called from setCookie?

MauriceArikoglu opened this issue · comments

I am having issues with a custom Store's methods not being called. I looked through the code in this repo and it seems CookieJar never calls store.putCookie from within setCookie (or any other method)?

Hello @MauriceArikoglu,

What do you mean by using a "custom Store's method"? Can you post some of the code you are trying to use so I can replicate the issue?

Thanks,
Mike

@medelibero-sfdc hey, thanks for looking into this.
You can setup a spike project and setup a plain object conforming to the Store interface. You will notice its setCookie / putCookie method is never called on CookieJar.setCookie.

Have a look at the implementation for CookieJar.setCookie here:

setCookie(cookie, url, options, cb) {

If you follow the method implementation you will notice there is no path where putCookie will be called...

The full method in question, as its quite big and not so straightforward:

tough-cookie/lib/cookie.js

Lines 1094 to 1277 in 1b25269

setCookie(cookie, url, options, cb) {
validators.validate(validators.isNonEmptyString(url), cb, options);
let err;
if (validators.isFunction(url)) {
cb = url;
return cb(new Error("No URL was specified"));
}
const context = getCookieContext(url);
if (validators.isFunction(options)) {
cb = options;
options = {};
}
validators.validate(validators.isFunction(cb), cb);
if(!validators.isNonEmptyString(cookie) && !validators.isObject(cookie) && ( cookie instanceof String && cookie.length == 0)) {
return cb(null);
}
const host = canonicalDomain(context.hostname);
const loose = options.loose || this.enableLooseMode;
let sameSiteContext = null;
if (options.sameSiteContext) {
sameSiteContext = checkSameSiteContext(options.sameSiteContext);
if (!sameSiteContext) {
return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR));
}
}
// S5.3 step 1
if (typeof cookie === "string" || cookie instanceof String) {
cookie = Cookie.parse(cookie, { loose: loose });
if (!cookie) {
err = new Error("Cookie failed to parse");
return cb(options.ignoreError ? null : err);
}
} else if (!(cookie instanceof Cookie)) {
// If you're seeing this error, and are passing in a Cookie object,
// it *might* be a Cookie object from another loaded version of tough-cookie.
err = new Error(
"First argument to setCookie must be a Cookie object or string"
);
return cb(options.ignoreError ? null : err);
}
// S5.3 step 2
const now = options.now || new Date(); // will assign later to save effort in the face of errors
// S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
// S5.3 step 4: NOOP; domain is null by default
// S5.3 step 5: public suffixes
if (this.rejectPublicSuffixes && cookie.domain) {
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
if (suffix == null) {
// e.g. "com"
err = new Error("Cookie has domain set to a public suffix");
return cb(options.ignoreError ? null : err);
}
}
// S5.3 step 6:
if (cookie.domain) {
if (!domainMatch(host, cookie.cdomain(), false)) {
err = new Error(
`Cookie not in this host's domain. Cookie:${cookie.cdomain()} Request:${host}`
);
return cb(options.ignoreError ? null : err);
}
if (cookie.hostOnly == null) {
// don't reset if already set
cookie.hostOnly = false;
}
} else {
cookie.hostOnly = true;
cookie.domain = host;
}
//S5.2.4 If the attribute-value is empty or if the first character of the
//attribute-value is not %x2F ("/"):
//Let cookie-path be the default-path.
if (!cookie.path || cookie.path[0] !== "/") {
cookie.path = defaultPath(context.pathname);
cookie.pathIsDefault = true;
}
// S5.3 step 8: NOOP; secure attribute
// S5.3 step 9: NOOP; httpOnly attribute
// S5.3 step 10
if (options.http === false && cookie.httpOnly) {
err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
return cb(options.ignoreError ? null : err);
}
// 6252bis-02 S5.4 Step 13 & 14:
if (cookie.sameSite !== "none" && sameSiteContext) {
// "If the cookie's "same-site-flag" is not "None", and the cookie
// is being set from a context whose "site for cookies" is not an
// exact match for request-uri's host's registered domain, then
// abort these steps and ignore the newly created cookie entirely."
if (sameSiteContext === "none") {
err = new Error(
"Cookie is SameSite but this is a cross-origin request"
);
return cb(options.ignoreError ? null : err);
}
}
/* 6265bis-02 S5.4 Steps 15 & 16 */
const ignoreErrorForPrefixSecurity =
this.prefixSecurity === PrefixSecurityEnum.SILENT;
const prefixSecurityDisabled =
this.prefixSecurity === PrefixSecurityEnum.DISABLED;
/* If prefix checking is not disabled ...*/
if (!prefixSecurityDisabled) {
let errorFound = false;
let errorMsg;
/* Check secure prefix condition */
if (!isSecurePrefixConditionMet(cookie)) {
errorFound = true;
errorMsg = "Cookie has __Secure prefix but Secure attribute is not set";
} else if (!isHostPrefixConditionMet(cookie)) {
/* Check host prefix condition */
errorFound = true;
errorMsg =
"Cookie has __Host prefix but either Secure or HostOnly attribute is not set or Path is not '/'";
}
if (errorFound) {
return cb(
options.ignoreError || ignoreErrorForPrefixSecurity
? null
: new Error(errorMsg)
);
}
}
const store = this.store;
if (!store.updateCookie) {
store.updateCookie = function(oldCookie, newCookie, cb) {
this.putCookie(newCookie, cb);
};
}
function withCookie(err, oldCookie) {
if (err) {
return cb(err);
}
const next = function(err) {
if (err) {
return cb(err);
} else {
cb(null, cookie);
}
};
if (oldCookie) {
// S5.3 step 11 - "If the cookie store contains a cookie with the same name,
// domain, and path as the newly created cookie:"
if (options.http === false && oldCookie.httpOnly) {
// step 11.2
err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
return cb(options.ignoreError ? null : err);
}
cookie.creation = oldCookie.creation; // step 11.3
cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker
cookie.lastAccessed = now;
// Step 11.4 (delete cookie) is implied by just setting the new one:
store.updateCookie(oldCookie, cookie, next); // step 12
} else {
cookie.creation = cookie.lastAccessed = now;
store.putCookie(cookie, next); // step 12
}
}
store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
}

Yes, and putCookie is called on lines 1240 and 1272.

@medelibero-sfdc
You are right. It seems I lost my ability to read. My bad.

Still, I observed putCookie not being called on my custom Store...

Hi @MauriceArikoglu,

What do you mean by your custom store? Can you show me the code that is not working?

-Mike

Is this still an issue we should look at @MauriceArikoglu?

@awaterma I think it isn't. I am not a part of the project anymore. IIRC I used a workaround, but it also might've been a mistake on my end. Can be closed I guess :)

Thank you!