Send JWT from AngularJS to Node.js - javascript

An AngularJS app needs to exchange a JWT with the Node.js instance that serves it. The Node.js instance has a /user route which returns a JWT to the Angular client. What specific changes need to be made to the code below so that 1.) The AngularJS app can send the JWT back to the Node.js instance's /user route, and 2.) the Node.js code can isolate the JWT as a variable for processing?
The current AngularJS code for calling the backend /user route is:
$http.get('user').then(function(response) {
console.log('response is: ');
console.log(response);
if (response.data.token === 'anonymous') {
$rootScope.authenticated = false;
} else {
$rootScope.userJWT = response.data.token;
var payload = $rootScope.userJWT.split('.')[1];
payload = $window.atob(payload);
payload = JSON.parse(payload);
self.name = payload.name;
self.authorities = payload.authorities;
$rootScope.authenticated = true;
}
}, function() {
$rootScope.authenticated = false;
});
And the Node.js code for the backend /user route is:
app.get('/user**', function(req, res) {
console.log("You Hit The User Route TOP");
//How do we get the JWT from req?
var user = getUserName(theJwt);
var token = getToken(user);
var jwtJSON = getUser(token);
if( (jwtJSON["token"] == 'error') || jwtJSON["token"] == 'anonymous' ) {
res.sendStatus(500); // Return back that an error occurred
} else {
res.json(jwtJSON);
}
console.log("You Hit The User Route BOTTOM");
});
Note, the Node.js instance includes var jwt = require('jsonwebtoken');, and one of the processing methods will decode the JWT using var decoded = jwt.decode(token, {complete: true});, as per the jsonwebtoken API.

When using JWT there is no required way to communicate the token.
The most common way is to place the token into an HTTP Header.
On the AngularJS side you would make an HTTP request with an extra header (e.g. X-Auth-Token) which contains the JWT.
Example of AngularJS side:
var config = {
headers: {
"X-Auth-Token": $rootScope.userJWT
}
}
$http.get('routeThatNeedsJWT', config).then(function(response) { ... });
On the Node.js side you would get the contents of the header and process it using the jsonwebtoken library.
Example of Node.js side:
app.get('/routeThatNeedsJWT', function(req, res) {
var rawTokenFromHeader = req.get('X-Auth-Token'); // Get JWT from header
try {
var jwtJSON = jwt.verify(token, 'secret'); // Verify and decode JWT
res.json(jwtJSON);
} catch (err) {
res.sendStatus(500); // Return back that an error occurred
}
});
Helpful links:
Express 4.x getting header value
jsonwebtoken library verify token

Related

How do i store jsonwebtoken on cookie for front-end so client can send back the token for auth

