I have built a REST api using nodeJS and Typescript and I am performing authentication and authorisation by setting the JWT token as a cookie. I have cors middleware set up as follows on my
const corsOptions: cors.CorsOptions = {
optionsSuccessStatus: 200,
origin: "*",
methods: ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS'],
credentials: true
}
app.options('*', cors(corsOptions));
app.use(cors(corsOptions));
I have a flutter client that logs into the API as follows
class LoginRequest {
String email;
String password;
LoginRequest(String email, String password) {
this.email = email;
this.password = password;
}
}
class LoginResponse {
String username;
LoginResponse.fromJson(json) {
this.username = json["username"];
}
}
Map handleResponse(response) {
if (response.statusCode == 200) {
// Note: Here Token is printed correctly
print("Server responded with response ${response.headers['set-cookie']}");
return json.decode(response.body) as Map;
} else {
throw Exception("Server responded with status ${response.statusCode}");
}
}
Future<LoginResponse> postLogin(LoginRequest loginRequest) async {
final body = {"email": loginRequest.email, "password": loginRequest.password};
var bodyEncoded = json.encode(body);
final response = await http
.post(new Uri.http(apiHost, "/users/login"), body: bodyEncoded, headers: {
"Content-Type": "application/json",
"credentials": 'include',
"Accept": "application/json",
});
final jsonResponse = handleResponse(response);
return LoginResponse.fromJson(jsonResponse);
}
But I cannot seem to get the cookie on the subsequent POST requests. In desperation, I even tried directly sending the token as follows but on the server it comes up undefined. Please help!
Future<AccountInformation> getAccountAllInfos() async {
final response = await http.get(new Uri.http(apiHost, "/users/me"), headers: {
"credentials": 'include',
"Accept": "application/json",
"set-cookie":
"token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZjEyOTY4MTYwNTI1MzRlMWFjZGM4MTQiLCJ1c2VybmFtZSI6InBhd2FuIiwiZW1haWwiOiJwYXdhbkB0b3RvLmNvbSIsIm5hbWUiOiJQYXdhbiIsIm1vYmlsZSI6Ijk4ODYxNDQ2OTMiLCJjcmVhdGVkQXQiOiIyMDIwLTA3LTE4VDA2OjI4OjE3LjE1NloiLCJ1cGRhdGVkQXQiOiIyMDIwLTA3LTE4VDA2OjI4OjE3LjE1NloiLCJfX3YiOjAsImlhdCI6MTU5OTM4OTE5N30.cMVjgj5WntyN0yVjdfDQupyL-dnoDCqEE0rc7oz-k-A"
});
final json = handleResponse(response);
return AccountInformation.fromJson(json);
}
Server side, I use a cookie Extractor to get the cookie and passport to carry out auth:
const JwtStrategy = passportJWT.Strategy
const cookieExtractor = (req: Request) => {
console.log("Cookie : ", req.cookies.token)
if (req && req.cookies) {
const token: string = req.cookies.token
return token;
}
return null
};
const opts = {
jwtFromRequest: cookieExtractor,
secretOrKey: process.env.JWT_SECRET || "defaultjwtsecret",
}
passport.use(new JwtStrategy(opts, function (jwt_payload: IUserDocument, done) {
console.log("Payload:", jwt_payload)
User.findOne({ username: jwt_payload.username }, function (err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
And the route is secured as follows:
app.use("/users/me", passport.authenticate("jwt", { session: false }))