I developing Grails+BlazeDS server and Flex AIR client and stucked with this error:
Detected duplicate HTTP-based FlexSessions, generally due to the remote host disabling session cookies. Session cookies must be enabled to manage the client connection correctly
Google searches didn't successfully, as I see some difference in situations.
The issue I got only when Flex client interact with server via https.
Flex client:
<s:ChannelSet id="userChannel">
<s:SecureAMFChannel uri="https://localhost:8443/Con/messagebroker/amfpolling" />
</s:ChannelSet>
button click in UI triggered login method:
loginResult.token = channelSet.login(usernameInput.text, passwordInput.text);
And finished with DuplicateSessionDetected exception. :(
After investigating network monitor logs, I found that jsession cookie received from server not set in next requests to a server:
Response from server (operation: client_ping)
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=F58F1ADA97E70915EF9E6E4EE1AEBE00; Path=/; Secure
Content-Type: application/x-amf
Content-Length: 173
Date: Sun, 23 Feb 2014 10:17:00 GMT
Flex Message (flex.messaging.messages.AcknowledgeMessageExt) clientId = EA18E8B9-951F-6F87-7B47-48B8B202EE75 correlationId = 7D2782C1-C8A5-41A3-2055-5E3F771424C8 destination = null messageId = EA18E8F6-9E0E-1FE4-0D26-6F0E602F5C5E timestamp = 1393150620542 timeToLive = 0 body = null hdr(DSMessagingVersion) = 1.0 hdr(DSId) = EA18E8B9-950B-4B42-EF70-369D656BA3F2
And next request to server (login operation) without jsession cookie:
POST /Conn/messagebroker/amfsecure HTTP/1.1
Referer: app:/BlazeDSClient.swf
Accept: text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, text/css, image/png, image/jpeg, image/gif;q=0.8, application/x-shockwave-flash, video/mp4;q=0.9, flv-application/octet-stream;q=0.8, video/x-flv;q=0.7, audio/mp4, application/futuresplash, */*;q=0.5
x-flash-version: 12,0,0,68
Content-Type: application/x-amf
Accept-Encoding: gzip,deflate
User-Agent: Mozilla/5.0 (Windows; U; en) AppleWebKit/533.19.4 (KHTML, like Gecko) AdobeAIR/4.0
Host: localhost
Content-Length: 299
Flex Message (flex.messaging.messages.CommandMessage) operation = login clientId = null destination = auth messageId = 7B47BBF2-08C0-0E41-5D88-5E3F76FA4882 timestamp = 0 timeToLive = 0 ***not printing credentials***
and server answering with new session cookie:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=03BD8347F9E9511C299B717DD55625C9; Path=/; Secure
Content-Type: application/x-amf
Content-Length: 535
Date: Sun, 23 Feb 2014 10:17:01 GMT
Flex Message (flex.messaging.messages.ErrorMessage) clientId = null correlationId = 7B47BBF2-08C0-0E41-5D88-5E3F76FA4882 destination = auth messageId = EA18F4A7-C80D-103B-F8D0-58B6F148F142 timestamp = 1393150621768 timeToLive = 0 body = null code = Server.Processing.DuplicateSessionDetected message = Detected duplicate HTTP-based FlexSessions, generally due to the remote host disabling session cookies. Session cookies must be enabled to manage the client connection correctly. details = null rootCause = null body = null extendedData = null
And again - when used non-secure protocol everything ok - session cookie sevt to server in login operation as expected.
I have a little experience in Flex development and didn't find any method to set session cookie when triggered channel login request. Can you help to resolve this issue?
Thanks!
Gotcha!!
It's unbelievable, but the cause of DuplicateSessionDetected exception has been a Network Monitor tool of Flash Builder. After switching it off no any exception has been occurred. I think there issues when Monitor acting as proxy when used with secure protocol.
Surely, this question is already dead, but I have got something to say in this regard for future readers.
The Flash Player (including Flex) does not transmit the default JSESSIONID in the request and cannot do it until you have set SameSite=None in the JSESSIONID cookie.
I have faced the problem where the JSESSIONID cookie is dropped in the request and I have discovered that it is because modern browsers (chrome > 80) do not allow the Flash/Flex Player to access the JSESSIONID cookie it the cookie does not have SameSite=None and Secure flash.
Please, read the announcement from Adobe here
More to read about the new cookie policy:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
https://medium.com/adobetech/adobe-experience-cloud-cookie-updates-for-google-chrome-19ad67cf1598
https://digiday.com/media/what-is-chrome-samesite/
Do not perform the client_ping operation and then try the secure channelSet. by pingin the server, you are creating another channelset(by default flash creates one for you) and then you are trying to open another channelset using .login operation. Try this by restarting you server,(fresh instance) or else you will be creating more sessions.
Related
I have some react code that is rendering content dynamically via React.createElement. As such, css is applied via an object. Elements in that dynamic generation can have background image, pointing to a public aws S3 bucket.
It seems that every time my components re-render, the background images are being fetched again from S3. This is delaying the page render. I have S3 meta-data for Cache-Control set on all the objects . Here are request and response headers for background image load -
Response header -
Accept-Ranges: bytes
Cache-Control: public, max-age=604800
Content-Length: 52532
Content-Type: application/octet-stream
Date: Sun, 06 Feb 2022 05:57:32 GMT
ETag: "f29655808a5f80627d9ea7f44058a5e3"
Last-Modified: Sun, 06 Feb 2022 05:55:10 GMT
Server: AmazonS3
x-amz-meta-filetype: IMAGE
Request Header -
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,hi;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: <bucket-name>s3.amazonaws.com
Pragma: no-cache
Referer: https://<my-domain>.com/
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
I can see in Network tab that images are being loaded multiple times and it also shows data transfers being done everytime. What am I doing wrong here? Can someone please help finding the root cause. Thanks.
could it be a forgotten "disable cache" option selected in the network tab in the dev tools ? Because it seems the server responds with the correct type of cache headers.
If images are optimized and not huge, using base64 data url is a good solution.
const getBase64FromUrl = async (url) => {
const data = await fetch(url);
const blob = await data.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => {
const base64data = reader.result;
resolve(base64data);
}
});
}
const image = getBase64FromUrl('the image url')
While creating the element you can use
background-image: `url(${image})`;
In addition, rarely do we serve from S3 directly, you should probably use cloudfront as a proxy to
reduce get request
reduce bandwidth charges
cache at cdn
better control of cache headers
hide your s3 real url
The reason you're seeing a network request is probably because you're using the Cache-Control: no-cache header in your request.
As seen here:
The no-cache response directive indicates that the response can be
stored in caches, but the response must be validated with the origin
server before each reuse, even when the cache is disconnected from the
origin server.
Cache-Control: no-cache
If you want caches to always check for content
updates while reusing stored content, no-cache is the directive to
use. It does this by requiring caches to revalidate each request with
the origin server.
Note that no-cache does not mean "don't cache". no-cache allows caches
to store a response but requires them to revalidate it before reuse.
If the sense of "don't cache" that you want is actually "don't store",
then no-store is the directive to use.
See here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#response_directives
Here is what a full request for a cached asset looks like on my network tab, when the asset returns 304 Not Modified from the validation request. (from S3) This is in a background: url context.
I'm new to Cache Control Header implementation and I need someone to point out any of my mistakes and/or misunderstandings over the cache control effects on Firebase Cloud Functions.
My understanding & expectation on Cache Control over Firebase Functions
When the Cache Control Header has been successfully set using Express response object (confirmed by checking from the Chrome's Network
tab), regardless it is on localhost or production server, the Firebase
Https Functions (not callable functions) should not be invoked again
after the first reload until the cache is expired.
Am I right? But after a few rounds of testing, it seems like my cloud function on localhost still consistently get invoked (confirmed by server console logging) regardless the number of refresh on my web browser. Below is my current Http header:
**General:**
Request URL: http://localhost:5005/otk-web-solutions?id=B0Y0jp2x83WVYzWrpg5y
Request Method: GET
Status Code: 304 Not Modified
Remote Address: 127.0.0.1:5005
Referrer Policy: strict-origin-when-cross-origin
**Response Headers:**
Access-Control-Allow-Origin: *
cache-control: public, max-age=432000, s-maxage=432000
content-length: 9688
content-type: text/html; charset=utf-8
date: Mon, 05 Apr 2021 11:52:20 GMT
etag: W/"25d8-TxL0Q+ujhzDjys8IJ1mLigY7jT8"
vary: Origin, Accept-Encoding, Authorization, Cookie
x-powered-by: Express
**Request Headers:**
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,en-US;q=0.8,zh;q=0.7
Cache-Control: max-age=0
Connection: keep-alive
Cookie: _ga=GA1.1.816734993.1603107580; _gid=GA1.1.223745218.1617606982; __atuvc=20%7C12%2C15%7C13%2C23%7C14; __atuvs=606aec5f76521aab00a
DNT: 1
Host: localhost:5005
If-None-Match: W/"25d8-TxL0Q+ujhzDjys8IJ1mLigY7jT8"
sec-ch-ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
**Query String Parameters:**
id: B0Y0jp2x83WVYzWrpg5y
On Firebase documentation:
You can, though, configure caching behavior for dynamic content. For
example, if a function generates new content only periodically, you
can speed up your app by caching the generated content for at least a
short period of time.
You can also potentially reduce function execution costs because the
content is served from the CDN rather than via a triggered function.
Could it be that, the cache control header has no effects on localhost except on Firebase CDN, which means only when we've deployed it to the production server for the caching to work on the cloud CDN? Is there a right way to implement such test to see the effectiveness of the cache control header in helping to save the Firebase Cloud Functions' execution costs?
Please advise, thanks a lot!
From my understanding of the documentation and after checking your test, only if the content is served from the CDN you will be able to save costs.
Even if your request is met with a status code 304 Not Modified, it seems like you're still making the request and invoking the function if you're not using the Firebase Hosting CDN.
So to make a test to see if you can save costs by not invoking the function many times you should set up Firebase Hosting and do that same test to see if a response from the CDN invokes the function.
I am implementing a oAuth login for a user for the firebase platform.
All works fine except if the user has disabled cross domain cookies.
Here is what I did.
From my domain/app the user gets redirected to a cloud function.
The could function sets the state cookie and redirects the user to the oAuth provider.
The user signs in to the oAuth provider and gets redirected back to another function to get the code etc. And here is the problem
On step 3 above the function cannot read any cookie if the user has disabled the cross domain party cookies from his browser.
Both functions are on the same domain as seen below in the screenshot.
Is there any way I can remedy this issue? Am I doing something wrong in my approach?
I cannot understand why the 2 functions are treated as crossdomain.
Update to include more info
Request:
Request URL: https://europe-west2-quantified-self-io.cloudfunctions.net/authRedirect
Request Method: GET
Status Code: 302
Remote Address: [2a00:1450:4007:811::200e]:443
Referrer Policy: no-referrer-when-downgrade
Request Headers
:authority: europe-west2-quantified-self-io.cloudfunctions.net
:method: GET
:path: /authRedirect
:scheme: https
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
accept-encoding: gzip, deflate, br
accept-language: en-GB,en-US;q=0.9,en;q=0.8
cookie: signInWithService=false; state=877798d3672e7d6fa9588b03f1e26794f4ede3a0
dnt: 1
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
Response Headers
alt-svc: quic=":443"; ma=2592000; v="46,43,39"
cache-control: private
content-encoding: gzip
content-length: 218
content-type: text/html; charset=utf-8
date: Sat, 03 Aug 2019 08:55:18 GMT
function-execution-id: c8rjc7xnvoy8
location: https://cloudapi-oauth.suunto.com/oauth/authorize?response_type=code&client_id=xxx&redirect_uri=&scope=workout&state=1c8073866d1ffaacf2d4709090ad099872718afa
server: Google Frontend
set-cookie: state=1c8073866d1ffaacf2d4709090ad099872718afa; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
set-cookie: signInWithService=false; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
status: 302
vary: Accept
x-cloud-trace-context: 99a93680a17770f848f200a9e729b122;o=1
x-powered-by: Express
After that and once the user returns from the service he authenticated against the code that parses the cookies (or the function that handles that) is:
export const authToken = functions.region('europe-west2').https.onRequest(async (req, res) => {
const oauth2 = suuntoAppAuth();
cookieParser()(req, res, async () => {
try {
const currentDate = new Date();
const signInWithService = req.cookies.signInWithService === 'true';
console.log('Should sign in:', signInWithService);
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
const results = await oauth2.authorizationCode.getToken({
code: req.query.code,
redirect_uri: determineRedirectURI(req), // #todo fix,
});
// console.log('Auth code exchange result received:', results);
// We have an access token and the user identity now.
const accessToken = results.access_token;
const suuntoAppUserName = results.user;
// Create a Firebase account and get the Custom Auth Token.
let firebaseToken;
if (signInWithService) {
firebaseToken = await createFirebaseAccount(suuntoAppUserName, accessToken);
}
return res.jsonp({
firebaseAuthToken: firebaseToken,
serviceAuthResponse: <ServiceTokenInterface>{
accessToken: results.access_token,
refreshToken: results.refresh_token,
tokenType: results.token_type,
expiresAt: currentDate.getTime() + (results.expires_in * 1000),
scope: results.scope,
userName: results.user,
dateCreated: currentDate.getTime(),
dateRefreshed: currentDate.getTime(),
},
serviceName: ServiceNames.SuuntoApp
});
} catch (error) {
return res.jsonp({
error: error.toString(),
});
}
});
});
The above code does not find a cookie with the name state
So it fails here
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
Did a little more search here is some more info.
The example I based on https://github.com/firebase/functions-samples/tree/master/instagram-auth
Looks like other users suffer from the same issue https://github.com/firebase/functions-samples/issues/569
I opened also this issue https://github.com/firebase/firebase-functions/issues/544
Your Response shows a Set-Cookie header for state and signInWithService cookies without a domain attribute:
set-cookie: state=1c8073866d1ffaacf2d4709090ad099872718afa; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
set-cookie: signInWithService=false; Max-Age=3600; Path=/; Expires=Sat, 03 Aug 2019 09:55:18 GMT; HttpOnly; Secure
Set-Cookie without a domain means that what happens to the cookie on the way back to the server is browser-dependent. The "default", spec-compliant behavior: the browser will grab the FQDN of the service URL and associate it with the cookie. RFC6265:
Unless the cookie's attributes indicate otherwise, the cookie is
returned only to the origin server (and not, for example, to any
subdomains)...If the server omits the Domain attribute, the user agent
will return the cookie only to the origin server.
When the browser decides whether to accept the cookie from a HTTP service, one of the decision criteria is if the cookie is first-party or third-party:
First-party cookie: if the resource (web page) you requested that triggered a call to europe-west2-quantified-self-io.cloudfunctions.net/authRedirect resides at https://europe-west2-quantified-self-io.cloudfunctions.net/...
Third-party cookie: if the resource (web page) you requested that triggered a call to europe-west2-quantified-self-io.cloudfunctions.net/authRedirect resides at https://some.domain.app.com/...
In your case the FQDN of your "parent" app/page is likely different from europe-west2-quantified-self-io.cloudfunctions.net, thus these cookies are labeled as third-party. As you found out, a user can choose to block third-party cookies. As of August 2019, Firefox and Safari block 3rd-party cookies by default. Most (if not all) ad blockers and similar extensions also block them. This would lead the browser to simply ignore Set-Cookie header in the HTTP response from europe-west2-quantified-self-io.cloudfunctions.net/authRedirect. The cookie would not be sent back to 2nd Firebase function at europe-west2-quantified-self-io.cloudfunctions.net/authToken because it doesn't exist on the client.
Your options:
Host your app and Firebase functions on the same domain.
An architecture where all HTTP requests (app and Firebase functions) flow through the app; the latter acts as a proxy of sorts for the function calls. Here's one way to do it in Firebase.
Let's say your app and Firebase functions do reside in different domains. In Javascript you can create a small piece of middleware that calls the /authRedirect FB function, parses the response (incl. the cookies via Set-Cookie header), then writes the response (incl. cookies) back to the browser via document.cookie. The cookies in this case would be first-party.
Don't use cookies at all. The oAuth authorization grant flow that you're doing against cloudapi-oauth.suunto.com as the authorization server does not require cookies. You followed an instagram-auth example that recommends this flow
When clicking the Sign in with Instagram button a popup is shown which
redirects users to the redirect Function URL.
The redirect Function then redirects the user to the Instagram OAuth
2.0 consent screen where (the first time only) the user will have to grant approval. Also the state cookie is set on the client with the
value of the state URL query parameter to check against later on.
The check against state query parameter is based on an implementation best practice for oAuth clients when authorization servers don't support the PKCE extension (cloudapi-oauth.suunto.com doesn't support it):
Clients MUST prevent CSRF. One-time use CSRF tokens carried in the
"state" parameter, which are securely bound to the user agent, SHOULD
be used for that purpose. If PKCE [RFC7636] is used by the client and
the authorization server supports PKCE, clients MAY opt to not use
"state" for CSRF protection, as such protection is provided by PKCE.
In this case, "state" MAY be used again for its original purpose,
namely transporting data about the application state of the client
The key phrase is securely bound to the user agent. For web apps, a cookie is a decent option of implementing this binding but it's not the only option. You can stick the value of state into local or session storage, single-page apps do exactly that in practice. If you want to live in the cloud, you can stick state in Cloud Storage or equivalent...but you'd have to manufacture a key that uniquely identifies your client and this particular HTTP request. Not impossible but perhaps overkill for a simple scenario.
I am using MSXML6 in a vbscript-like code to download data over HTTP. But the server now requires connections to upgrade to HTTPS.
This is causing the xmlhttp object to fail with the error "msxml6.dll: Access is denied."
Set http = CreateObject("msxml2.xmlhttp.6.0")
http.open "Get", URL, False 'false is for 'async'
http.send
Using a sniffing tool, the operation stops after receiving the redirection-to-https response, and the error is generated without further details.
Requesting http://host/doc.php (plain http), the returned headers look something like this:
HTTP/1.1 301 Moved Permanently
Date: Fri, 19 Jul 2019 23:59:30 GMT
Content-Type: text/html; charset=iso-8859-1
Transfer-Encoding: chunked
Connection: keep-alive
Location: https://host/doc.php
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
However, if the requested URL is already https, the operation resumes normally without any complaint.
Is there anything I can do on the server side to convince xmlhttp to upgrade the connection to https peacefully?
Updating the code in the client application is out of the question as it is a legacy application with so many users out there using it, without an update mechanism.
Asking the users to update the URL adding an "s" after http is workable but too much hassle, as reaching them to tell them is also not easy at all.
Edit:
The conclusion is in this comment. The summary is that this is a client-side protection feature and it cannot be overridden from the server side.
The problem as mentioned in Xmlhttp request is raising an Access Denied error is you need to use the Server version of XMLHTTP that isn't restricted to accessing sites trusted by IE and restricted by the IE security policies. This is because XMLHTTP is designed for client-side whereas ServerXMLHTTP is specifically designed for server-side usage.
In ASP.net 2.0 Response.Cookies.Add() is a VOID (which boggles me somewhat). Is there a way to check to see if the function successfully added the cookie?
I just spent 2 hours trying to track down an authentication issue for one user. Finally I realized he was in a BUNCH of AD groups and I believe the cookie we were building for him, and trying to set, was > 4096 bytes.
Would be nice to know up front if the call to Response.Cookies.Add() failed.
We were trapping the error later in the global.asax page when we did:
HttpCookie authCookie = Context.Request.Cookies[cookieName];
and authCookie for this user was always null.
Thanks for the responses.
I've decide to check the size before calling Response.Cookies.Add() and alert the user accordingly.
int iSize = System.Text.UTF8Encoding.UTF8.GetByteCount(authCookie.Values.ToString());
if (iSize > 4096)
{
lblMessage.Text = "The authentication cookie cannot be set as it is > 4096 bytes; a limit imposed by the browser. The current size of the Cookie.Values is " + iSize.ToString() + " bytes.";
lblMessage.CssClass = "msgBox Alert";
return;
}
I setup a simple project that write a random cookie value, and used Fiddler to examine the response:
GET http://localhost.:2605/Default.aspx HTTP/1.1
Accept: */*
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: localhost.:2605
Pragma: no-cache
Cookie: ASP.NET_SessionId=wxpo32z2zxr1qlbwgx4on3z2; test1047139665=value775124204
You can see the cookie in the Cookie section of the header above. This is consistent with the documentation at: http://msdn.microsoft.com/en-us/library/system.web.httpresponse.cookies.aspx
The size limitation is documented here:
http://support.microsoft.com/kb/306070
Looks like you'll have to check yourself before writing it.
Yes, but likely not how you want to. On the subsequent page or request, check it in Request.Cookies if the cookie is available. If not, you can assume the Response.Cookies wasn't able to add the cookie (they have cookies off, cleared them, etc.).
You can't verify the Response.Add worked when it happens because it doesn't truly happen until the response is sent to the browser.
Response.Cookie.Add() succeeds always. It's the client accepting said cookie that might fail. But the Add() method has no way of knowing - since it just places the cookie in the HTTP response. You won't know until the HTTP response is sent back and the client processes it. Which happens long after Add() returns.
So the only way to verify that is to check for the cookie on subsequent requests, which seems like you're already doing by checking for the auth info.