Session Managment between NodeJS and Angular using JSONWebToken - node.js

I'm trying to build an application in such a way that NodeJS serves as the backend with all business logic exposing JSON REST services to be consumed by the angular 4 app which is nothing but a dumb client. So far so good, however I'm having a hard time figuring the Session management.
I found that token based authentication is a way to go since you might one day be serving mobile apps however, I have a problem: if I go with JSONWebToken strategy on the server side with token expiration set to half an hour, then my client will need to re authenticate it self after half an hour which doesn't seem like a good fit, because then it may force the user to sign in again that is already working on the client application which is not how any web app works. Should I also need to maintain session management at Angular level and auto sign in if my token expires on server but then it violates the principle of a dumb client or I should scrap it altogether implement sessions at NodeJS it self? Another thing is if I implement the WebTokenStrategy I found that for every request that comes from the client I'll be making a trip to database to verify a user which I can cache in session if I'm doing session management on NodeJS.
Last thing that I have a hard time figuring out is okay I can secure my resources on NodeJS but then I also need my routes and pages to be served depending on user rights in my client application, should I also store this information in the NodeJS database and serve by the same API server but I think this again violates the single responsibility principle or should there be another database for this client site route and user management.
Can someone suggest a good approach and if possible with examples?
Thanks.

No a JSON web token do not required a trip to the database since you encode the information you want on the payload. However you can implement a redis strategy if you want to be able to revoke them (For right changes for example). The signature part will be used by your server to ensure the authenticity (thanks to your server-side JWT secret).
You can also choose the expiration time you want. But if you want to limit it to 30 minutes you can also implement a renew strategy. (Ask for a new token before the old one will expire soon : The server will just deliver a new token with the same data encode. For the front end renew strategy you can use such a lib :
'use strict';
/**
* Helper class to decode and find JWT expiration.
*/
class JwtHelper {
urlBase64Decode(str) {
let output = str.replace(/-/g, '+').replace(/_/g, '/');
switch (output.length % 4) {
case 0: { break; }
case 2: { output += '=='; break; }
case 3: { output += '='; break; }
default: {
throw 'Illegal base64url string!';
}
}
return this.b64DecodeUnicode(output);
}
// credits for decoder goes to https://github.com/atk
b64decode(str) {
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let output = '';
str = String(str).replace(/=+$/, '');
if (str.length % 4 == 1) {
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (
// initialize result and counters
let bc = 0, bs, buffer, idx = 0;
// get next character
buffer = str.charAt(idx++);
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf(buffer);
}
return output;
}
// https://developer.mozilla.org/en/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem
b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(this.b64decode(str), (c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
decodeToken(token) {
let parts = token.split('.');
if (parts.length !== 3) {
throw new Error('JWT must have 3 parts');
}
let decoded = this.urlBase64Decode(parts[1]);
if (!decoded) {
throw new Error('Cannot decode the token');
}
return JSON.parse(decoded);
}
getTokenExpirationDate(token) {
let decoded;
decoded = this.decodeToken(token);
if (!decoded.hasOwnProperty('exp')) {
return null;
}
let date = new Date(0); // The 0 here is the key, which sets the date to the epoch
date.setUTCSeconds(decoded.exp);
return date;
}
isTokenExpired(token, offsetSeconds) {
let date = this.getTokenExpirationDate(token);
offsetSeconds = offsetSeconds || 0;
if (date == null) {
return false;
}
// Token expired?
return !(date.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000)));
}
}
const jwtHelper = new JwtHelper();
const decodedData = jwtHelper.decodeToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ');
console.log(decodedData)

Related

How to handle concurrency issue NodeJS with Redis

My requirement
Read the token from Redis which is not reserved for application(it's application value is not set).
Update this non-reserved token with given application name and Other threads should not get same token for reservation.
static async reserveNewTokens(indextype: string, numberOfNewTokensForReservation: number, appname: string) {
mgLogger.debug(this._module, "reserveTokens()", "start");
for (let index = 0; index < numberOfNewTokensForReservation; index++) {
const nonReserverdToken = await LicenseDbManager.getNonReserverdToken(indextype);
LicenseDbManager.reserveNewForProject(nonReserverdToken.documents[0].id, appname)
}
mgLogger.debug(this._module, "reserveNewTokens()", "end");
}
After execution of above code I observed that same token is override with two application names. Code is executed on subscription of Redis pub/sub channel. Please let me know how can I solve this issue, I am using node-redis.
Instead of using EXISTS/GET to check if the token does not exists and only then set it, you can use SET with the NX (stands for not exists) modifier:
let reply;
do {
reply = await client.set(generateTokenKey(), 'value', {
NX: true
});
} while (reply === null)

What's the easiest way to fetch a SharePoint file by a path from the Microsoft Graph?

Say you have a file path like: https://contoso.sharepoint.com/sites/somesite/MyDocLib/Folder/Foo.docx
What's the easiest way to turn this into a Microsoft Graph call to fetch the contents of the file which I assume we need to do via the drives endpoint using the correct id's.
I assume I might have to run multiple calls and perhaps assume that /slash1/slash2 is the site, then the next is the doclib etc(?)
Not sure is it the easiest or the only option but the below solution demonstrates how to
meet the requirements of Addressing resources in OneDrive API:
first step would be to transform the URL into a sharing token (see below section), for that matter we utilize Shares API
once the sharing token is generated, the OneDrive API request to download a file could be constructed like this: /shares/{shareIdOrEncodedSharingUrl}/driveitem/content
How to transform the URL into a sharing token
For url:
https://contoso.sharepoint.com/sites/somesite/MyDocLib/Folder/Foo.docx
should be generated the following token:
u!aHR0cHM6Ly9jb250b3NvLnNoYXJlcG9pbnQuY29tL3NpdGVzL3NvbWVzaXRlL015RG9jTGliL0ZvbGRlci9Gb28uZG9jeA
On how to encode a URL is described in MS Graph documentation (C# version is provided there)
NodeJS version:
function urlToToSharingToken(url) {
var trimEnd = function(str, c) {
c = c ? c : ' ';
var i = str.length - 1;
for (; i >= 0 && str.charAt(i) == c; i--);
return str.substring(0, i + 1);
};
var value = Buffer.from(url).toString('base64');
return "u!" + trimEnd(value, '=').replace(/\//g, '_').replace(/\+/g, '-');
}
Example
The example demonstrates how download a file by url provided in the https://contoso.sharepoint.com/sites/somesite/MyDocLib/Folder/Foo.docx format using msgraph-sdk-javascript library:
const sharedItemId = urlToToSharingToken(url); //1.construct sharing token
const requestUrl = "/shares/" + sharedItemId + "/driveitem/content"; //2. construct a query to download a file content
return new Promise((resolve, reject) => {
var builder = client.api(requestUrl);
getAsBinary(builder, (err, stream) => {
if (err) {
return reject(err);
}
return resolve(stream);
});
});

Execute a web api method several times based on time constraint

I have an assignment to access a web API to get a token and use this token as an input to call another web API. Token expires after 60 seconds so I have to again call first web API to get new token and then use this new token to access my second web API.
In normal scenario it is working fine but some time second API takes more time to execute and token expires.
I have used HttpClient class to call web API. How should I handle token expiration time constraint before calling another web API. I want to make it more generic.
I have written following code.
public override bool CallAPI()
{
try
{
bool isSPAdded = false;
int counter = 0;
do
{
Uri uri = new Uri(_settings.HostUrl + "/auth/realms/master/protocol/openid-connect/token");
var requestContent = string.Format("username={0}&password={1}&client_id=admin-cli&grant_type=password", _settings.UserName, _settings.Password);
StringContent _requestContent = new StringContent(requestContent, Encoding.UTF8, "application/x-www-form-urlencoded");
_response = _client.PostAsync(uri, _requestContent).Result;
if (_response.IsSuccessStatusCode)
{
Uri uri = new Uri(String.Format(_settings.HostUrl + "/auth/admin/realms/{0}/clients", Node.OrgId));
_requestContent = new StringContent(response.Content.ReadAsStringAsync().Result, Encoding.UTF8, "application/json");
_response = _client.PostAsync(uri, _requestContent).Result;
if (_response.IsSuccessStatusCode)
{
isSPAdded = _response.IsSuccessStatusCode;
break;
}
}
counter++;
} while (_response.StatusCode == HttpStatusCode.Unauthorized && _response.Content.ReadAsStringAsync().Result == "Bearer" && counter < 2);
return isSPAdded;
}
}
I have used do while loop to retry it if token expires.
Please suggest the best approach for same.

generate random unique token for user id

I want to generate token as user id and store in database , but how to generate unique one?
should I add timestamp var currentUnixTimestamp = (new Date().getTime() / 1000); as salt? how to do with crypto?
var generateToken = function() {
return new Promise(function (fulfill, reject){
crypto.randomBytes(8, function(error, buf) {
if (error) {
reject(error);
} else {
var token = buf.toString('hex');
fulfill(token);
}
});
});
};
Eight random bytes from a properly seeded crypto library has a low chance of a collision, so you don't usually need to concern yourself with duplicates. In fact, increase that to 16 bytes, and your code is on par with UUID version 4. This is considered a standard for UUIDs. The chances of a collision are so remote it is not usually worth considering.
If you are going that far though, consider using a standard format UUID, such as the node package "uuid". There are also database-side uuid functions which you can add as default to schemas e.g. in Postgres. The advantage is a standardised and well-understood format for your ids, and you won't need to spend any time justifying or maintaining your code for this, just point developers to the standard docs.
If you want this token for authentication purposes you should use json web token instead. It will manage for you and its quite efficient.
Only have to include as a middleware .
app.use(expressJWT({
secret: new Buffer("Your-secret-key").toString('base64')
}).unless({
//# pass api without validating
path: unlessRoutes
}));
You could specify which routes you don't want to to skip in jwt middleware by giving an array in unlessRoutes.
var unlessRoutes = [
'/',
/\/login/,
/\/register/,
/\/customers/,
/\/customer$/,
/\/addCustomer/,
/\/just/,
/\/search/,
/\/dynamic/,
/\/favicon.ico/
]
This is what i think we can do for generating the random token using the crypto:
var passwordResetToken = createRandomToken(data.body.email);
exports.createRandomToken = function (string) {
var seed = crypto.randomBytes(20);
return crypto.createHash('abcde').update(seed + string).digest('hex');
};

How do I generate a session ID in Node.js?

Im trying to learn Node.js without using any third party modules or frameworks. I'm getting to the point where I got to figure out how to give session ids to users who login...
So far I know I can set the session id by writing it in the headers, setting the cookies:
writeHead(200, {'set-cookie':'Math.random() ' } );
and then I can retrieve the session id and later compare it to the database.
request.headers.cookie(request.url);
But how do I GENERATE the session ID value? I am new to programming. The first thing I thought of was using Javascript's Math.random(); and using that value to set the cookie ( session id ). In my mind it feels stupid but that is how far I can think.
How am I suppose to generate the session id using Node.js, no third party modules, barebones please!
Note: You should probably use a session manager for whatever framework you go with.. be it connect, express, koa or any number of others.
This will give you a UUID version 4 (random) using crypto.randomBytes.
var crypto = require('crypto');
module.exports = genUuid;
function genUuid(callback) {
if (typeof(callback) !== 'function') {
return uuidFromBytes(crypto.randomBytes(16));
}
crypto.randomBytes(16, function(err, rnd) {
if (err) return callback(err);
callback(null, uuidFromBytes(rnd));
});
}
function uuidFromBytes(rnd) {
rnd[6] = (rnd[6] & 0x0f) | 0x40;
rnd[8] = (rnd[8] & 0x3f) | 0x80;
rnd = rnd.toString('hex').match(/(.{8})(.{4})(.{4})(.{4})(.{12})/);
rnd.shift();
return rnd.join('-');
}
You could also use the UUID module from npm. The crypto package is not an in-browser option, though you could use Browserify's crypto shim.

Resources