I've been struggling to do this for about 6 days...
Everything is working perfectly such as authorization but one problem I had is making authentication.
On my user model (for creating the database schema) I do have a way to generate a token for logged in users or registered.
userSchema.methods.generateAuthToken = function(){
const token = jwt.sign({ _id: this._id }, config.get('jwtPrivateKey'));
return token;
}
So when user post to /login, server will respond with a token:
router.post('/', async (req, res) =>{
// Here i'm validating data and then if everything is right the code under will run.
console.log('logged in as: ' + user.username);
// Here i'm using the function to generateAuthToken().
const token = user.generateAuthToken();
console.log("Token from server: " + token);
// now here is my main problem i would like to use cookies to store it for an hour or so.
// then client can send it back to server for protected route.
res.status(200).send(token);
});
I have made a middleware function for auth (to check the token if you're going through a protected route)
module.exports = function (req, res, next){
// instead of using headers i would like to check for the cookie value if it's the token,
// pass the user in, else Access denied.
// I have no idea how to use cookie parser with middleware functions.
const token = req.header('x-auth-token');
if(!token) return res.status(401).send('Access denied. Sign in or register.');
try{
const decoded = jwt.verify(token, config.get('jwtPrivateKey'));
req.user = decoded;
next();
}
catch(err){
res.status(400).send('Invalid Token!');
}
}
here i'm using the auth middleware function:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
// but it's actually not passing the user in since i haven't done it with cookies.
router.get('/', auth, (req, res) =>{
res.render('index', {});
});
I do know I can do it with localStorage but it's a terrible practice and it would be better to store it on cookies so no one could hack on.
Is there any good approach to solve this problem? I'm kinda lost and lost hope to go back to sessionID (which I don't want to :( ).
After you request on frontend, you need get the response (token) and save on browser using this for example:
fetch('http://your-api-host/login', {
method: 'POST',
body: {
username: "user1",
password: "passworduser"
}
})
.then((res) => res.text((res)))
.then((token) => {
document.cookie = `AUTH_API=${token}`; <-- this save the cookie
})
With this value saved on frontend you need send this information on all requests, it's commum send this value on your HEADER (how you makes), to save on header you need read the value from token and put on header, like this:
const headersTemp = document.cookie.split(';'); // <-- this get all cookies saves and splits them in the array.
const finalHeaders = {};
headersTemp.forEach((header) => { // <-- looping on all cookies
const headerTemp = header.split('='); // <-- split each cookie to get key and value
finalHeaders[headerTemp[0].trim()] = headerTemp[1].trim() // <-- save on object to access using keys.
})
Now you can access all cookies using the key (the same used before), I used the key AUTH_API to save my cookie, let's send the request using fetch api:
fetch('http://your-api-host/route-protected', {
method: 'POST',
headers: {
'x-auth-token': finalHeaders['AUTH_API']
},
})
If you creating your application using libraries how React or any SPA framework, probably you will use tools like Axios, and I recommend uses libraris how This, it's more easy to work with cookies.

'Cannot set headers after they are sent to the client' error received after attempted to serve new html file

I'm very new to NodeJS, and I'm trying to follow/build off of a sample project built with the Spotify API and Express. The user is prompted to authenticate on the home page, and then I want to have them land at a different html file where relevant information will be displayed from the API. To my understanding "app.get" specifies what should happen once that endpoint is navigated to, so I thought that when my client.js file gets '/nextfile', I would present it with a new html file for that endpoint with response.sendFile(__dirname + '/views/nextpage.html'); within app.get('/nextpage').
Obviously, this isn't correct, because when I run the server, it simply returns to the index.html file after authentication, with an error that reads:
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I tried looking into this error, but I couldn't find anything that helped me solve my specific problem. Relevant excerpts from my client.js and server.js files are below:
Server.js
/** when home page is requested, respond with this file **/
app.get("/", function (request, response) {
response.sendFile(__dirname + '/views/index.html');
});
//-------------------------------------------------------------//
// init Spotify API wrapper
var SpotifyWebApi = require('spotify-web-api-node');
// Replace with your redirect URI, required scopes, and show_dialog preference
var redirectUri = 'http://localhost:8888/callback',
clID = '9013dc5d86b84ffca62df2f22e00968e',
clSEC = 'b9484118ab374707925b1b15100cc58b';
var scopes = ['user-top-read','streaming','user-read-private'];
var showDialog = true;
// The API object we'll use to interact with the API
var spotifyApi = new SpotifyWebApi({
clientId : clID,
clientSecret : clSEC,
redirectUri : redirectUri
});
app.get("/authorize", function (request, response) {
var authorizeURL = spotifyApi.createAuthorizeURL(scopes, null, showDialog);
console.log(authorizeURL)
response.send(authorizeURL);
});
// Exchange Authorization Code for an Access Token
app.get("/callback", function (request, response) {
var authorizationCode = request.query.code;
spotifyApi.authorizationCodeGrant(authorizationCode)
.then(function(data) {
console.log(data)
response.redirect(`/#access_token=${data.body['access_token']}&refresh_token=${data.body['refresh_token']}`)
}, function(err) {
console.log('Something went wrong when retrieving the access token!', err.message);
});
});
app.get("/logout", function (request, response) {
response.redirect('/');
});
app.get('/nextpage', function (request, response) {
**/* I want to serve his html file after the user is authenticated */**
response.sendFile(__dirname + '/views/nextpage.html');
var loggedInSpotifyApi = new SpotifyWebApi();
console.log(request.headers['authorization'].split(' ')[1]);
loggedInSpotifyApi.setAccessToken(request.headers['authorization'].split(' ')[1]);
// do stuff with the api
});
Client.js
$(function() {
$('#login').click(function() {
// Call the authorize endpoint, which will return an authorize URL, then redirect to that URL
$.get('/authorize', function(data) {
console.log(data)
window.location = data;
});
});
const hash = window.location.hash
.substring(1)
.split('&')
.reduce(function (initial, item) {
if (item) {
var parts = item.split('=');
initial[parts[0]] = decodeURIComponent(parts[1]);
}
return initial;
}, {});
window.location.hash = '';
if (hash.access_token) {
$.get({url: '/nextpage', headers: {"Authorization": `Bearer ${hash.access_token}`}}, function(data) {
// "Data" is the array of track objects we get from the API. See server.js for the function that returns it.
console.log(data)
var title = $('<h3>Your top tracks on Spotify:</h3>');
title.prependTo('#data-container-mod');
// For each of the tracks, create an element
data.items.forEach(function(track) {
var trackDiv = $('<li class="track"></li>');
trackDiv.text(track.name);
trackDiv.appendTo('#data-container ol');
});
});
}
});
Any help would be greatly appreciated.
The flow of your app in sending http headers appears to be like this from when the 'callback' url is reached at:-
A http header is sent alongside the redirect URL that contains the access token and refresh token.
Client.js takes the access token and sends it back through the /nextpage route with headers containing the access token
Send nextpage.html back to client.js and update the page accordingly..
What happens next?
Beware the browser tab still is on the route with the access tokens and refresh tokens.
So the jquery ajax request to '/nextpage' runs while the previous http headers had been sent already.
I however do not understand how the index.html is being returned..
can you try removing the window.location.hash=' ' in client.js

Validating Kentico Cloud webhooks signatures in Express.js

How to validate webbooks signature using express.js?
In docs, there is a section about notification signatures but I don't know how to combine it with Express.js
This question is a migrated from official Kentico Cloud Forum, that would be deleted.
In the API reference, there is a sample describing webhook validation in various languages including node.js.
If you want to use express.js you could start with this template code:
const express = require('express');
const crypto = require('crypto');
// Create a new instance of express
const app = express();
// Set up a raw bodyparser to read the webhook post
const bodyParserRaw = require('body-parser').raw({
type: '*/*',
});
function webhookValidator (req, res, next) {
// get the header signature from the webhook request
const givenSignature = req.headers['x-kc-signature'];
// throw error if it's missing
if (!givenSignature) {
console.log('Missing signature');
return res.status(409).json({
error: 'Missing signature'
});
}
// create HMAC from the raw request body
let hmac = crypto.createHmac('sha256', [your-webhook-secret-key]);
hmac.write(req.body);
hmac.end();
// get a base64 hash from HMAC
let hash = hmac.read().toString('base64');
// check validity with timingSafeEqual
let webhookValid = false;
try {
webhookValid = crypto.timingSafeEqual(Buffer.from(givenSignature, 'base64'), Buffer.from(hash, 'base64'));
} catch (e) {
webhookValid = false
}
// return validity
if (webhookValid) {
return next();
} else {
console.log('Invalid signature');
return res.status(409).json({
error: 'Invalid signature'
});
}
}
// create a route and pass through the bodyparser and validator
app.post('/webhook', bodyParserRaw, webhookValidator, ( req, res, next ) => {
// If execution gets here, the HMAC is valid
console.log('webhook is valid');
});
EDIT
You can use the Kontent webhook helper library to quickly verify the webhook notifications and their signatures. The library is available as #kentico/kontent-webhook-helper npm package and helps you avoid common problems when calculating the hash.

How to use JWT in Client with AJAX

My application is a Node.js API with a client inside the same application.
I'm trying to implement a simple auth login that uses a JWT token generated by a Node.js API.
My logic is as follows:
Client: User submits login information to /auth/login route.
$.ajax({
url: "/auth/login",
type: "POST",
data: formData,
dataType: "json",
success: function(data, textStatus, jqXHR) {
if (typeof data.redirect == "string") {
window.location = data.redirect;
}
},
error: function(data) {
if (typeof fail === "function") fail(data);
}
});
API: Verify user and on success generates JWT and sends back to the client.
router.post("/login", async (req, res) => {
var login = { UID: req.body.UID, password: req.body.password };
AU.manualLogin(login)
.then(result => {
res.header("x-auth-token", result.token).json({
status: 200,
message: "success",
data: result.data,
redirect: "/dashboard"
});
})
.catch(err => next({ status: 400, message: err.message }));
});
Client: Saves JWT to the header and checks for redirect - In this case, I use window.location to direct to /dashboard after successful login. (this part I'm not sure about)
API: Middleware checks valid JWT on protected routes.
module.exports = function auth(req, res, next) {
const token = req.headers["x-auth-token"];
if (!token)
return res.status(401).send("Access denied. No token provided.");
try {
const decoded = jwt.verify(token, "jwtPrivateKey");
req.user = decoded;
next(); //pass control to next middleware
} catch (ex) {
res.status(400).send("Invalid token.");
}
};
The Problem:
The token is definitely being sent from API -> Client. But I have no idea how to handle the token from the client-side. I think the issue might be to do with the window.location redirect as at this point it does not seem to be sending the x-auth-token to the API.
What I have tried
I have tested the solution with Postman from end-to-end and it works fine. That probably proves that it isn't the API side that has the issue.
I've also tried these sources:
Pass request headers in a jQuery AJAX GET call
Adding custom header in HTTP before redirect
How to add header to request in Jquery Ajax?
jwt on node - how does the client pass the token back to the server
You need kind of a storage to keep the token. Otherwise the user has always to login again after he closes the browser/tab. So it's quite common to keep the token in local or session storage.
Approach 1: Use a single page application (SPA) framework like angular, vue.js, react etc. to protect your routes client-side
Approach 2: You can request only html and css (view) from your backend and then store the token after a login procedure. With a valid token, fetch the (protected) data with ajax requests. Redirect to the login page if a ajax request returns the status code 401 (unauthorized) or a user wants to access the protected route without having a token stored. This is perhaps the most suitable for you.
Approach 3: Use Node.js with a backend framework like express and store auth information in a server side session
index.js
const express = require('express');
const session = require('express-session');
const app = express();
app.use(require("cookie-parser")());
app.use(session({ secret: 'aslwezoweasdfasdlkfalksdfhweelaerfcv', resave: false, saveUninitialized: true}));
routes/protectedRoutes.js
const express = require('express');
const router = express.Router();
router.all("/*", util.handleAuthenticate); // check auth on every request
// other routes
indexController.js (login functionality)
module.exports.login = function(req, res) {
if(!req.session.name) {
// check username/password --> db lookup
// if valid:
req.session.name = ...
// redirect to home or backref
// else: redirect to login
}
}
util/security.js
function isLoggedIn(req) {
return !!req.session.name;
}
function handleAuthenticate(req, res, next) {
if(isLoggedIn(req))
{
next();
}
else
{
// redirect to login page
}
}

Facebook Graph API private_replies returning error 10903 This user cant reply to this activity

I am attempting to write an App that sends a message with private_replies, but it always returns the following error:
{
message: '(#10903) This user cant reply to this activity',
type: 'OAuthException',
code: 10903,
fbtrace_id: 'HUzYz7nKBPV'
}
I have read a number of solutions, but none seem to be working for me, so I'm not sure if I've missed something or if the something on the Facebook API side has changed.
The App I am creating (currently based on a number of tutorials) waits for a user to mention a specific word in a comment on my page and will then send a message to the user via private_replies.
When I debug my Apps Page Access Token, I get this info:
Access Token Info
App ID 18**********164 : MTestChatBot
Type Page
Page ID 25**********999 : MTestPage
App-Scoped User ID
Learn More
10**********048 : <MY NAME>
User last installed this app via API N/A
Issued 1520423580 (on Wednesday)
Expires Never
Valid True
Origin Web
Scopes manage_pages, pages_show_list, read_page_mailboxes, pages_messaging, pages_messaging_phone_number, pages_messaging_subscriptions, public_profile
One of the 'solutions' I have read states that I need to have to App reviewed by Facebook first to get the read_page_mailboxes subscription, but as shown above, I should already have that permission. It also seems odd to get an App reviewed before I can test it.
I have tried giving a friend developer access to the App and Admin rights to the page. When they post comments I get the same error.
I have tried Publishing the page and all comments still get the same result.
In case it's of any use, here is a rough version of the App code:
'use strict';
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const request = require('request');
const express = require('express');
const body_parser = require('body-parser');
const app = express().use(body_parser.json());
app.listen(process.env.PORT);
app.get('/webhook', (req, res) => {
// Parse params from the webhook verification request
let mode = req.query['hub.mode'];
let token = req.query['hub.verify_token'];
let challenge = req.query['hub.challenge'];
// Check if a token and mode were sent
if (mode && token) {
// Check the mode and token sent are correct
if (mode === 'subscribe' && token === FB_VERIFY_TOKEN) {
// Respond with 200 OK and challenge token from the request
console.log('WEBHOOK_VERIFIED');
res.status(200).send(challenge);
} else {
// Responds with '403 Forbidden' if verify tokens do not match
res.sendStatus(403);
}
}
});
app.post('/webhook', (req, res) => {
// Parse the request body from the POST
let data = req.body;
if (data.object === 'page') {
data.entry.forEach(function(pageEntry) {
var pageID = pageEntry.id;
var timeOfEvent = pageEntry.time;
if (pageEntry.hasOwnProperty('changes')) {
pageEntry.changes.forEach(function(changes){
if(changes.field === 'feed' && changes.value.item === 'comment' && changes.value.verb === 'add'){
var messageData = {
message: 'Hello'
};
privateReply(messageData, changes.value.comment_id);
}
});
}
});
}
})
function privateReply(messageData, comment_id) {
request({
uri: 'https://graph.facebook.com/v2.12/' + comment_id + '/private_replies',
qs: { access_token: FB_PAGE_ACCESS_TOKEN },
method: 'POST',
json: messageData
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
} else {
console.error('Private Replies Failed: ', response.statusCode, response.statusMessage, body.error);
}
});
}
All advice gratefully received.
It is maybe a facebook glitch or something. But the user is somehow blocked from receiving private reply from your page

Categories