Cookie not being stored in browser after call with Axios - vue.js

I am playing with creating a simple authentication system and have a simple play project at: https://github.com/ericg-vue-questions/authentication
The main technologies I am using are:
FastAPI for the backend server
Vue for the frontend UI
axios to communicate from the frontend to the backend (eventually, a standard OpenAPI client)
I am make the axios call by doing:
axios
.get('http://127.0.0.1:8000/api/login', {
params: {
username: username,
password: password
},
withCredentials: true
})
.then((response) => {
console.log('Logged in')
console.log(response)
})
.catch(err => { console.log(err) })
On the backend, the code that sets the cookie is:
#app.get("/api/login", tags=["api"], operation_id = "login" )
async def login( username: str, password: str, response: Response ):
authenticated = False
if username in users:
if users[ username ][ 'password' ] == password:
authenticated = True
response.set_cookie( key="user", value=str( users[ username ][ 'id' ] ), max_age = 60 * 60 * 24 )
return authenticated
I can confirm that the cookie is being sent and received in the browser, but it is not being stored for some reason. I thought that the only thing that was required was to set withCredentials: true in the axios get request, but that doesn't seem to be working. Any idea what I might be doing wrong?
My Response headers are:
HTTP/1.1 200 OK
date: Sun, 15 Dec 2019 01:54:14 GMT
server: uvicorn
content-length: 4
content-type: application/json
set-cookie: user=userid; Max-Age=86400; Path=/
access-control-allow-credentials: true
access-control-allow-origin: http://localhost:8080
vary: Origin

I'm pretty sure that's because you are not setting a path, for example '/', to your cookie. That's the fourth parameter in set. If you're using your cookie in a https connection, you should also set a domain.

Related

Why is browser using cached response headers if they are not present in the actual response?

I realized that a response header would be present in the Network tab of the Chrome console even if that header wasn't set in express. I found this answer suggesting disallowing caching. What confuses me is why the cached response is still used even if a request is made to the server.
request from react
const baseURL = 'http://localhost:3001'
const axiosClient = axios.create({
baseURL,
withCredentials: true,
})
let accessToken
axiosClient.interceptors.response.use((response) => {
const { data, headers } = response
//store access token in memory
accessToken = headers['x-access-token']
console.log(accessToken)
// if (accessToken) axiosClient.defaults.headers['X-Access-Token'] = accessToken
return data
})
async me() {
return await axiosClient.get('/auth/me')
}
request reaches route
router.get('/me', (req, res) => {
// res.set('X-Access-Token', 'test 4')
res.send('me')
})
vscode debug console
res.getHeaders()
{x-powered-by: 'Express', access-control-allow-origin: 'http://localhost:3000', vary: 'Origin', access-control-allow-credentials: 'true', access-control-expose-headers: 'X-Access-Token'}
req.headers
{host: 'localhost:3001', connection: 'keep-alive', sec-ch-ua: '"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"', accept: 'application/json, text/plain, */*', sec-ch-ua-mobile: '?0', …}
old token still appears in chrome
Since Chrome made a request to the server instead of just using the cached response (First, since there is no need to deliver the request to the origin server, then the closer the client and cache are, the faster the response will be), why isn't Chrome using the received response where the token header isn't present?

Safari doesn't send cookie to Express when requesting image via p5 loadImage()

