Cookie not being set in browser - CORS/Port issue - express

Context:
I just split up my existing Next.js/Express app using Lerna, separating the Next.js server (serving the front end on localhost:3000) and my express server (serving the API on localhost:4000). As a result, I had to create an axios instance with the default {baseUrl: localhost:4000}, so my relative path calls will continue to work (ie. axios.post('/api/auth/login', {...})). This was all working before, when the server was serving both the API and the Nextjs app.
Problem:
After this switch, my Authentication flow no longer works properly. After logging in as usual, a page refresh will erase any memory of the user session. As far as I can tell, the Cookie is not being set. I cant see it in my dev-tools, and when the app makes a subsequent call, no cookies are present.
When my app mounts, it makes a call to api/me to see if the user is logged in. If they are, it will respond with the user information to hydrate the app with the users info. If they aren't, it wont do anything special. This request to /api/me no longer contains the persistent cookie set in place by the login call. (dev-tools shows the Set-Cookie header being returned as expected from the original login request)
Possibly Useful information:
Depending on how I run my app, (debugging in VSCode vs running both yarn dev commands in terminal) I will get a CORS error. I am using the cors package with no configuration: app.use(cors())
The call made to /api/me when the application mounts looks like this:
// API.js
`export default axios.create({baseURL: 'http://localhost:4000'})`
// app.js
import API from 'API'
API({
method: 'get',
url: '/api/me'
})
.then(r => {
//...
})
I am setting the cookie using this function
function setCookie(res, token, overwrite, sameSite = true) {
res.cookie(cookieName, token, {
httpOnly: true,
sameSite: sameSite,
secure: process.env.NODE_ENV === 'production',
expires: new Date(Date.now() + cookieExpiration),
overwrite: !!overwrite
})
}
Suspicions
It is some cors policy I'm not familiar with
It is because of sameSite (thought it wouldn't matter)

Related

Heroku Express / Nextjs client cookie not being set

So I'm having a bit of an issue where I have two apps hosted on Heroku the first being an Express application with the following cookie settings
const cookieSettings = {
maxAge: expiryTime,
...cookieOptions || {},
// For security these properties should always remain below the spread
httpOnly: true,
secure: process.env.NODE_ENV !== "development",
sameSite: "none",
path: "/",
}
And a Nextjs app which has some middleware that uses the cookie for login control to protect routes.
Locally I have no issues with the following login logic within the login route which sets the cookie browser side
const cookie = getCookie({ tokenOptions: { id: user._id } });
res.setHeader("Set-Cookie", cookie);
return res.sendStatus(200);
I have read there is issues with Heroku as it's on the public list of domains so the browser wont set if it comes from this but the issue I'm having is on a custom domain. My domain is https://www.mydomain.co.uk but for some reason I can't get it to set when I'm on this domain.
When using Postman I do get the cookie back but the domain comes from my API domain ie api.reviewcircle.co.uk which I think is why the browser isn't setting the cookie as the domains don't match but I can't find any info on how to fix this so would be great if anyone has any ideas.
You can see the cookie is included in the response but isn't set:

Nuxt SSR - i can't check if a user is authenticated

I'm trying to work on a Nuxt SSR frontend that uses a Django backend with Session Authentication.
I would like to have some SSR pages as well as client rendered pages in my frontend, so i'm using Universal mode.
The problem is that i did not find a working approach to check if a user is authenticated before loading a page, so i can't restrict pages to anonymous users. In order to check if a user is authenticated, Django will check if the request's headers contain a cookie, and according to that return if the user is authenticated or not.
Here is what i tried:
1) Middleware
export default async function ({context, redirect}) {
axios.defaults.withCredentials = true;
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//Redirect if user is authenticated
}).catch(function (error) {
console.log(error)
});
}
Here i'm sending a request to my backend to check if the user is authenticated. The problem is that the middleware is executed from server side, which means there will never be any cookie in the request, even if the user is authenticated. This means that every time i refresh the page, according to the middleware the user is always anonymous, even when the user is authenticated.
2) Plugin
export default function (context, inject) {
if (process.client){
console.log('client')
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//IF AUTHENTICATED, REDIRECT
context.redirect('/')
}).catch(function (error) {
console.log(error)
});
} else {
console.log('server')
}
}
Here i'm trying the same but with a plugin, and i'm "forcing" the plugin to check if the user is authenticated on the backend only when the plugin executes from client side. This works, cookies are sent in the headers and Django receives the cookie, but the problem with this solution is that Nuxt doesn't allow redirecting to other pages from a plugin (https://github.com/nuxt/nuxt.js/issues/4491).
3) Using beforeMount() in Vue
I tried to do that using beforeMount() from my Vue pages, but the problem is that since it will execute AFTER idration, the page will be loaded and after 1/2 seconds the redirect happens, which is kind of ugly.
Is it possible that there isn't a way to do this? Any kind of advice is appreciated
EDIT: the problem is not that i don't know how to code this, the problem is that when Nuxt sends a request to my backend from the server side middleware, the request will not contain any cookie, and because of this my Django backend cannot check the session cookie, which means that the backend cannot check whether or not the user is authenticated. The same code works when the middleware is executed from client side (if i navigate directly to the page instead of refreshing), because the request will contain the cookies.
I'm trying to understand if this is normal or not, but this could be an issue with Nuxt.
I know this a year old question and it was probably about nuxt 2, now nuxt 3 is out and running and I found my self with the same problem and here is how I solved it, just in case someone stumble here just like I did.
With Nuxt 3 server side you can use useFetch with the options headers: useRequestHeaders(['cookie'])
const { data, error, pending, refresh } = await useFetch(api.auth,
{
credentials: "include",
headers: useRequestHeaders(['cookie'])
}
);
There are a few issues you need to be aware of:
_ The cache, if you perform the same request with the same parameters it will return the same cached response (it won't even call the end point API). Try to use the key option with different values or the returned refresh method and check the doc "Data fetching" for more info.
_ The cookies, any cookie generate server side won't be shared with the client side, this means if the API generate a new token or session cookie on server side the browser won't receive those cookies and may generate new ones, this may get you in some 400 - bad request if you use session with CSRF, check this issue for more info.
I do have a working middleware with this
export default ({ redirect, store }) => {
if (store?.$auth?.$state?.loggedIn) {
redirect('https://secure.url')
} else {
redirect('https://login.please')
}
})

