JWT is not stored... How to view it? - node.js

Can't understand the nodejs Authentication (KoaJS2).
I have this piece of code :
router.post("/login", koaBody, (ctx, next) => {
const data = ctx.request.body;
db
.any("SELECT * FROM users ORDER BY id ASC")
.then(function(data) {
token = jwt.sign(data, token_secret, {
expiresIn: "24h" // expires in 24 hours
});
console.log(ctx.request.header);
// ctx.set("token", `test`);
})
.catch(function(error) {
// error;
});
Where is the token stored after I sign it?
There is no "Authentication" header...

On the server side you neary to the right thing. What you need to do: return back the token to the client:
router.post("/login", koaBody, (ctx, next) => {
const data = ctx.request.body;
// ...
// extract username and password from body
// ...
db
.any("SELECT * FROM users ORDER BY id ASC") // here using and checking credentials is also missing ;-)
.then(function(data) {
// create the token
token = jwt.sign(data, token_secret, {
expiresIn: "24h" // expires in 24 hours
});
// now you have to pass it back to the client
// the token is NOT stored on the server side!
ctx.body = { 'token': token }
})
.catch(function(error) {
// error;
});
On the client side - if you get back a token in the body - you store it e.g. in the local storage
I am using angular 2 on the client side, here the code (client side login service) could look like this:
login(credentials) {
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
const reqopt: RequestOptions = new RequestOptions({
headers: headers
});
// credentials: username, password --
return this.http.post(...your_api_endpoint_ + '/login', credentials, reqopt)
.map(res => {
const data = res.json();
if (data && data.token) {
localStorage.setItem('token', data.token); // <--- here you store your token
}
});
}
Each time you now hit your API again, do not forget to provide you token in the header and check it on the server side (JWT.verify()).
Here you can find a more general introduction to JWT:
https://medium.com/vandium-software/5-easy-steps-to-understanding-json-web-tokens-jwt-1164c0adfcec
Hope that helps.

Related

How to use JWT token stored in cookie on a NodeJs API using Fetch API Get Request in ReactJs to get user data

Hi I have managed to store the JWT token generated on Login to a cookie in the browser. How can I use the token to display a list of users from a protected route in Nodejs APi.
I have accessed the token from the cookie using following code
Cookies.get("access")
the Fetch API Code is
const url = "http://localhost:4000/users/";
const getUsers = () => {
fetch(
url,
{ method: "GET" },
{
headers: {
Authorization: "Bearer" + Cookies.get("access"),
"x-auth-token": Cookies.get("access"),
},
}
)
.then((data) => data.json())
.then((prdt) => console.log(prdt));
};
useEffect(getUsers, []);
Node API Code is as follows:
//http://localhost:4000/users
router.get("/", auth, async function (request, response) {
try {
const result = await getAllUsers();
response.send(result);
} catch {
response.status(400).send({
message: error,
});
}
});
the Nodejs Auth Code is as follows:
export const auth = (request, response, next) => {
try {
const token = request.header("x-auth-token");
console.log("token", token);
jwt.verify(token, process.env.SECRET_KEY);
next();
} catch (err) {
response.status(401).send({
error: err.message,
});
}
};
I am not sure where I am going wrong
the Nodejs Part works as tried in Postman The issue is originating on the react side fetch API where I am not sure how to send the keyname "x-auth-token" with jwt token to retrieve the data.

res.cookie() not working in Nodejs/Angular

Pls help me figure out where I am going wrong.
Problem: When I login and then refresh, I get logged out.
Expected: Upon logging in, a cookie should be set by Node, which should be sent to the server for every request. Which is not happening.
The app runs an initializer function to try and auto authenticate based on the token present in the cookie.
Angular initializer function
export function appInitializer(accountService: AccountService) {
return () => new Promise(resolve => {
// attempt to refresh token on app start up to auto authenticate
accountService.refreshToken()
.subscribe()
.add(resolve);
});
}
Angular Account Service
#Injectable({ providedIn: 'root' })
export class AccountService {
private accountSubject: BehaviorSubject<Account>;
public account: Observable<Account>;
....
constructor(private httpClient: HttpClient, private router: Router) {
this.accountSubject = new BehaviorSubject<Account>(null);
this.account = this.accountSubject.asObservable();
}
login(userName: string, password: string) {
return this.httpClient.post<any>(`${this.reqUrl}/authenticate`, {userName, password})
.pipe(map(account => {
this.accountSubject.next(account);
this.startRefreshTokenTimer();
return account;
}));
}
refreshToken() {
return this.httpClient.post<any>(`${this.reqUrl}/refresh-token`, {})
.pipe(map(account => {
this.accountSubject.next(account);
this.startRefreshTokenTimer();
return account;
}));
}
private refreshTokenTimeout;
private startRefreshTokenTimer() {
// parse json object from base64 encoded jwt token
const jwtToken = JSON.parse(atob(this.accountValue.jwtToken.split('.')[1]));
// set a timeout to refresh the token a minute before it expires
const expires = new Date(jwtToken.exp * 1000);
const timeout = expires.getTime() - Date.now() - (60 * 1000);
this.refreshTokenTimeout = setTimeout(() => this.refreshToken().subscribe(), timeout)
}
Node Route
router.post('/authenticate', AccountController.authenticateSchema, AccountController.authenticate);
router.post('/refresh-token', AccountController.refreshToken);
Node Acct Controller
exports.authenticate = (req, res, next) => {
const { userName, password } = req.body;
const ipAddress = req.ip;
accountService.authenticate({userName, password, ipAddress})
.then(({refreshToken, ...account}) => {
setTokenCookie(res, refreshToken); // <<<<<<<<<<<<<<<<<<<<<<<<<
res.json(account);
})
.catch(next);
}
exports.refreshToken = (req, res, next) => {
const token = req.cookies.refreshToken;
if(!token) {
return res.status(400).json({ message: 'acct ctlr rTkn not found'}); // <<< even after Login and refresh I reach here
}
const ipAddress = req.ip;
accountService.refreshToken({token, ipAddress})
.then(({refreshToken, ...account}) => {
setTokenCookie(res, refreshToken);
res.json(account);
})
.catch(next); // ?
}
function setTokenCookie(res, token) {
// create cookie with refresh token that expires in 1 day
const cookieOptions = {
httpOnly: true,
expires: new Date(Date.now() + 1*24*60*60*1000)
};
res.cookie('refreshToken', token, cookieOptions); ///?
}
I get this error in dev tool upon starting the appand also when i refresh after logging in.
Try adding:
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

Get JWT token in redux

I am using JWT to handle authentication and I have a update new password function. My problem is that how do I pass this JWT to my server in the UpdatePasswordAction ? Actually, I have that JWT in my cookies but when I submit the data to the server, the data is not passed correctly. Do I pass it right?
From my server, the JWT can not be retrieved.
console.log(JSON.stringify(req.cookies));// -> {}
console.log(req.headers.authorization);// -> undefined
In my User.actions.jsx, what I want to do is to get the token stored in cookies and pass the updatePasswordState which contains current password, new password, and new confirmed password.
import { Cookies } from 'js-cookie';
export const UpdatePasswordAction = (updatePasswordState) => {
return async (dispatch) => {
try {
// what I do is to pass the new password and JWT to the server to handle new password update
const token = Cookies.get('jwt');
const res = await axios.patch(`http://127.0.0.1:3000/users/updateMyPassword`, updatePasswordState, token);
const { data } = res;
dispatch({ type: UserActionTypes.UPDATE_PASSWORD_SUCCESS, payload: data });
alert('Update Password Successfully');
} catch (error) {
if (error.response) {
dispatch({
type: UserActionTypes.UPDATE_PASSWORD_FAIL,
payload: error.response.data.message,
});
console.log(error.response.data.message);
}
}
};
};
In my server, I have a middleware to check whether the user is login or not.
exports.protect = catchAsync(async (req, res, next) => {
//Getting token and check of it's there
let token;
console.log(JSON.stringify(req.cookies));// -> {}
console.log(req.headers.authorization);// -> undefined
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
token = req.headers.authorization.split(' ')[1];
} else if (req.cookies.jwt) {
token = req.cookies.jwt;
}
if (!token) {
return next(
new AppError('You are not logged in! Please log in to get access.', 401)
);
}
// rest jwt decode and verification
});
Any solution?
You are not correctly sending the data to the server. The axios#patch signature is this:
axios#patch(url[, data[, config]])
So you should change it to something like this:
await axios.patch(`http://127.0.0.1:3000/users/updateMyPassword`, updatePasswordState, {
headers: {
"Authorization": token
}
});
And on your server side you can access the Authorization header with req.headers.authorization

How do I store JWT Token after receiving from Cognito? Logging in via the Cognito Hosted UI

Architecture: front end Angular, backend nodejs/express.
Currently the setup works as follow:
Login to the site via the Cognito Hosted UI
This redirects to our home page and sends us a code in the URL
I pull down this code in Angular
import { Component, OnInit } from '#angular/core';
import { DbService } from '../db.service';
import { Iss } from '../db.service';
import { Router, ActivatedRoute } from '#angular/router';
import { Http, Response, RequestOptions, Headers} from '#angular/http';
#Component({
selector: 'app-dashboard'
})
export class GroupSelectionComponent implements OnInit {
cognitoCode: string;
constructor(
private DbService: DbService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
this.route.queryParams
.subscribe(params => {
console.log(params);
console.log(params.code);
this.cognitoCode = params.code;
});
this.DbService.getIss(this.cognitoCode).subscribe(
iss => this.iss = iss
);
}
In the code you will see I am passing the congitocode to the dbservice for getIss.
db.service
getIss(cognitoCode ): Observable<Issuer[]> {
const url = hosturl +'i_l';
// let header: HttpHeaders = new HttpHeaders();
const httpOptions = {
headers: new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
'Authorization': cognitoCode
})
};
let params = new HttpParams()
console.log(httpOptions.headers);
return this._http.get(url, httpOptions)
.pipe(
map((res) => {
console.log(res);
return <Issuer[]> res;
})
);
}
I then send the code as part of the headers of my GET request to the backend.
The GET then hits my backend router with these settings.
var authMiddleware = require('../middleware/AuthMiddleware.js');
router.get('/i_l', authMiddleware.Validate, i_l.get);
This will then call my authMiddleware which takes the code provided by Cognito Hosted UI and use a POST against oauth2/token to get my JWT token.
That token is then parsed used to compare to the https://cognito-idp.us-east-2.amazonaws.com/REMOVED/.well-known/jwks.json for congnito.
Once validated the request continues and I get data back from the backend.
// POST that trades the code for a token with cognito
var options = {
'method': 'POST',
'url': 'https://REMOVED.amazoncognito.com/oauth2/token',
'headers': {
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
'grant_type': 'authorization_code',
'client_id': 'CLIENTIDREMOVED',
'code': req.headers['authorization'],
'redirect_uri': 'http://localhost/group-selection'
}
};
// First request gets the JSON request of the token using the POST above
request(options, function (error, response) {
if (error) throw new Error(error);
token = JSON.parse(response.body).access_token;
//localStorage.setItem('token', token);
// request pull down status based on validitiy of token
request({
url : `https://cognito-idp.us-east-2.amazonaws.com/REMOVED/.well-known/jwks.json`,
json : true
}, function(error, response, body){
console.log('token: ' + token);
if (!error && response.statusCode === 200) {
pems = {};
var keys = body['keys'];
for(var i = 0; i < keys.length; i++) {
var key_id = keys[i].kid;
var modulus = keys[i].n;
var exponent = keys[i].e;
var key_type = keys[i].kty;
var jwk = { kty: key_type, n: modulus, e: exponent};
var pem = jwkToPem(jwk);
pems[key_id] = pem;
}
var decodedJwt = jwt.decode(token, {complete: true});
if (!decodedJwt) {
console.log("Not a valid JWT token");
res.status(401);
return res.send("Not a valid JWT token");
}
var kid = decodedJwt.header.kid;
var pem = pems[kid];
if (!pem) {
console.log('Invalid token - decodedJwt.header.kid');
res.status(401);
return res.send("Invalid token - decodedJwt.header.kid");
}
jwt.verify(token, pem, function(err, payload) {
if(err) {
console.log("Invalid Token - verify");
res.status(401);
return res.send("Invalid token - verify");
} else {
console.log("Valid Token.");
return next();
}
});
} else {
console.log("Error! Unable to download JWKs");
res.status(500);
return res.send("Error! Unable to download JWKs");
}
});
});
Quesiton -- how I set this up so that the Token I get back continues for the user?
If I understand your question properly then you are trying to validate all your apis through cognito user right?
Then you just need to do two things.
Add in header JWT token once you are getting after login. Just store into your application scope and pass everytime whenever any API is calling.
Auth.signIn(data.username, data.password)
.then(user => {
let jwkToken = user.getSignInUserSession().getAccessToken().getJwtToken();
// Store above value in singletone object or application scope.
})
.catch(err => {
//error
});
Now When API is calling pass jwkToken as header.
Then Go AWS ApiGateWay Console and add into Authorizers.

Google API, Node.js - redirect_uri_mismatch after getToken

I have a route on my backend application which should return an access token for a code sent from the frontend:
router.get('/token', (req, res) => {
const auth = googleService.getAuth();
auth.getToken(req.query.code, (error, res2) => {
const data = { code: 200 }
if (error) {
data.code = error.code;
data.error = error.response.data;
} else {
console.log(res2);
}
res
.status(data.code)
.send(data);
})
});
I retrive auth from googleService.getAuth():
const { google } = require('googleapis');
const keys = require('../config/keys');
var module = module.exports = {
getAuth: (token = false) => {
let auth = new google.auth.OAuth2(
keys.google.clientID,
keys.google.clientSecret,
keys.google.callbackURL
);
if (token) {
auth.credentials = {
access_token: token,
refresh_token: null
};
}
return auth;
},
youtube: google.youtube('v3')
};
In my config file, I have callbackURL:
module.exports = {
google: {
apiKey: 'XXXXXXXXXXXXXXXX',
clientID: 'XXXXXXXXXXXXXX',
clientSecret: 'XXXXXXXXXXXXXXX',
callbackURL: 'http://localhost:3000/google/redirect'
}
}
I also set it in my console:
However, I always have the following error when calling this route:
"error": {
"error": "redirect_uri_mismatch",
"error_description": "Bad Request"
}
The uri needs to match from the auth to the token. So it looks like you auth via your token endpoint and try to send the token to the google/redirect path. You can work around this.
To do so, verify that the redirect uri is whitelisted in your google project console. You can view this via the API Access (where you would see client ID, client secret, as well as a list of redirect uris)
It's possible that you can't have a redirect URI that is local.
If so, you can have an http valid address for you localhost in 2 mins with https://ngrok.com
Hope it'll help

Resources