I am unable to retrieve a cookie that I sent earlier.
As part of login, I sent back a refresh token as an httpOnly cookie.
const payload = {name, email};
console.log("payload: ", payload);
const accessToken = jsonwebtoken.sign(payload, process.env.ACCESS_TOKEN_KEY, { expiresIn: '15m' });
const refreshToken = jsonwebtoken.sign(payload, process.env.REFRESH_TOKEN_KEY, { expiresIn: '1d' });
console.log("Access Token:", accessToken); // access token is generated
console.log("Refresh Token:", refreshToken); // refresh token is generated
res.cookie('refreshToken', refreshToken, { httpOnly: true, secure: false, sameSite: 'Lax', maxAge: 24*60*60*1000 }); // call succeeded. what is the name of cookie?
res.json({ accessToken });
Later on a refresh endpoint I look for a cookie and can't find it:
export const handleRefreshToken = async (req, res) => {
console.log("Request Cookies", req.cookies);
const cookies = req.cookies;
if (!cookies?.refreshToken) return res.sendStatus(401);
I see the following cookies:
_ga: 'xxxxxxxxxxxxxxxxx',
_gid: 'xxxxxxxxxxxxxxxx',
_gat_gtag_UA_xxxxxx: 'x',
_ga_QPY49S2WC4: 'xxxxxxxxxxxxxxxxxxx'
This is on my dev environment with nodejs running on localhost:5000.
Update: Using devtools (Network) I see the cookie in the response on the client side. The name of the cookie is 'refreshToken'. However, the cookie doesn't show up on the browser when I look at the cookies on the browser. Perhaps, the cookie isn't being retained on the browser. Any suggestions on why that could be?
Update2: The link provided by #Konrad Linkowski worked. When the axios request is made from the client, I needed the option "{ withCredentials: true }".
The error was on the client end. The express code functioned correctly. This link explains it: Does Axios support Set-Cookie? Is it possible to authenticate through Axios HTTP request?
My original call on the client side (using axios) was:
const res = await axios.post('/login', { ident: email, password });
Instead it should have been:
const res = await axios.post('/login', { ident: email, password }, { withCredentials: true });
Related
i have an issue when trying to send the cookies from browser to nextJs server
first let me describe the issue :
my backend API is built with Nodejs and express, the frontend is
handled by Nextjs , when the user try to login the token is stored as
an httpOnly cookie ,to get the loggedin user i must send the cookie
from my frontend to my backend so i do that through nextJs
getServerSideProps function with the help of
Context.req.headers.cookie in order to sent this token to my nodeJs
backend.
this is the login controller (backend):
exports.login = catchAsync(async (req,res,next)=>{
// SOME CODE
const token = jwt.sign({id: user.id}, process.env.JWT_SECRET);
const cookieOption = {
expires: new Date(Date.now() + process.env.JWT_COOKIE_EXPIRES_IN*24*60*60*1000),
httpOnly: true,
secure : true,
sameSite: 'none',
// domain: '.herokuapp.com'
};
res.cookie('jwt', token, cookieOption);
res.status(200).json({
status: 'success',
data: {
user
}
})
});
and this is my getServerSideProps function in which i try to send the token from nextJs to nodeJs server:
export async function getServerSideProps(context){
console.log(context.req.headers.cookie) // EMPTY OBJECT!
console.log(context.req.cookies) //EMPTY OBJECT TOO
let user
try{
const res = await fetch(`${url}/users/isLoggedin`, {
method: 'GET',
credentials:'include',
headers: {
'Access-Control-Allow-Credentials': true,
Cookie: req.headers.cookie
},
})
const data = await res.json()
if(!res.ok){
throw data
}
user = data
}catch(err){
return console.log(err)
}
return {
props: {
user,
}
}
}
i can send the token from browser directly to nodeJs (client side) but i can't send the token from the browser to nextJs server i don't know why, i have tried many solutions but no ones workes for me .
note: both backend and frontend are deployed to heroku
I am trying to save the access_token and refresh_token of a user after oAuth.
Flow:
User authenticates and I retrieve their accessToken, refreshToken, and user data from the social site
I send data to a remix resource route
In the action for the remix-resource route, I save the data and then try to set the access token and refresh token using 'Set-Cookie' but it doesn't work.
Including the relevant part of my /api/setUser resource route where I try to set the cookie
export let action: ActionFunction = async ({ request }) => {
const session = await sessionStorage.getSession(
request.headers.get('Cookie')
);
const jsonData = await request.json();
session.set('access_token', jsonData['accessToken']);
session.set('refresh_token', jsonData['refreshToken']);
return json<LoaderData>(
{ status: 'ok' },
{
headers: {
'Set-Cookie': await sessionStorage.commitSession(session),
},
}
);
};
Make sure that in your createCookieSessionStorage that you have the cookie path set to / otherwise, the cookie is only visible on the route you set it.
I build the simple application that shows GitHub repositories. Here is the link for the app: https://wemake-services-test-client.herokuapp.com (firstly, you need to authenticate to GitHub). It seems to me that fetch doesn't send httpOnly cookies. Click "Sign In" and open the console:
Console Snapshot
Backend handler for route '/login' sets the httpOnly cookie:
import { Request, Response } from 'express';
import { createOAuthAppAuth } from '#octokit/auth-oauth-app';
import cookieOptions from '../cookieOptions';
export default async function login(req: Request, res: Response) {
const code = req.query.code as string;
const appAuth = createOAuthAppAuth({
clientType: 'oauth-app',
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
});
try {
const userAuth = await appAuth({
type: 'oauth-user',
code,
});
const { token } = userAuth;
res.cookie('ws_token', token, cookieOptions);
res.json({ isAuthenticated: true });
...
Then I make GET to '/user' route which has the authMiddleware that checks if there is 'ws_token' in req.cookies and send status 401 (Unauthorized) if not.
authMiddleware.ts:
export default function authMiddleware(
req: Request,
res: Response,
next: NextFunction
) {
if (!req.cookies.ws_token) {
res.sendStatus(401);
return;
}
next();
}
So I got 401 error on '/user' because fetch doesn't send the httpOnly cookie ws_token from client side.
...
async fetchUser(): Promise<GithubUser> {
const response = await fetch(`${API_URL}/user`, {
headers: {
Accept: 'application/json',
},
credentials: 'include',
});
const user: GithubUser = await response.json();
return user;
}
...
It works on localhost (without flag secure and domain set to localhost), but not on Heroku. But why? How to fix it?
Source code:
frontend – https://github.com/standbyoneself/ws-test-client (calls to API using fetch are in src/services/GithubService.ts)
backend – https://github.com/standbyoneself/ws-test-server
Solved by disabling "Prevent cross-site tracking" in Safari Security Settings.
that is my code in helper signin router
so I'm trying to set cookie to browser after confirm sign in
exports.signin = (req,res) => {
db.User.findOne({email:req.body.email}).then(user => {
user.comparePassword(req.body.password,function(err,isMatch){
if(isMatch){
let token = jwt.sign({userId: user.id},process.env.SECRET_KEY,{ expiresIn: 60*5 });
res.setHeader() //here I wanna send header Bearer to the browser
} else {
res.status(400).json({message:"Incorrect Password!"});
}
})
})
.catch(err => {
return res.status(404).send('No user found.');
})**strong text**
}
Authorization header is a client header, you cannot set it on the server(Server does not need authorization). You need to send it in a JSON response and then handle it in the client side.
Your server sends a JSON response with the token.
Your client sets the Authorization header and send it to any route that reaquires authorization
javascript client example:
var myHeaders = new Headers()
/**you need to get the token and put it in myToken var.
Where to store the token is the real question, you need
to take care about the storage you choose beacause of
the security risks*/
myHeaders.append('Content-Type','application/json; charset=utf-8');
myHeaders.append('Authorization', 'Bearer ' + myToken);
fetch( '/myurl', {
credentials: 'include',
headers: myHeaders,
method: 'GET'
}).then( res => {
return res.json();
}).then( res => {
/**your stuff*/
});
Then in your server check for the headers and you will see the Bearer
In Node.js res.setHeader() and Express js res.header() is an alias of res.set() method.
you can use in following ways :
res.setHeader('Authorization', 'Bearer '+ token);
res.header('Authorization', 'Bearer '+ token);
But I recommend you to read jwt example (angularjs & node) : https://code.tutsplus.com/tutorials/token-based-authentication-with-angularjs-nodejs--cms-22543
I'm new to Oauth and server-side stuff, so please be patient with me.
I have a web application that authenticates users with dropbox-js. Everything is pretty straightforward. The application uses dropbox-js' client.authenticate function, and if the user is authenticated, the application gets automatically redirected to the initial page, where it executes the authenticate callback. From that moment on, I know I'm happily authenticated with Dropbox, and I can do stuff with the app's Dropbox directory.
I got a public node.js server that currently does nothing. What I would like to do is:
As soon as the client is authenticated, call my server and tell it that the user is authenticated
If the user doesn't exist on the server database, create an entry for it him/her the user database (I don't need detailed instructions to do this). If it exists, send back the user's associated data.
How can I do that in a secure way? I mean, how can the server tell that the user is a valid Dropbox user? Should the server authenticate to Dropbox on its side with the user credentials? What is the workflow in these cases?
At the end of the authentication process, you have an access token, which is what's used to make calls to the API. If both the client and the server need to make calls to the API, then both will need to have the access token.
If you're doing the authentication client-side today, you could pull the access token out somehow (not sure if/how it's exposed from the library, but it's in there somewhere and also storaged in local storage) and pass it to the server. The server can then use it to call /account/info and get the Dropbox user ID of the authenticated user.
An alternative is to do it the other way around. Authenticate the user with the "code flow" (rather than "token flow") and get the access token on the server in the first place. Then you could pass it down to the client and pass it as an option in the Dropbox.Client constructor. I think that dropbox-js supports this itself, but it's also not hard to do yourself. Here's some raw Express code that logs in a user and displays his or her name:
var crypto = require('crypto'),
express = require('express'),
request = require('request'),
url = require('url');
var app = express();
app.use(express.cookieParser());
// insert your app key and secret here
var appkey = '<your app key>';
var appsecret = '<your app secret>';
function generateCSRFToken() {
return crypto.randomBytes(18).toString('base64')
.replace(/\//g, '-').replace(/\+/g, '_');
}
function generateRedirectURI(req) {
return url.format({
protocol: req.protocol,
host: req.headers.host,
pathname: app.path() + '/callback'
});
}
app.get('/', function (req, res) {
var csrfToken = generateCSRFToken();
res.cookie('csrf', csrfToken);
res.redirect(url.format({
protocol: 'https',
hostname: 'www.dropbox.com',
pathname: '1/oauth2/authorize',
query: {
client_id: appkey,
response_type: 'code',
state: csrfToken,
redirect_uri: generateRedirectURI(req)
}
}));
});
app.get('/callback', function (req, res) {
if (req.query.error) {
return res.send('ERROR ' + req.query.error + ': ' + req.query.error_description);
}
// check CSRF token
if (req.query.state !== req.cookies.csrf) {
return res.status(401).send(
'CSRF token mismatch, possible cross-site request forgery attempt.'
);
} else {
// exchange access code for bearer token
request.post('https://api.dropbox.com/1/oauth2/token', {
form: {
code: req.query.code,
grant_type: 'authorization_code',
redirect_uri: generateRedirectURI(req)
},
auth: {
user: appkey,
pass: appsecret
}
}, function (error, response, body) {
var data = JSON.parse(body);
if (data.error) {
return res.send('ERROR: ' + data.error);
}
// extract bearer token
var token = data.access_token;
// use the bearer token to make API calls
request.get('https://api.dropbox.com/1/account/info', {
headers: { Authorization: 'Bearer ' + token }
}, function (error, response, body) {
res.send('Logged in successfully as ' + JSON.parse(body).display_name + '.');
});
// write a file
// request.put('https://api-content.dropbox.com/1/files_put/auto/hello.txt', {
// body: 'Hello, World!',
// headers: { Authorization: 'Bearer ' + token }
// });
});
}
});
app.listen(8000);