JWT store at server database - node.js

Hello I have a query regarding JWT and authentication. In authentication, does the server have to store the JWT as well and have it related to the client?
When the client log in, it receives the JWT token and store it in localStorae with the "auth" key:
For example, this is a client's basic code
const user{
email:user_email,
password:user_password
//other field
}
axios
.post("http://localhost:4000/api/login", user)
.then(function (response) {
if (response.data.success === false) {
// password or user does not exist!
} else {
localStorage.setItem("auth", response.data.token);
setTimeout(() => {
history.push("/");
}, 3000);
}
})
.catch(function (error) {
console.log(error);
});
};
Then in our Routes, we can check if there is a token and if so, we can access certain urls that are only accessible when logged in:
const PrivateRoute = (props) => {
const token = localStorage.getItem("auth");
return <>{token ? <Route {...props} /> : <Redirect to="/login" />}</>;
};
But my question comes from: when the client sends a new request (once logged in) doing a POST, GET, DELETE, does the HTTPS packet data need to be in JSON format with the corresponding token? But if so, the server must have the JWT stored to confirm that the user is registered and is who they say they are. So what is the difference with a session?
How do you check if the server doesn't have the JWT stored in its database that the client is who it claims to be?
For example, in django, must there be a field that is jwt?
class User(models.Model):
email = models.EmailField(max_length=254)
token = models.BigIntegerField(default=None)
#Otros campos

A jwt typically contains a set of so called claims, one if them oftentimes is the sub claim which denotes the subject for whom this token was issued. Depending on the particular issuer, this claim may also be named differently. Another typically claim is exp which denotes a timestamp, when this token expires.
Furthermore a JWT should be signed by a trusted party (the issuer). This can be your own server or any other trusted party (for instance sign-in with Google or something like that). If you are able to verify that signature, you are safe to assume the token wasn't tampered with.
So if you find a sub claim in a token whichs signature you can verify, you can easily identify the user. If you furthermore check the expiration, you can reject tokens which are not valid anymore.
But if you don't store the token at the backend, you won't be able to revoke such a token prematurely and it will be valid until it expires. That may, or may be not, a problem, depending on your usecase.

Related

Refresh Token is not returned in #azure/msal-node using Authorization code flow?

In example project provided by Microsoft here which uses Authorization code flow the acquireTokenByCode method does not return refresh tokens.
From #azure/msal-node here refresh token is not mentioned.
Result returned from the authority's token endpoint.
uniqueId - oid or sub claim from ID token
tenantId - tid claim from ID token
scopes - Scopes that are validated for the respective token
account - An account object representation of the currently signed-in user
idToken - Id token received as part of the response
idTokenClaims - MSAL-relevant ID token claims
accessToken - Access token received as part of the response
fromCache - Boolean denoting whether token came from cache
expiresOn - Javascript Date object representing relative expiration of access token
extExpiresOn - Javascript Date object representing extended relative expiration of access token in case of server outage
state - Value passed in by user in request
familyId - Family ID identifier, usually only used for refresh tokens
please ensure your MSAL authorization code request includes the offline_access scope.
You could use MSAL.js to get token in this case, there is acquireTokenSilent method, it can perform silent renewal of tokens, which means you are no need to get the refresh token by yourself.
Popup
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(async (error) => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenPopup(request);
}
}).catch(error => {
handleError(error);
});
Redirect
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(error => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenRedirect(request)
}
});
It's designed to not return the refresh token if you are using #azure/msal-node.
As they stated in the discussion, the refresh token is handled background, inside the library itself for better security, which I also disagree with.
However, if you insist to have the token, you can manually call the API to the AzureAD endpoint.

Verify JWT from Google Chat POST request