Background
I set up an Express.js app behind a proxy to let users to login before being directed to a web app. This app is failing to serve up some images in Safari (macOS/iOS) because Safari is not sending the cookie back with requests for images that originate from the loadImage() method in my p5.js code. This does not happen on Chrome (macOS).
When I load the page, the browser requests the resources fine. But requests originating from my application returns a different session, which is not logged in, and gets caught by Express:
// Request for the main page by the browser
Session {
cookie:
{ path: '/',
_expires: 2020-05-04T16:26:00.291Z,
originalMaxAge: 259199998,
httpOnly: true,
secure: true },
loggedin: true }
// Request for image assets by a script in my application
Session {
cookie:
{ path: '/',
_expires: 2020-05-04T16:26:00.618Z,
originalMaxAge: 259200000,
httpOnly: true,
secure: true } }
HTTP Requests from Safari
GET https://mydomain/app/img/svg/Water.svg HTTP/1.1
Host: mydomain
Origin: https://mydomain
Connection: keep-alive
If-None-Match: W/"5e6-171c689d240"
Accept: image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5
If-Modified-Since: Wed, 29 Apr 2020 15:24:13 GMT
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
Referer: https://mydomain/app/
Accept-Language: en-us
Accept-Encoding: gzip, deflate, br
HTTP/1.1 302 Found
Date: Fri, 01 May 2020 06:50:07 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.3.17
X-Powered-By: Express
Location: /
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 23
Access-Control-Allow-Origin: *
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Found. Redirecting to /
Express app
The app is set up behind an HTTPS proxy, so I set the Express Session object to trust proxy and set security to auto (setting to false doesn't fix the problem):
app.set('trust proxy', 1)
app.use(session({
secret: 'my-secret',
resave: true,
saveUninitialized: true,
cookie: {
secure: 'auto',
maxAge: 259200000
}
}));
When the user signs in, it is sent to /auth to check against the database
app.post('/auth', function (request, response) {
var user = request.body.user;
var password = request.body.password;
if (user && password) {
connection.query('SELECT * FROM accounts WHERE user = ? AND password = ?', [user, password], function (error, results, fields) {
if (results.length > 0) {
request.session.loggedin = true;
// If the user logs in successfully, then register the subdirectory
app.use("/app", express.static(__dirname + '/private/'));
// Then redirect
response.redirect('/app');
} else {
response.send('Incorrect password!');
}
response.end();
if (error) {
console.log(error);
}
});
} else {
response.send('Please enter Username and Password!');
response.end();
}
});
They are redirected to /app when logged in:
app.all('/app/*', function (request, response, next) {
if (request.session.loggedin) {
next(); // allow the next route to run
} else {
// The request for images from my p5 script fails and these request redirect to "/"
response.redirect("/");
}
})
Question
What can I do to ensure Safari pass the session cookie with its request so that Express will return the correct asset?
Edit
Including the function that invokes loadImage(). This is embedded in an ES6 class that loads image assets for particles in a chemical simulation. This class must successfully resolve promises so other higher order classes can set correct properties.
loadParticleImage() {
return new Promise((resolve, reject) => {
loadImage(this.imageUrl, (result) => {
// Resolves the Promise with the result
resolve(result);
}, (reason) => {
console.log(reason);
});
})
}
Edit #2
Including the headers for a successful request directly to the URL of the image asset:
GET https://mydomain/app/img/svg/Water.svg HTTP/1.1
Host: mydomain
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Cookie: connect.sid=s%3A2frOCv41gcVRf1J4t5LlTcWkdZTdc8NT.8pD5eEHo6JBCHcpgqOgszKraD7AakvPsMK7w2bIHlr4
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Safari/605.1.15
Accept-Language: en-us
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
I suggest using express's static middleware to serve static files. With this, you won't need any session to get images, js, css, etc. Also, it accelerates your application. You need to place
app.use(express.static( ... ))
before the app.use(session( ... )) statement if you want some additional perfomance, because if you do, express won't attepmt to creare session for static files.
The fetch() call in the source code for that loadImage() function is not setting the credentials option that controls whether cookies are included with the request or not, therefore they are not sent with the request.
Do you really need authentication before serving an image? If not, you could rearrange the way you serve images in your server so that they can be served without authentication using express.static() pointed at a directory that contains only resources that can be served without authentication. If they do need to be authenticated, you may have to patch the loadImage() code to use the credentials: include option or load your images a different way.

Cannot sent JWT in headers to the server

I'm learning a fullstack app having Vue as front-end, express as back-end and Mongodb.
I have login form and did a simple authentication in the back-end, after user is authenticated i generate JWT and store in user browser, In user's route navigation guards i make a request to sent that token to be verified in the server before let user in protected route
{
path: '/user',
name: 'User',
component: () => import('../views/User.vue'),
beforeEnter: (to, from, next) => {
let token = localStorage.getItem('access_token')
axios.post('http://localhost:5000/authorize', {headers: {
Authorization: `Bearer ${token}`
}})
}
}
But i couldn't sent headers which included token to the server, it got undefined So how to do this properly ?
In order to authenticate, your header must have a key of Content-Type and a Value of application/json
Try using Postman to login. You should receive a JWT ${token} in the response.
Once you are authenticated, all of your requests must include the following Key/Value pairs in the header:
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
Authorization: Bearer ${token}
Note: You must include the text "Bearer " (with a space) in the Authorization header and it is case-sensitive.

How to fix SLO with passport-saml that works on first logout but not on subsequent logouts

I have to connect an application to my company's ADFS server. I am using passport-saml for SSO and SLO. SSO works, and SLO works on the first logout only. I am trying to make SLO work every time a user logs out.
I have been searching high and low for a solution to this problem, but it evades me. Here's the details:
I clear the cookies in the browser to start with a clean slate.
I login to my application which redirects to ADFS' login page
Enter user credentials and then ADFS redirects back to my app's homepage
I log out of my app and a request is sent to the ADFS server killing my session locally and on ADFS, I am then redirected back to my app's homepage
I log in again and this works as intended
I logout but this time I am sent to my ADFS server's logout page.
Further inspection shows that ADFS is not clearing its cookies so the ADFS session stays live.
I have used Firefox's SAML viewer plugin to watch what is happening and here are my findings:
On a successful logout:
HTTP:
GET https://myadfs.org/adfs/ls/?wa=wsignout1.0 HTTP/1.1
Host: myadfs.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://example.com/dashboard/data
Connection: keep-alive
Cookie: MSISAuth=AAEAAMVBaN7qo03wm/4jDH9e/tZ6ih6HN++2S7c7c0aXHK1RYIZ5++4Y7pf3g4v+OdRUzcJgOROfZkXx0tSEeCOfJFMluodJiSYsESiJnidVcR7Os/iHkNqIp88qGG7UZj+l8NYyvsO/7soTyQGkbMqoI0Z+0z+xXz2CZgOxsqWcjJ3FmTR32bsMR8Lra77XI2KyKycFiNYdYJ2dSKC7yBdxBRKHB7LAs4DOJKAtOt//IWspe9zPbju+x6chgP0dKToyfqX6m4EwlQnbHG4hmCImtXrEDytx1rbuLiBC7N56Y9WmGBTht5vgYvVEoA2cRqBbNYK+HoonL6+oBIJdba6+XZ2lBQsO/yJowvaHxPM8wgwLBknSt39RswaSdGjrI18CcgABAAB/eeLBPuQ9dk6ItCeTem38XttX/PQPLi52Ts+ZQGYHxs4VsO1EMe7EgMGYThPGlMCDcmS9ouXOSh6yW/LiL1jTuhc2/jhq3X0jWY+XPOSXtp81mineHeNv8SWsFjggzh5AymLtPPrPUYT6ihj9fcbJymqatsZMI5B5h0gxS2LaUUWjJyRxpMIyQXEpLSx1mxU5psQrj5/nGpOiq98uy8HE4kJp+Ey9uugSZQXhn9NwY+EqqmWxf6LDrCaeMLFDIX6mlgqu2eTLrUA9gNIJ4kSOC/5Rtw4JQVJpSeQuMom6kCHFEvZo/57BIhGkgWR8vNNCguHzZeB+as0xxfxmmb9SgAMAAMVFqaMXn0uG8+IGJIfxdIIoJ7EsLqV7so7WnFT/4OxfLzsXlO2flq0vcEbasLuLoqhGFaOuy1dkq/ft9se6Pv6rQfH7Esk/aMey/cKObBUPkcZAUFtQxXD7MSLScsiVnq3hHjrpZzEnMTToVkA9Zjv3i72Wv20tdE658+7O1olibavPPIT7Z5syoQNa1rjOAaXcPlM5hbbjXm7BiXx37ZEnvxwpY1Mf4Yocvgd9kMoApciDB2csbTf4GEic7MKeAI2G5KpwArY7g+zt4BJud+F/xnyuwVPpwPVEiNbHQnAogh5NoMDwRx+macTdkHku4AdNvruS/4L/aUHcEhPlhu3j/7r9kP1EnRso12NP1AWipsGlmpdAjoIXfK0+NBqJnDq0KwSEcvJ38OI6Z1FVkRWySi8br8pjtcytFhdh5RTkpD8FVQZ/RnGC1XE4q4IJhxMBlE1Kd8PNh3p85qpoX6r2I36a3knwK2dkm7pb0XNVwhxhC5DGpaB2iNo86CGi+BX4rICBGkNgyrOW/aWKpIhLu0bo1IDVQJw7MORdROJJk/o81E15HuC2g4r3ch+IvZOXKfAenGYM2mYrgnSRHLD0p7KsDN0vuU3IdLXAL5/D5ezr3WQFDFXPpRJyQ+qfx8kyUCe/vtvEVaNezHzOKosQsNGwSvp+lHrEGA9LLYM8RkU/Vwshgkeq2H8MoyuDRaxgOoudNGOmvwNfMp9BoOsz8OCDA5R2BB+JXzsEkSpNYebJK+VWm5wOcYnJ2j9y1OKjRU1ICRtsSPG5kLWmYUt8hHsswzrj4UAxpks+Dn2S09YzeOudC5ss5hmTM/UeVG3r3kJ9+Ad7716V9g7016u+XGhfSWty8EPxVAg0qV9wwAIk+FliWFdF1OLY1RODcsS3swqYfMrBWWdULVNl5d36ycFGucaP893o4Q/im7tx2+588lfvPbZO+DkP40MHP9Hwe++ra6kDiQx5si4M16zYIMmxa4nq6XVcr2hFlqbsLQjhIqkiFOCkt9LNRdKNZlghQkspUH44qLBq4sTHK0iD13FFmBs5rEE1CWa89oCELhea/Z9hPEtjPpC3Q52cAXBgbOJCTr6OYFYfQKbATqHdTU09/nJOafMK5ID1pf7pmBL+ZTH7Kl64lxhyO/9F84t47TctQhhFqxgsIxmv+ZVHajanNl4E0gXqJ0ULsY2h; SamlSession=aHR0cHMlM2ElMmYlMmZmcGNkcmRldi5tb2ZmaXR0Lm9yZyZGYWxzZSZDdWtyYXNTRCYmJiYmXzFkZjY4M2RhLTM4NTktNDVjNS04ODNkLTA3NmRiYTdiMjk3Yg==; MSISAuthenticated=NC8xNi8yMDE5IDExOjI2OjI4IEFN; MSISLoopDetectionCookie=MjAxOS0wNC0xNjoxMToyNjoyOFpcMQ==
Upgrade-Insecure-Requests: 1
HTTP/1.1 302 Found
Content-Length: 0
Content-Type: text/html; charset=utf-8
Location: https://example.com:443/login?SAMLRequest=lZLfa4MwEMf%2fFcl71KjxR7BCqS9C18I69rCXEjXpZJq4XCz982crY6yMwh7vuO9973N3OfChH9lWn%2fRkn8XnJMA6VblCR%2bpzQqWUOKWE4igMUlz7nGKexHUYJdSnKUXOqzDQabVCgesjpwKYRKXAcmXnlE8y7EeYxC%2bEsCBmYeamCX1DTjm7dIrbm%2fLd2hGY58mxaU0rzu6gpeysdbU5eb0%2bdQo5G61AXHtORjHNoQOm%2bCCA2YYd1k9bNtuzZilik4JRNJ3sRIucnbZ7tTdraYW5HykkPyNdhl4Bu23jsctotNWN7lGR33DNIn0s4gDCXHFRccWdac04Auh7XN5K8ObSc9cI8KyZwObeYlPku7ltVf7TbjN9GA6HMvcWeZEvFz8IuB6uUq24FEfSyjgNW47DlGY4og3F6RxjP4nbmid1kCV17v2h%2fE7%2beqDiCw%3d%3d&Signature=pT%2fSUpslARJlvOCah5VzZk4stZLIREyHmUFOO4siHUbkL5eJG4QsfYj9Pq%2bwxnOaPaevYkmiXq0rft3drTzJHspns9UbucyYQvEaSAZVmRTTyfPC3Z0EgVGSvtr0JL3nuDPsq2IfbToseuQQtJFsA%2b94D8KtaLjtUJxiMcQMHyg2yR00Ac3NGt9AsRg1X73X%2frt0XZDN9bSt4R8t%2bt2Yl2UsZsL4GHTGk7RbN3AUrYHsLtKeuN07umXqX3otVtHo%2f9tx2w2h1glYycYbFCk%2bWjox8Mej%2fiLLkpAhw9EXlhiTGrEJ2%2bcYvnQxGokOsz2vXEOoc3%2fhle27LuTPFMN9yw%3d%3d&SigAlg=http%3a%2f%2fwww.w3.org%2f2001%2f04%2fxmldsig-more%23rsa-sha256
Server: Microsoft-HTTPAPI/2.0
P3P: ADFS doesn't have P3P policy, please contact your site's admin for more details
Set-Cookie: SamlSession=; expires=Mon, 15 Apr 2019 11:26:39 GMT; path=/adfs
SamlLogout=aHR0cCUzYSUyZiUyZnJwcHNzb2Rldi5tb2ZmaXR0Lm9yZyUyZmFkZnMlMmZzZXJ2aWNlcyUyZnRydXN0Pz8/aHR0cHMlM2ElMmYlMmZmcGNkcmRldi5tb2ZmaXR0Lm9yZyZGYWxzZSZDdWtyYXNTRCYmJiYmXzFkZjY4M2RhLTM4NTktNDVjNS04ODNkLTA3NmRiYTdiMjk3Yj9fNTBhMTVmZmYtODUxNS00MzI4LWIwYTUtYTc2YjM0NzUwNTg1P3VybiUzYW9hc2lzJTNhbmFtZXMlM2F0YyUzYVNBTUwlM2EyLjAlM2FzdGF0dXMlM2FTdWNjZXNz; path=/adfs; HttpOnly; Secure
MSISAuthenticated=; expires=Mon, 15 Apr 2019 11:26:39 GMT; path=/adfs
MSISAuth=; expires=Mon, 15 Apr 2019 11:26:39 GMT; path=/adfs
ReturnUrl=aHR0cHM6Ly9ycHBzc29kZXYubW9mZml0dC5vcmc6NDQzL2FkZnMvbHMvP3dhPXdzaWdub3V0MS4w; path=/adfs; HttpOnly; Secure
MSISSignoutProtocol=U2FtbA==; expires=Tue, 16 Apr 2019 11:36:39 GMT; path=/adfs; HttpOnly; Secure
Date: Tue, 16 Apr 2019 11:26:39 GMT
SAML:
<samlp:LogoutRequest ID="_50a15fff-8515-4328-b0a5-a76b34750585"
Version="2.0"
IssueInstant="2019-04-16T11:26:39.875Z"
Destination="https://example.com/login"
Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified"
NotOnOrAfter="2019-04-16T11:31:39.875Z"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
> <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">http://myadfs.org/adfs/services/trust</Issuer> <NameID xmlns="urn:oasis:names:tc:SAML:2.0:assertion">USERNAME</NameID> <samlp:SessionIndex>_1df683da-3859-45c5-883d-076dba7b297b</samlp:SessionIndex> </samlp:LogoutRequest>
On subsequent, unsuccessful logouts:
HTTP:
GET https://myadfs.org/adfs/ls/?wa=wsignout1.0 HTTP/1.1
Host: myadfs.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://example.com/dashboard/data
Connection: keep-alive
Cookie: MSISLoopDetectionCookie=MjAxOS0wNC0xNjoxMToyODoyNlpcMQ==; SamlLogout=aHR0cCUzYSUyZiUyZnJwcHNzb2Rldi5tb2ZmaXR0Lm9yZyUyZmFkZnMlMmZzZXJ2aWNlcyUyZnRydXN0Pz8/aHR0cHMlM2ElMmYlMmZmcGNkcmRldi5tb2ZmaXR0Lm9yZyZGYWxzZSZDdWtyYXNTRCYmJiYmXzFkZjY4M2RhLTM4NTktNDVjNS04ODNkLTA3NmRiYTdiMjk3Yj9fNTBhMTVmZmYtODUxNS00MzI4LWIwYTUtYTc2YjM0NzUwNTg1P3VybiUzYW9hc2lzJTNhbmFtZXMlM2F0YyUzYVNBTUwlM2EyLjAlM2FzdGF0dXMlM2FTdWNjZXNz; ReturnUrl=aHR0cHM6Ly9ycHBzc29kZXYubW9mZml0dC5vcmc6NDQzL2FkZnMvbHMvP3dhPXdzaWdub3V0MS4w; MSISSignoutProtocol=U2FtbA==; MSISAuth=AAEAAFOnxdlEvO8Le/Gti39Bx6BFj1cEJ39/A6ogocbLbXlBnq07uT1v+MuAzZs0NqyB1Wmqx3O8oTwPancFPCEFrQbngzsvsWI/oAXmuDih8uBG9MVPfstAu/cFPXL95V2IIUjX6r3Tv08FqipxW/1CHa7QM8XvXU5a516zFsZTaxke+ITD3B+nGPsuQY+oVG47NhtoMHmCrbShjOBd9Wn6Q5FzDqbHlxD/5czDUXixYf8gg+MTNq9W+oT5J7TF6NaBb7o1QojY7c8UoJ4fQONwlMNE17TgGVomqN4N9qVPTShGSaTlM8C+er9SOWQiALfZHvH2sv8N0AIn9qpivuCzw9WlBQsO/yJowvaHxPM8wgwLBknSt39RswaSdGjrI18CcgABAAAAz9AfrV1onudL+YY+0zL4vWeCboTECwksETafeI44/o0n0DEBx8kVGELmmPqSKD216OFB+p4k0K//HTW+YnRiuFpk1dAnN+dmwirgwzohFU1A3lWq0pQcHFyui1xs1UHnzDZokvK+7r859oZP0XZ4pGGTZsjWyc2B32FgwfvpiKYKDsWALpajW9FRDnt1VnGyDSzsN3V6vQHmKIEBZn5wb3+b3DtB9hV/ZssxiE7Xf8V8l+144wE71YH4ETNbcX0VXKNlkL9x5R+EThMlzyNl2tAcGWSk+3xM3lhfTm3+8y5GEP3rtJjLQGZSPKUljPcZM/MU3EX3YRrCkYsAyhgpgAMAAKGsYkEEca74go1dVexUCjdky1zUJMng5a/ZmKCRWTYsPT2DCjR579a0Hr69s8nl36p8EgyqnyXPm/uiFp+LPp1CuCCuXe/QYFoySixCOEcJsnRbikBEAP/Bpj5UUifnqgyO7MHH1GQiXeOlw2llsPu7rdNiEqB4X6Hqhnn6xaasl+5iqvNkZSTi8DSQc/24MRT4VsAcJcO7eqxjQBluWr2cyvdr9pn4GigQ05WaXWfogo3BwPJzLUo+NngvLHfxyn1wDmUYghc+oxS+vJwTadiiSDDzrcTVTuVxw2xj6OVi8DXbyRii5+VTKolRK0qCa/4C4BCzOOGUkooktX/GecV6eNuk8xOdLsiybY9Ah5Z2WVgraDntw/w/pP/ij4v0jDLvDQjU+BIfGOpeV1jcG9VDObir5GYGfOm59DtlRpoy/kpjiDLWI8EE75DEFlhomeae0v4xBQ6XqgVd5lEcA2DTm/3Ophg31FA2M5J65yE4t7W7inIC4XjMWFOu3GCMse7ERYyFbq59vf+iSs6eyev7wXidvAekALmq6Gk2Ths2JR1TbV27E2+kgGhmvlgiShx67E9s2wrBfPKvV7+IMS9Xe1YPKpZAlfCwnkbQNonqAMQH5LsHq1K7DWrNTcon10TiOtlMbzin8FtNphcnChHYmBbDxpqrf5xwwYXbyznQnMfeDnjN7aPo909gwhfUGNltLTOZ81m6k9c3Z0C8ugvL61bbw3Ku42OZiOnoVcEYjf50bMWZQl/hUMlRp+uHVNhK41z6U2O9Ph7S4ZI4wg7z33Z+VCp+08HpMRqrX155atJYVX73mnr3+J4rKvyJvjglb9aA333MUOC7iGMDDNImibvofyhbqK3VO+zqyPYj0R4OvhnA9RlvV10MWDhn5qnVevA5Oo1MQNPGnTLtfRZXpB8oa2bZZMh62XO4a5gZ/ioNsigiDAFKbQnx0wvBTb0uqYSZpfxoA4K2o87swOYB81FTkQNBnNZG171szH89jijOuEAI7hAWdAnM2LjagGZwWpuF2yHbJqQqsGzjvnqbQ6yMTvaEbkooSelFEBeRW2Gg5rGAjj5Pvs+T0ljhVlby6FfFKJ71NDBvn/7PGIglARSZqUZcAuthlhr8pta11WnhsfnyumvLfWvOZHZZjWslKMLBpGEBe1WgcYBUBYUrUeHmCqDRy5Zc4KJXwGrY; SamlSession=aHR0cHMlM2ElMmYlMmZmcGNkcmRldi5tb2ZmaXR0Lm9yZyZGYWxzZSZDdWtyYXNTRCYmJiYmX2NlNDAwODQxLTA2ZDItNDI3Ni05MTRlLWU5N2ExYWRlZmQzZQ==; MSISAuthenticated=NC8xNi8yMDE5IDExOjI4OjI2IEFN
Upgrade-Insecure-Requests: 1
HTTP/1.1 200 OK
Cache-Control: no-cache,no-store
Pragma: no-cache
Content-Length: 8957
Content-Type: text/html; charset=utf-8
Expires: -1
Server: Microsoft-HTTPAPI/2.0
Date: Tue, 16 Apr 2019 11:28:45 GMT
SAML:
NO SAML SENT
You will see that on a successful logout ADFS sets the cookies to clear them while an unsuccessful logout does not. Also, the unsuccessful logout does not send the SAML logout request.
Lastly, when I clear the cookies in the browser, the first login/logout session will work as intended again, and all subsequent logouts will not. I can see the cookies are retained on subsequent logouts as ADFS is not getting the SAML logout request. I just don't understand how this works on the first logout but not the following logouts. I have looked in and out of passport-saml's code but can't seem to find the issue.
Any assistance would be great.
Here is my passport.js setup:
const fs = require('fs');
const passport = require('passport');
const SamlStrategy = require('passport-saml').Strategy;
require('dotenv').config();
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new SamlStrategy({
entryPoint: 'https://myadfs.org/adfs/ls',
issuer: 'https://example.com',
callbackUrl: process.env.NODESERVERURL + ':' + process.env.PORT + '/authenticate/adfs/postResponse',
privateCert: fs.readFileSync(__dirname + '/private/keys/fpcdr.key', 'utf-8'),
logoutUrl: 'https://myadfs.org/adfs/ls/?wa=wsignout1.0',
signatureAlgorithm: 'sha256'
},
function(profile, done) {
const username = profile.nameID.toLowerCase();
const email = profile['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'].toLowerCase();
const sessionIndex = profile.sessionIndex;
return done(null, {
username,
email,
sessionIndex
});
})
);
module.exports = passport;
passport callbackUrl:
module.exports.adfsAuthenticate = function(req, res) {
const email = req.user.email;
const username = req.user.username;
if (process.env.UAT === 'true') {
res.status(302).redirect(LANDING_PAGE_REDIRECT_DEV);
} else {
res.status(302).redirect(LANDING_PAGE_REDIRECT_PROD);
}
};
adfs logout:
module.exports.logout = function(req, res) {
req.logout();
req.session.destroy(function (err) {
if (!err) {
res.status(200).clearCookie('connect.sid', {path: '/'}).json({status: "Success"});
} else { alert(err); }
});
};
I have observed that there is one cookie which gets stored in browser called "MSISSignoutProtocol", if this cookie exist, the logout does not work as expected on subsequent requests.
To get it working, you might need to implement proper SAML logout which most IDP support.
Here is my solution, shared here
I found out why SLO doesn't work. I found a solution to make it work properly, im my case with Microsoft Azure (SAML-ADFS), using a database session storage. I log in from my app. Then I log out from microsoft azure ( not from my app), and it logs out nicely !
I think important to mention that there is a huge difference between the IDP logout request with all others. Most of the time, passport routes handle user device's initiated requests or redirects, That means the device's cookies comes along theses requests, which allows passport-saml to successfully retreive device's user information.
When the IDP broadcasts a logout request to relevant ISP(s), it is not always a device request (either initiated nor redirected). It is a simple HTTP GET request that contains a SAMLRequest. This SAMLRequest contains what the IDP uses to identify a saml user.
A saml user is identified using nameID and nameIDFormat profile properties.
Therefore, when a user logs in through an IDP, the app must store both values, nameID and nameIDFormat. You can acheive that first in you saml strategy authentication handler :
export const samlStrategy = new SamlStrategy({
...samlStrategyOptionsHere,
logoutCallbackUrl: 'https://your.isp.com/saml/logout'
},
(profile: Profile | null | undefined, done: VerifiedCallback) => {
// do login process. On successful login, don't forget
// to pass along nameID and nameIDFormat profile values !
done(null, {
userId: user.id, // in my case, my app just need to store the user id
nameID: profile?.nameID,
nameIDFormat: profile?.nameIDFormat
})
});
Then store nameID and nameIDFormat in passport user's session ( within serialization process ) :
interface SessionUserData{
userId: number,
nameID?: string,
nameIDFormat?: string,
}
passport.serializeUser<SessionUserData>((user: any, done) => {
// do serialization stuff
done(null, {
userId: user.id,
nameID: user?.nameID,
nameIDFormat: user?.nameIDFormat
})
});
SLO requests can then be handled properly by first retreiving passport sessions that holds the specified saml identification, and destroy them manually, like this :
interface SAMLLogoutExpectedResponse{
profile?: Profile | null | undefined;
loggedOut?: boolean | undefined;
}
authRouter.get('/saml/logout', async (req, res)=>{
// req.user may not exists here, since this is a single HTTP-GET request that contains a SAMLRequest
// initiated by the IDP, not a user device redirection
// use passport-saml to validate this request
const response:SAMLLogoutExpectedResponse = await (samlStrategy._saml as SAML).validateRedirectAsync(req.query, url.parse(req.url).query)
// check the « SAML USER » profile
if( response?.profile &&
response.profile?.nameID &&
response.profile?.nameIDFormat &&
response.loggedOut===true ){
// get related session(s) if exists
const sessionsToDestroy:string[] = ((await knex.from('sessions').where(true).select('*')) ?? []).reduce((acc:any[], session:any) => {
const userData:any = JSON.parse(session?.data ?? {})?.passport?.user
if( userData &&
userData.nameID &&
userData.nameIDFormat &&
userData.nameID == response?.profile?.nameID &&
userData.nameIDFormat == response?.profile?.nameIDFormat
){
acc.push(session.session_id)
}
return acc;
}, [])
if(sessionsToDestroy.length){
// destroy database session --> which invalidate any corresponding cookie on any device (if still exists)
await knex.from('sessions').whereIn('session_id', sessionsToDestroy).delete();
}
}
// if this is a IDP request, all ends here
// if user initiated the logout request, IDP will redirect device here. Send the device somewhere coherent after successful logout
res.redirect('/')
});
Voilà. I know this is a precise case, and doesn't fit to everyone. But the most important point is that a SLO callback must handle nameID and nameIDFormat values. It should not handle a device session.