Set-Cookie not accessible through axios or fetch

I am building a web application with a go backend and a vue.js frontend.
I want to do a simple sign in form in which I send the sign in request from a method of my component with Axios (or fetch) and get in response a JSON object of the user and a session token in the cookie to be stored and reused in future requests to the server.
The code of my components method :
class LoginComponent extends Vue {
sendLogin (): void {
axios.post<User>('http://192.168.1.227:8080/signin', body)
.then(res => console.log('Axios Response :', res)
.catch(err => console.error('Axios Error :', err))
}
}
The part of the code of the go server :
go API
with the headers :
go headers
the front and backend are on different IP addresses in a local network and they communicate through HTTP.
The problem that I faced is that when receiving the response after the post request to login I don't have access to the cookie that has been set by the server. When I use Axios to analyze the response the cookie isn't in the headers whereas when I look at the network logs in the browser, the cookie is in the headers but it is not saved and it is not sent when I do another request.
Also, the only header that is visible with Axios is Content-Type : application/json; charset=UTF-8
I tried many things to be able to see this cookie but it doesn't work :
adding { withCredentials: true } to the axios request or axios.defaults.withCredentials = true to the axios instance only stops the request because of CORS.
changing all the Access-Control headers to "*" didn't change anything
using { auth: { username: 'foo', password: 'bar' } } in the axios options instead of the body
The only thing that worked and automatically saved the cookie was to send the request via the attributes of the form html tag, like so :
<form method="POST" action="http://192.168.1.227/signin">
...
</form>
But this way I am redirected to the JSON response object and not to one of my routes from vue-router and I can't access the User object in my app.
Is there any way that my problem can be solved?
Ok so the comment of Зелёный was the answer.
I needed the go server to set Access-Control-Allow-Origin: http://192.168.1.218:8080 (the address of the frontend) and then configure axios with { withCredentials: true } to be able to automatically store the cookie. Although I still don't see it when I do a console.log on the axios response, it is successfully stored and reused for each call to the server.

Lyft-API - GET from Localhost

I have been trying to figure out how to get this Vue project to work with the Lyft API. I have been able to get an Auth Token successfully created from the three-legged procedure, but I am unable to get the available drive types https://api.lyft.com/v1/ridetypes endpoint from the localhost:8080. It does work on Postman.
It keeps stating:
Access to XMLHttpRequest at
'https://api.lyft.com/v1/ridetypes?lat=37.7752315&lng=-122.418075'
from origin 'http://localhost:8080' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
I had tried doing a proxy using a vue.config.js file:
module.exports = {
devServer: {
proxy: {
'/lyftapi': {
target: 'https://api.lyft.com/v1',
ws: true,
changeOrigin: true
}
}
}
}
I been around other parts of Stack Overflow, and this is the closest thing to my problem, but no answers.
CORS error in Lyft API started recently
Any suggestions?
Axios Get Call
axios.get('/ridetypes', {
baseURL: 'https://api.lyft.com/v1',
headers: {
'Authorization': this.lyftToken,
},
params: {
lat: lat.toString(),
lng: long.toString()
}
})
If it means anything, I am able to make successful GET calls to retrieve Uber products, but not so much the Auth Token (unless its from Postman).
Lyft-API has disabled CORS, this means that browsers will block calls to api.lyft.com.
Vue won't be able to do anyting about this as this is a browser security policy.
Luckily there is nothing from stoping you to make this call from your own server.
One solution is to forward the request and response using your own server. You make a call to your server, the server makes a call to lyft, waits for the response and then responds your request.
This is not a vue only solution.

Feathers - Authentication and Authorization

I have created an app using Feathers. I've been using this app for a while. It successfully hosts a blog and some other web pages. However, I've now reached a point where I need to protect some of my routes. For example, I want to have a route for my administrative activitivies (/admin), but I only want specific users to have access.
I know that I need to use the authentication and authorization components. However, at this time, I'm stuck on the authorization. My goal is to authenticate using OAuth via Google. However, to get past my authentication challenge, I'd be happy with just using a hard-coded username / password just to get the /admin route locked down (no, it's not deployed).
At the moment, I have
const app = feathers();
const routes = require('./routes');
app.configure(configuration(path.join(__dirname, '..')));
app.use(compress())
.options('*', cors())
.use(cors())
.use(favicon( path.join(app.get('public'), 'favicon.ico') ))
.use('/public', serveStatic(app.get('public'), staticFileSettings ))
.use(bodyParser.json())
.use(bodyParser.urlencoded({ extended: true }))
.configure(routes)
.configure(hooks())
.configure(rest())
.configure(socketio())
.configure(services)
.configure(middleware)
.configure(authentication())
;
// Setup the authentication strategy.
app.authenticate({
type: 'local',
'email': 'admin#feathersjs.com',
'password': 'admin'
}).then(function(result){
console.log('Authenticated!', result);
}).catch(function(error){
console.error('Error authenticating!', error);
});
My problem is, as soon as I add the block of code with the app.authenticate stuff, I get an error when I start my app. The error says:
TypeError: app.authenticate is not a function
If I remove app.authenticate(...); My app starts fine, but nothing is locked down. In my ./routes/index.js file, I have:
app.use('/admin', function(req, res) {
res.render('admin/index.html', {});
});
Which, renders just fine. It's just not restricted to an authenticated and authorized user. What am I missing? At a bare minimize I'm trying to understand how to get past the app.authenticate error.
In order to protect a route from unauthorized access you need to follow the documented usage of express middleware provided by the feathers-authentication package that is installed when you do feathers generate authentication.
Here's an example of authenticating the /admin route.
const auth = require('feathers-authentication');
app.use(
'/admin',
auth.express.authenticate('jwt'), // <-- this is a strategy, can local/jwt... etc
(req, res, next) => {
console.log("Request for '/admin'...");
res.render('admin');
}
);