I have a bot in NodeJS connected to Google Chat using HTTPs endpoints. I am using express to receive requests. I need to verify that all requests come from Google, and want to do this using the Bearer Token that Google Sends with requests.
My problem is that I am struggling to find a way to verify the tokens.
I have captured the token and tried a GET reuqes to https://oauth2.googleapis.com/tokeninfo?id_token=ey... (where ey... is the token start).
Which returns:
"error": "invalid_token",
"error_description": "Invalid Value"
}
I have tried what Google recommends:
var token = req.headers.authorization.split(/[ ]+/);
client.verifyIdToken({
idToken: token[1],
audience: JSON.parse(process.env.valid_client_ids)
}).then((ticket) => {
gchatHandler.handleGChat(req.body, res);
}).catch(console.error);
And get the following error:
Error: No pem found for envelope: {"alg":"RS256","kid":"d...1","typ":"JWT"}
Any idea where I should head from here?
Edit: https://www.googleapis.com/service_accounts/v1/metadata/x509/chat#system.gserviceaccount.com found this, investigating how to use it. The kid matches the one I get.
Worked it out, eventually.
You need to hit: https://www.googleapis.com/service_accounts/v1/metadata/x509/chat#system.gserviceaccount.com to get a JSON file containing the keys linked to their KIDs.
Then when a request arrives, use jsonwebtoken (NPM) to decode the token and extract the KID from the header.
Use the KID to find the matching public key in the response from the website above, then use the verify function to make sure the token matches the public key.
You also need to pass the audience and issuer options to verify, to validate that it is your particular service account hitting the bot.
The solution above maybe the correct for Google Chat, but in my experience Google services (e.g. Google Tasks) use OIDC tokens, which can be validated with verifyIdToken function.
Adding my solution here, since your question/answer was the closest thing I was able to find to my problem
So, In case if you need to sign a request from your own code
on client, send requests with OIDC token
import {URL} from 'url';
import {GoogleAuth} from 'google-auth-library';
// will use default auth or GOOGLE_APPLICATION_CREDENTIALS path to SA file
// you must validate email of this identity on the server!
const auth = new GoogleAuth({});
export const request = async ({url, ...options}) => {
const targetAudience = new URL(url as string).origin;
const client = await auth.getIdTokenClient(targetAudience);
return await client.request({...options, url});
};
await request({ url: 'https://my-domain.com/endpoint1', method: 'POST', data: {} })
on the server, validate OIDC (Id token)
const auth = new OAuth2Client();
const audience = 'https://my-domain.com';
// to validate
const token = req.headers.authorization.split(/[ ]+/)[1];
const ticket = await auth.verifyIdToken({idToken: token, audience });
if (ticket.getPayload().email !== SA_EMAIL) {
throw new Error('request was signed with different SA');
}
// all good
Read more about Google OpenID Connect Tokens

signature verification failed for JWT - node/express/identityServer/asp.net.

I am having an issue with validating the JWT on the server side end of my node/express app. The token is being generated in Identity Server in an asp.net core app. The token that is generated is an RS256 token type, which means a private key and public key need to be generated on creation in the Identity Server. I need to retrieve a valid certificate with a valid signature.
On the client side (Angular) I'm passing in the Bearer token on all requests once signed in. I need to authenticate that token somehow. The way to do that with a RS256 token type is to make sure the public key matches. I'm using
const jwt2 = require('jwt-simple');
For my JWT validation.
The issue is the secret, here is the jwt-simple documentation jwt-simple link. If I make the third value in decode false it works, because it's ignoring the secret/cert that is required.
I'm making this validation in the middleware so all endpoints will hit it.
I saw this issue - SO Similar Issue and ran those same commands. I'm still getting the error because the token doesn't really have anything to do with the certs because I'm getting it from the Identity Server project. So I need to retrieve the cert public key from that project.
How would I be able to send that cert in the token or retrieve that valid cert somehow?
v1 - (using the self signed server.crt as the cert and getting this error)
Error: Signature verification failed
App.js
//This is for a self-signed certificate locally with no correlation to the token itself.
const options = {
key: fs.readFileSync('./key.pem', 'utf8'),
cert: fs.readFileSync('./server.crt', 'utf8')
};
app.use((req, res, next) => {
if(!req.headers.authorization){
return res.status(403).json({ error: 'No credentials sent!'});
} else {
let token = req.headers.authorization.split(' ')[1]
var decoded = jwt.decode(token, options.cert);
if(decoded){
let currentTime = new Date().getTime()/1000
if(decoded.exp <= currentTime){
return res.status(403).json({
error: 'Token has expired'
});
}
}
else if(!decoded){
return res.status(403).json({
error: 'invalid token'
});
}
}
next();
})
JWT.io parsed token structure -
Header
{
"alg": "RS256",
"kid": "1231231231231231231",
"typ": "JWT",
"x5t": "si7bdXd6......HnxhO4Wi_s"
}
Do I do anything with x5t? Apologies for the long post. Thanks.
If the signing public key is provided with the token and you blindly trust it, it basically defeats the purpose of having signed tokens.
You need some other mechanism for sharing the authentication service's public key. Depending on your requirements for key rotations and how your app works in general, you can either get it from a static path/url when your app starts up, or you may want to build in a mechanism to periodically check for updated valid public key(s).