Does Axios support Set-Cookie? Is it possible to authenticate through Axios HTTP request?

I'm trying to authenticate express API back-end using Axios HTTP request call.
I was able to see 'Set-Cookie' in the response header, but cookie was not set. Is it possible to set cookies through Axios HTTP calls?
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 355
Content-Type: application/json; charset=utf-8
Date: Fri, 28 Sep 2018 05:59:01 GMT
ETag: W/"163-PAMc87SVHWkdimTJca7oRw"
Set-Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; Max-Age=3.6; Path=/; Expires=Fri, 28 Sep 2018 05:59:04 GMT; HttpOnly
X-Powered-By: Express
Try this out!
axios.get('your_url', {withCredentials: true}); //for GET
axios.post('your_url', data, {withCredentials: true}); //for POST
axios.put('your_url', data, {withCredentials: true}); //for PUT
axios.delete('your_url', data, {withCredentials: true}); //for DELETE
For more information on this from the axios docs:
"withCredentials indicates whether or not cross-site Access-Control requests should be made using credentials" - https://github.com/axios/axios
More detail on withCredentials:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
Yes you can set cookies by Axios. The cookies need to be passed into the headers object. You can send cookies in a get/post/put/delete/etc. request:
As suggested by Aaron:
axios.get('URL', {
withCredentials: true
});
axios.post('URL', data, {
withCredentials: true
});
axios.put('URL', data, {
withCredentials: true
});
axios.delete('URL', data, {
withCredentials: true
});
Or you may also try this:
axios.get(url, {
headers: {
Cookie: "cookie1=value; cookie2=value; cookie3=value;"
}
}).then(response => {
console.log(response);
});
In case anyone else faces the problem I've had,
Here's a repost of my answer on a similar question https://stackoverflow.com/a/62821342/8479303
In my case, the network panel showed that the response had the 'Set-Cookie' header, but in axios the header wouldn't show up, and the cookie was being set.
For me, the resolution was setting the Access-Control-Expose-Headers header.
For explanation, from this comment on an issue in the axios repository I was directed to this person's notes which led me to set the Access-Control-Expose-Headers header -- and now the cookie is properly setting in the client.
So, in Express.js, I had to add the exposedHeaders option to my cors middleware:
const corsOptions = {
//To allow requests from client
origin: [
"http://localhost:3001",
"http://127.0.0.1",
"http://104.142.122.231",
],
credentials: true,
exposedHeaders: ["set-cookie"],
};
...
app.use("/", cors(corsOptions), router);
It was also important that on the axios side I use the withCredentials config in following axios requests that I wanted to include the cookies.
ex/
const { data } = await api.get("/workouts", { withCredentials: true });
I tried setting withCredentials: true but was still getting this error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4000/users/register. (Reason: CORS request did not succeed).
CORS was configured to allow requests from the frontend port.
I had to change the default options for axios like so:
axios.defaults.withCredentials = true
And the issue was solved. No error and Set-Cookie working as expected.
cookie can't be touched, the thing is it gets bundled to request object after appended to the response object.
function sign(req,res){
res.cookie("x-token", signed, { maxAge: (new JWTService().jwtExpirySeconds *
1000) });
}
client after receiving this response just have to continue with requests, set-cookie in the name of "Cookie " will be bundled to those request, like this
caveat: when http cookie expires its is automatically removed and not bundled to request there after.