May be this will be silly question but I/m curious about that. I'm new to React-js and node-js backend. I'm save jwt token in web browser local storage and decode that token in react js frontend. my question is that safe use jwt decode method in front-end because someone can also decode token if they know token?
Yes, it is.
The idea behind JWTs isn't that they can't be decoded, in fact it's the exact opposite. They're designed for use in distributed systems. Instead they are secure, because they can only be generated using a secret key, which should only be available on the server-side of your application.
A JWT actually comprises three parts - headers, payload, and signature - with the headers, payload, and secret key being combined and hashed to form the signature.
This signature is then used to validate that the headers and payload haven't been modified. If they have, the signature would no longer match.
JWT codes have 3 separate part that separated with .
Public part which contains
HEADER:ALGORITHM & TOKEN TYPE
PAYLOAD:DATA
and a VERIFY SIGNATURE which guarantee your token is valid or not.
The public part can be extracted by everyone who have the token
JWT decode only look for public part so it is totally safe to do that in your front-end code.
An example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
>> after decode
HEADER:ALGORITHM & TOKEN TYPE
{
"alg": "HS256",
"typ": "JWT"
}
PAYLOAD:DATA
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
The answer is yes and no, in case you have some user identifying information (like userId) and u are okay with that data going out, then yes u can decode it in the front end. If you are gonna store some sensitive information on the token then no. (This is a bad practice). Also note that the payload cant be modified. Even if modified the signature wont verify the token. So the best practice will be not to have sensitive data in the payload.
Related
So I'm currently learning/building a REST API backend server for my web application using NodeJS, ExpressJS and JWT as the authentication. My query is regarding the best practice to identify the owner of the JWT token in the server without having to send a payload from the web application - as this limits the use of the GET HTTP method which I don't want to do.
You may ask why do I want to identify the owner of the JWT token, well all of the example tutorials from which I have learnt from have all taken the user to be the object required, which makes sense to simply have an endpoint saying GET /user/:userId. However for other user specific endpoints, such as a blog post, I really don't want to resort to GET /user/:userId/blog/:id and hence the pickle I'm in.
It is possible that I am overthinking this and that the simplest solution is staring me in my face - to which I apologise. But I have thought about a few ideas, including the one I am currently using, and I would like your feedback on it or if there is a better way of doing it.
Scenario: User is already logged in and they would like to view their blog at the endpoint GET /blog/:id but the server needs to validate that the user have access to view that blog
1. Pass the user id inside the header (The one I'm currently using)
This is probably the simplest way to do it. I simply include the User ID inside the headers.
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwtToken}`,
UserId: userId
}
Is it good practice to enforce the need of sending additional data inside the header for all calls to the server, and more importantly is it safe to do so?
2. Force all endpoints to include /user/:userId
Another simple one so that it becomes GET /user/:userId/blog/:id. While this is fine, I just feel icky with the solution. But if this is considered the common practice, then so be it.
3. Cache the token against the user ID
This is probably the "best" solution I can think of as it additionally serves as a another layer of authentication, and also easily identifies the owner of the token. However it adds another layer of complexity to the server which is fine if necessary, but I always feel that less is better.
4. Temporarily save the token in the DB
Similar to the number 3, and is probably even easier than having to add a caching layer. However I don't know if there are any implications of using the DB as a temporary storage.
So that's where I'm currently at. I very much appreciate your time and response. Thank you in advance.
The user id is normally encoded in the JWT token and the JWT middleware then sets the attribute request.user.
when user login , from backend you have to provide jwt token :
jwt.sign(payload,JWT_SECRET_KEY,{ expiresIn: JWT_EXPIRATION },(err, token) => {
res.json({
success: true,
token: 'Bearer ' + token
});
});
now from frontend side you have to save this token inside cookie or localStorage
now when you have to access data which requires user id , for that you have to pass below headers in request :
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${jwtToken}`,
}
now you have to define route that requires user id like below :
router.get('/user', passport.authenticate('jwt', { session: false }),controllers.auth.user);
and you can simply get user information in req.user
code for controllers.auth.user :
module.exports=(req,res)=>{
res.json({
user:req.user
});
}
passport middleware will handle request that requires user is
authenticated or not , if user is authenticated you can get user in
req.user
passport.authenticate('jwt', { session: false })
I am new to Next.js and I am struggling with the authentication system using jwt token. I want to know what is the best / standard way to store the jwt token and routing with the authentication system. I have been trying different approaches, from different tutorials/articles, but do not quite understand it. Here are what I have tried.
When the user login, it sends username/password to a separated API server (ex. new project that handles backend stuff), the server will respond with the access-token, then in Next.js project, I set the cookie with that received token. In Next.js project, protected routes will be wrapped with a withAuth hoc, which will check for the token in a cookie. The problem with this approach is that it is vulnerable to XSS because the cookie has no httpOnly flag.
This is similar to 1.) but using localStorage, the problem is access-token could not be sent to the server on the first request. (This one I'm not sure, but in my understanding, in every HTTP request, I must stick my access-token manually, so what about requests that I have no control over? ex. first request or using <a> tag).
I wrote authentication backend inside Next.js server (custom express server). When the user login, the server will validate it and then set an httpOnly cookie. Then the problem is, with client-side routing (go to URL using Next.js Router), it could not check for token. For example, if a page is wrapped with withAuth hoc, but it cannot access the token inside cookies with javascript.
And I've seen a lot of people, in getInitialProps of the protected route, they only check for existence token in cookie / localStorage, then what if the token is being revoked or blacklisted, how do they handle it because they did not send the token to the server? Or do I have to send the token to the server in every client-side page change?
Since we are on quarantine I have enough time to answer this question. It will be a long answer.
Next.js uses the App component to initialize the pages. _app page is responsible for rendering our pages. We authenticate users on _app.js because anything that we return from getInitialProps can be accessed by all of the other pages. We authenticate user here, authentication decision will be passed to pages, from pages to header, so each page can decide if the user is authenticated or not. (Note that it could be done with redux without prop drilling but it would make the answer more complex)
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {};
const user = process.browser
? await auth0.clientAuth()
: await auth0.serverAuth(ctx.req); // I explain down below
//this will be sent to all the components
const auth = { user, isAuthenticated: !!user };
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps, auth };
}
render() {
const { Component, pageProps, auth } = this.props;
return <Component {...pageProps} auth={auth} />;
}
}
If we are on the browser and need to check if a user is authenticated, we just retrieve the cookie from the browser, which is easy. But we always have to verify the token. It is the same process used by browser and server. I will explain down below. But if we are on the server. we have no access to the cookies in the browser. But we can read from the "req" object because cookies are attached to the req.header.cookie. this is how we access to cookies on the server.
async serverAuth(req) {
// console.log(req.headers.cookie) to check
if (req.headers.cookie) {
const token = getCookieFromReq(req, "jwt");
const verifiedToken = await this.verifyToken(token);
return verifiedToken;
}
return undefined;
}
here is getCookieFromReq(). remember we have to think functional.
const getCookieFromReq = (req, cookieKey) => {
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
Once we get the cookie, we have to decode it, extract the expiration time to see if it is valid or not. this part is easy. Another thing we have to check is if the signature of the jwt is valid. Symmetric or asymmetric algorithms are used to sign the jwt. You have to use private keys to validate the signature of symmetric algorithms. RS256 is the default asymmetric algorithms for APIs. Servers that use RS256, provide you with a link to get jwt to use the keys to validate the signature. You can either use [jwks-rsa][1] or you can do on your own. You have to create a certificate and then verify if the token is valid.
Assume that our user authenticated now. You said, "And I've seen a lot of people, in getInitialProps of the protected route, they only check for existence token in cookie / localStorage,". We use protected routes to give access only to the authorized users. In order to access those routes, users have to show their jwt tokens and express.js uses middlewares to check if the user's token is valid. Since you have seen a lot of examples, I will skip this part.
"then what if the token is being revoked or blacklisted, how do they handle it because they did not send the token to the server? Or do I have to send the token to a server in every client-side page changing?"
with verifying token process we are 100% sure if the token is valid or not. When a client asks the server to access some secret data, the client has to send the token to the server. Imagine when you mount the component, component asks the server to get some data from the protected routes. The server will extract the req object, take the jwt and use it to fetch data from the protected routes. Implementation of the fetching data for browser and server are different. And if the browser makes a request, it just needs the relative path but the server needs an absolute path. As you should know fetching data is done getInitialProps() of the component and this function executed on both client and server. here is how you should implement it. I just attached the getInitialProps() part.
MyComponent.getInitialProps = async (ctx) => {
const another = await getSecretData(ctx.req);
//reuslt of fetching data is passed to component as props
return { superValue: another };
};
const getCookieFromReq = (req, cookieKey) => {
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
const setAuthHeader = (req) => {
const token = req ? getCookieFromReq(req, "jwt") : Cookies.getJSON("jwt");
if (token) {
return {
headers: { authorization: `Bearer ${token}` },
};
}
return undefined;
};
export const getSecretData = async (req) => {
const url = req ? "http://localhost:3000/api/v1/secret" : "/api/v1/secret";
return await axios.get(url, setAuthHeader(req)).then((res) => res.data);
};
[1]: https://www.npmjs.com/package/jwks-rsa
With the introduction of Next.JS v8, there are examples placed in the NextJS example page. The basic idea to follow is:
JWT
Using cookies to store the token (you may choose to further encrypt it or not)
Sending the cookies as authorization headers
OAuth
Using a third-party authentication service such as OAuth2.0
Using Passport
This question might need an updated answer, now middlewares are there in Next.js 12 (october 2021): https://nextjs.org/docs/middleware
I am drafting a comprehensive answer to explain auth in Next.js more deeply, you can follow the progress there on GitHub
Here I'll try to propose a summary for Next.js, using middlewares.
Verifying the token after auth and redirecting accordingly
Most of the answer from #Yilmaz from april 2020 is still relevant. However, previously, we had to use getInitialProps in _app to process the request OR a custom server.
This is no longer the case.. Using a middleware let's you achieve a similar purpose, with cleaner code. Because middleware are specifically designed for such use cases.
Here, I suppose you get a JWT access token using an asymetrical algorithm like RS256, exactly like in this previous answer.
Here is a possible implementation:
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
const removeCookie = (res: NextResponse, cookieName: string) => {
res.headers.append("Set-Cookie", `${cookieName}=; Max-Age=-1; Path=/`);
return res;
};
export default async function middleware(
req: NextRequest,
ev: NextFetchEvent
) {
const { pathname } = req.nextUrl;
const isPublic = isPublicRoute(pathname);
if (isPublic) {
return NextResponse.next();
}
const accessToken = req.cookies[TOKEN_PATH];
if (!accessToken) {
return NextResponse.redirect(LOGIN_HREF);
}
const isValidToken = await checkAccessToken(accessToken);
if (!isValidToken) {
let res = NextResponse.redirect(LOGIN_HREF);
res = removeCookie(res, TOKEN_PATH);
return res;
}
return NextResponse.next();
}
How to verify the token
In my example, the checkAccessToken should verify the token (not decode, verify the signature).
This is where things are the most complicated imo.
When using the RSA256 algorithm
You also get a PUBLIC certificate (in addition to the SECRET key that must be... kept secret). Eventhough you do the check in the middleware, which is private and server-only code, that's good news because it means you could even use it in the browser, in theory.
So, you can either fetch the token validation endpoint provided by your auth server, or verify the token yourself.
Fetching is not the recommended option because it might break Vercel/Next edge capabilities and add latency, according to the documentation.
I must admit that I did not succeed to verify the token yet using Next.js :) I'll update this answer if I manage to have a code sample that works.
When using a symmetrical encryption
You have only a PRIVATE secret passphrase. It means that the decoding have to happen server-side (good news, you are writing a middleware).
Login/logout
This doesn't change with middlewares. You store your access token as an httpOnly cookie. When logging out, you unset this cookie.
Managing those Set-Cookies headers are the responsibility of your auth server.
This is a basic workflow but it should work. You can then add a refresh token in the mix with a similar approach.
About token revokation
If you verify the token in your middleware, there is no immediate revokation mechanism for the access token. Because there is no call to a database.
Therefore, in this scenario, you'd want to opt-in for short lived access token (eg 5 minutes) coupled with a refresh token. You can revoke the refresh token, so basically revoking works but takes a few minutes.
If a 3rd party server verifies the token: then it could check for blacklisted tokens.
Caveats
Also, some piece of advice: most articles, tutorials etc. online are focused on server-to-server communication. Or client-to-API. They completely suck when it comes to check authentication before accessing web pages.
For instance, setting the Authorization header is not possible in the browser. It works only when communicating with an API. Cookies are mandatory for web pages.
Even then, if this API is meant to be called from a browser, it should preferably accept a cookie.
When discussing with experts on the field, you need to always clarify the Next.js use case.
Open questions: about session-based authentication
Some frameworks seem to prefer relying on the database. They store a hashed token in the db, which acts as a session. If you want to check auth, you need a server that will check the user's token against the stored token (= checking that there is an active session with this token).
I am thinking of Meteor for instance.
I couldn't find the name of this mechanism and its actual relation to JWT however. Are they simply variations of the JWT approach?
Next.js official authentication doc is not showing middlewares at the time of writing, but instead use getServerSideProps. I really don't like this pattern.
It uses a kind of session system but I am not clear about the internals of it, I am not even sure of the name (is that session-based auth?).
Vercel edge handles examples shows how to secure an API route, but not a page (at the time of writing)
Is it possible to use Auth0 with CouchDB or Cloudant? If so, does anyone know a tutorial, code sample or github project for examples?
This question was already asked in the Auth0-Forum (not by me) but with no response: https://auth0.com/forum/t/can-you-use-auth0-with-couchdb-or-cloudant/3127
In my special case i want to connect a Ionic 2 App with Auth0 to a CouchDB instance without a middle (API) layer.
Any help would be appreciated!
There's the couch_jwt_auth plugin which allows users to authenticate via JWT tokens and hence allows this kind of scenario. There's even an example project on how to use it with pouchdb and Auth0, so guess this could be kind of useful for you.
I'm using the same plugin to authenticate users and it works flawlessly. I actually maintain a fork of it which allows JWT signature verification with asymmetric keys (RS256) - And yeah once I feel confident enough there'll be a pull request.
Will the user have its own database in couchdb?
Cause without a server side middleware you won't be able to limit access to only the users data.
If thats the case you could consider using oauth for it.
I am not deep into Auth0 but it seems to support it https://auth0.com/docs/oauth2-examples
as CouchDB too http://docs.couchdb.org/en/stable/api/server/authn.html#oauth-authentication
CouchDB 3.1 supports JWT auth out of the box.
The Couch docs on JWT auth are here.
Basically, update the [chttpd] section of the configuration to include the JWT section below. Well, you can delete default and/or cookie, but they're quite useful to keep around:
[chttpd]
authentication_handlers = {chttpd_auth, cookie_authentication_handler}, {chttpd_auth, jwt_authentication_handler}, {chttpd_auth, default_authentication_handler}
Then add this section, which is mentioned without example in the docs:
[jwt_auth]
; List of claims to validate
; As of couch 3.1, can NOT require issuer:
; required_claims = {iss, "http://issuer"} <-- does not work
; I left it blank and it's fine
required_claims =
and then add or uncomment this section:
[jwt_keys]
; Configure at least one key here if using the JWT auth handler.
; I found I had to inclue the 'kid' claim, even if it is just "_default".
; REMINDER: BASE64 ENCODED!
hmac:_default = aGVsbG8=
hmac:demo = aGVsbG8=
That's "hello", base64-encoded.
To get a JWT for this, do something like:
// once off...
const secret = 'hello'
// then, in an express-jwt handler somewhere...
const token = sign(data, secret, { subject: user.name, audience, algorithm, keyid: 'demo' });
return res.send({ error: null, token })
I found that subject (sub), audience (aud), algorithm (alg) and keyid (kid) all had to be included as JWT claims or CouchDB wouldn't accept the auth.
Try this: https://github.com/softapalvelin/couch_jwt_auth This plugin allows users to authenticate via JWT Tokens.
I'm using JWT (jsonwebtoken) package with node for token handling.
Is there any way to "logout"/revoke/invalidate a token?
If you just want to log the current user out, like #Massimiliano said, it's often implemented by deleting the token from whatever client-side store you've been using to keep track of it (eg. cookies).
If you need revocation for security reasons, here's an article that talks about why you might not want to go with JWT's in the first place, and instead use regular, opaque Bearer tokens (implemented by storing them in a database, often using a secure random string as their ID).
https://www.dinochiesa.net/?p=1388
Basically, they aren't revocable without adding a database lookup (negating one of the main reasons to use JWT's to begin with). And in the case of needing to revoke via a user interface, you're going to want to be able to query for tokens by user_id or group_id or similar, so you'll want to be able to index tokens by that information, instead of having it be opaque in the JWT claims.
with jwt authentication there is no need to insert the token in a database, if you want to revoke/logout/invalidate a token, just delete it locally in the browser there where you have decided to store it (cookie or header), in that case at the next call of whatever route (protected by authentication) the server, if had a good programmer, should ask you authentication again.
Hope this help, ciao.
I like that had this problem, I decided it this way:
First i create model session for db, like this:
var SessionSchema = new Schema({
user_id: { type: String, required: true },
tokenHash: { type: String, required: true }
});
After create token i save session, when user do something in my "checkSession" midleware i search token in db, if he exist i verify him, if not exist send auth_error, if token exist in db but expired, i remove him from db and send auth_error, if i delete or block user i remove session from db to.
Try, maybe this help you.
I am using a combination of Node.js and Passportjs on the server-side, and Emberjs on the client side for an app. My current Authentication strategy is to use Passport-Local to authenticate users with normal email/password combinations as standard, and then hook in a session creation mechanism to generate an Authentication Token, which is saved into a separate table, and passed back to the user for use in any further protected routes. (passed in the REST header).
Creation of the token is fine, i'm doing that without issue, however i'm struggling to work out if I need an extra step.
Currently, I generate the token with node-jwt-simple by using a random node-uuid pass as the payload, and the users UID (another node-uuid) as the secret. I then save this to a $.cookie on the clientside, and to a table on the serverside, along with a creation date.
Obviously, one of the steps in node-jwt-simple is to encode the token. There is also a decode function provided. My question is, do I need to decode the token into something when I am doing my auth checking, or is simply checking the user's session cookie (REST header) for a match against the token in the database sufficient? I wouldn't want to go to all the effort of having generated a token, only to then miss an important step, but i'm not seeing how I could decode it into anything that would provide any additional useful security.
UPDATE:
I think I worked this out last night:
The solution seems to be to use the User's UID as the payload for JWT, with a static string as the secret (taken from something like a server environment variable or similar), and then only store the encoded token in the database. Pass the token back to the client for re-auth, then when the client attempts to access a protected route, they must pass their UID along with the encoded token to the server, which is then decoded, and the decoded payload compared to the UID that has been passed. If they match, the auth is successful, otherwise the token is destroyed and the user has to log in again.
By doing this, it makes the store of tokens effectively useless without knowing either the Secret key, or having the User's UID, but makes the auth process more secure.
If you don't validate the token, you could as well create some other random data to use as a session cookie, as long as it is unique and cannot be guessed by clients.
But as you already made a lot of effort, you could encode something useful in the token which tells you how long it is valid, e.g. an exp field, so you don't have to read from the database.
I'm not sure if I fully understand your JWT, but the problem I see is that you need information to decode the token which is probably not at your hand. So you have to do a search in your database.
I think it would be sufficient to use some random session key, e.g. following function:
var crypto = require('crypto');
/**
* Create random bytes and encode base64url.
* #param {int} [lengthInBytes=40] the size of the raw token in bytes
* (will be longer since base64url-encoded)
* #param {function} callback node-style callback-function;
* data-parameter is a string w/ a shortened (no trailing ==)
* base64url-encoded string of the generated bytes.
*/
exports.createRandomToken = function createRandomToken(lengthInBytes, callback) {
if (typeof lengthInBytes === 'function') {
callback = lengthInBytes;
lengthInBytes = 40;
}
crypto.randomBytes(lengthInBytes, function (ex, buf) {
if (ex) {
callback(ex);
return;
}
callback(null, buf.toString('base64')
.replace(/\//g, '_')
.replace(/\+/g, '-')
.replace(/=/g, ''));
});
};
Set expire time of the token as small as possible(5 min, 30 min, nor months neither years).
Use refresh token to get a new token and update refresh token every time you update old token (and when user is logged in, no doubt)
Do not store passwords, credit card numbers and any confidential informations in the token ( I'm shure, you know it :) )
Store all necessary information for checking privileges ( or checking ip, for example ). It is good for REST API and horizontal scaling.