Keeping Session data on backend only using JWT

I'm using JWT on my application and I define my claims like so:
const claims = {
iss: process.env.DOMAIN,
scope: "game",
email: user.get("email")
};
I verify the token like so:
nJwt.verify(socket.handshake.query.token, app.get("jwt.secret"), (err, decoded) => {
if (err) return;
console.log(decoded.body.email); // doge#doge.com
});
However, it forces me to add session data into claims object. Instead, I want to use the token as a session identifier and keep session values on backend only, such as:
nJwt.verify(socket.handshake.query.token, app.get("jwt.secret"), (err, decoded) => {
if (err) return;
// Since we verified token, read token's session data from backend
});
Is there any example about doing this?
Ps. I use Redis so I can SET/GET values via Redis, but I feel like it's bad practice since I'll be developing whole session thing myself.
To uniquely identify your token, you could take the whole tokenstring as identifier, though this doesn't make much sense since its too long and unhandy.
Instead, jwt has already standardized a claim for this purpose: jti.
So generate your token something like this:
var claims = {
iss: process.env.DOMAIN,
jti: yourGenerateUniqueIdFunction();
};
After JWT verification, you have the jti value.
In the Backend you could model your Session K/V Store like this:
[jti].[keyName] -> [value]
Accessing K/V something like this:
Session.get('3452345.email') //doge#doge.com
Another way how to achieve what you want is just enabling the default session mechanism in express (https://github.com/expressjs/session),
then using https://github.com/tj/connect-redis to store the session in your redis store. In this way you could just easily read and write to the req.session and it will be backed to your configured redis store.

How is authentication token used to access user data

I am trying to understand how token based authentication strategy uses the token (e.g.: JWT) to access the data specific to a user. All I see explained in search results is the part when the user supplies the username and password and then the token is created and verified with each subsequent call.
Le't say I have a Node.JS service with two MongoDB collections: Users and UserMessages. Each entry from UserMessages contains a UserID and a Message.
If an authenticated user wants to see all the messages pertaining to that user, how do I (programatically) know how to filter the correct messages? How do I make the correlation between the token and the user identity (e.g.: UserID or anything else that helps identify the user) that I need for querying the message collection in the next step?
I just wish to understand the concept. I couldn't find anywhere a clear example where the user identity is obtained in code. Example:
apiRoutes.use(function(req, res, next) {
var token = req.body.token || req.param('token')
|| req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message:
'Failed to authenticate token.' });
} else {
req.decoded = decoded;
next();
}
});
}
});
The JWT contains a payload.
Typically, you put the user's ID in this payload. You can put anything you like in there, but since the token is sent on every request you want to keep the payload small. Also, because the JWT is base-64 encoded, anyone can decode the token and see the contents of the payload.
However, because only your server knows the "secret" for the JWT, no one can alter the payload or otherwise create a phony JWT... your server will consider such tokens invalid.
The above code seems to be decoding the payload and storing it on the req object. When it decodes the JWT it is also verifying the signature, so if the decode is successful you can trust the data in the payload. Now you can use the user ID from the payload to get the data you desire.

Resources