Is the following a secure implementation of CSRF token verification? Specifically, I want to call this Stripe API endpoint:
https://connect.stripe.com/express/oauth/authorize?redirect_uri=https://example.com&client_id=ca_11111&state={STATE_VALUE}
and the Stripe docs say
To prevent CSRF attacks, add the state parameter, passing along a unique token as the value. We’ll include the state you gave us when we redirect the user back to your site.
I am thinking of implementing like this:
- browser generates a random token and stores it in a cookie (or local storage)
- browser calls the above endpoint
- stripe redirects to my application at https://example.com
- when I get the response back, I check the contents of the state parameter and compare it to the value stored in local storage, or in my cookie.
Is this a secure/correct implementation of CSRF? Or do I need to somehow involve a backend service?
(Stripe docs here: https://stripe.com/docs/connect/express-accounts)
Notice that in the Rocket Ride example written by Stripe themselves in NodeJS, the state is just calculated on the server as a random string and added to the session of the user. When Stripe responds at the redirect_uri they just compare the state from the user's session with the state included in the response. The following two code snippets are taken from the following - https://github.com/stripe/stripe-connect-rocketrides/blob/master/server/routes/pilots/stripe.js
/**
* GET /pilots/stripe/authorize
*
* Redirect to Stripe to set up payments.
*/
router.get('/authorize', pilotRequired, (req, res) => {
// Generate a random string as `state` to protect from CSRF and include it in the session
req.session.state = Math.random()
.toString(36)
.slice(2);
// more stuff below
});
And for the check -
/**
* GET /pilots/stripe/token
*
* Connect the new Stripe account to the platform account.
*/
router.get('/token', pilotRequired, async (req, res, next) => {
// Check the `state` we got back equals the one we generated before proceeding (to protect from CSRF)
if (req.session.state != req.query.state) {
return res.redirect('/pilots/signup');
}
// more stuff below
});
Related
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)
Sorry for bad English.
To generate JWT-token i'm using
jwt = require('jsonwebtoken');
/* some code */
//function to create jwt-token
function createToken(user) {
return jwt.sign(user, config.secret, { expiresIn: 60*60*5 });
}
/* some code */
//after registration/authorization (if success) createToken token function is calling
createToken(user);
I would like to learn the best practices, what data must be passed to a function to create the token.
For example, can this be as:
login (John) and id( ObjectId("5821d94dbb021a1360582da3") when using MongoDb)?
And here, I think, will be relevant question:
If I store in token some information that allows initialize user, I can pull its data from the database. Is this correct, initialize the user from authorization header from JWT? For initialization I'm using express-jwt, which, if successful, sets req.user?
Thanks.
you can put the generated token in browser local storage, if you are using browser. You can also store session values in the token itself, those values will be decoded on the server side by nodejs.
I am new to using nodejs and am working on a project where I can make a custom playlist by adding one song at a time via a search. I've been able to get the code to do the searching and grabbing the proper ids done, but when trying to add to the playlist, I'm getting an error about the scope being wrong. Long story short, I was doing the wrong type of authentication.
So I read up on the spotify-web-api-node documents, but I'm getting lost between generating the authorization url and then getting the response, which is then used by another method to get the authorization token. I'm not sure if there is another method I'm not seeing that will make the request, or if I'm just supposed to do a regular request out via normal node methods.
The code I'm using is pretty much a copy-paste from the following link (https://github.com/thelinmichael/spotify-web-api-node#authorization), where the second box with the header "The below uses a hardcoded authorization code..." is where I'm lost... I need to get that code from the response, but I'm not sure how I'm to send the request to even get the response, the createAuthorizeURL method just seems to make the actual url but not send it.
I believe the confusion stems from the way the Authorization Code flow works, and the way I've written the documentation for the node wrapper. The purpose of the createAuthorizeURL method is to help you create the URL that you need to forward the user to.
From the same piece of documentation that you linked to:
In order to get permissions, you need to direct the user to our Accounts service.
Generate the URL by using the wrapper's authorization URL method.
So let's say that the user starts out by entering your site, http://www.jd.example.com. It'll have a Spotify styled button that says Login here. The button links to the URL that the createAuthorizeURL has generated. One very important part of the URL is the redirect_uri query parameter. For example, the URL that you would generate would look something like
https://accounts.spotify.com:443/authorize?client_id=5fe01282e44241328a84e7c5cc169165&
response_type=code&redirect_uri=https://www.jd.example.com/callback&
scope=playlist-modify-public
When the user clicks the button they will be taken through the authentication and authorization flow on Spotify's site (accounts.spotify.com/). However, when they've finished this flow, they will be directed by Spotify to the same redirect_uri that you gave in the createAuthorizeURL, e.g. https://www.jd.example.com/callback.
This means that your web server (e.g. Express) needs to be able to handle a request to the redirect_uri. If your web server was indeed Express, it may look like this.
/* Some express.js setup here */
/* Some spotify-web-api-node setup here */
/* Handle authorization callback from Spotify */
app.get('/callback', function(req, res) {
/* Read query parameters */
var code = req.query.code; // Read the authorization code from the query parameters
var state = req.query.state; // (Optional) Read the state from the query parameter
/* Get the access token! */
spotifyApi.authorizationCodeGrant(code)
.then(function(data) {
console.log('The token expires in ' + data['expires_in']);
console.log('The access token is ' + data['access_token']);
console.log('The refresh token is ' + data['refresh_token']);
/* Ok. We've got the access token!
Save the access token for this user somewhere so that you can use it again.
Cookie? Local storage?
*/
/* Redirecting back to the main page! :-) */
res.redirect('/');
}, function(err) {
res.status(err.code);
res.send(err.message);
}
});
});
Hope this helps!
I'm working in a web app which handle resources from a Mongo database, for such resources I'd like to offer an API, so a future mobile application can seize it or consume it from a raw client.
However I'd like to have web app consuming same API, here is where I get a bit confused about how to properly implement this.
Here is what I've done so far:
API Auth:
app.route('/api/auth/')
.post(function (request,response) {
var email = request.body.email;
var password = request.body.password;
var login = new Account({"local.email":email,"local.password":password});
Account.findOne({"local.email":email}, function (err,user) {
if (err) {
response.send(500);
}
if (!user) {
response.send(404);
}
else {
user.validPassword(password, function (err,matched) {
if (err) {
response.send(500);
}
if (matched) {
var uuidToken = uuid.v4();
redisClient.set(uuidToken,user._id,redis.print);
redisClient.expire(user._id,100);
response.send(uuid);
}
else {
response.send(403);
}
});
}
});
});
So basically I receive consumers username and password, I authenticate it against database, If it matches I reply a token, (actually an UUID). That token gets stored at Redis paired with the user id in databse. Every future request to any API route will verify for such token existance.
Here I wonder:
How should I manage the token TTL, and renewal upon future requests?
How can I control requests per time windows limits?
Is there any security caveat in the approach I'm taking?
Website Auth:
Basically I perform SAME username-password authentication against database and I then:
1. Start a new server session.
2. Naturally, offer back a cookie with session ID.
3. I create then the Redis UUID and user ID record, which API will check. I guess this is OK as there's any sense in requesting POST /api/auth authenticating again.
Here I wonder:
Is this a best approach?
Should I include any token salt to distinguish a pure API consuming request from a request from web app?
Is there any security caveat in the approach I'm taking?
Should I include more tokens?
This is example of POST /login:
app.route('/login')
.post(function (request,response,next) {
var email = request.body.email;
var password = request.body.password;
var login = new Account({"local.email":email,"local.password":password});
Account.findOne({"local.email":email}, function (err,user) {
if (err) {
response.redirect('/error');
}
if (!user) {
var cookie = request.cookies.userAttempts;
if (cookie === undefined) {
response.cookie('userAttempts',1);
}
else {
response.cookie('userAttempts',(++cookie));
}
response.redirect('/');
}
else {
user.validPassword(password, function (err,matched) {
if (err) {
// Redirect error site or show err message.
response.redirect('/error');
}
if (matched) {
var session = request.session;
session.userid = user._id;
var uuidToken = uuid.v4();
redisClient.set(uuidToken,user._id,redis.print);
redisClient.expire(uuidToken,900);
response.cookie('email',email);
response.redirect('/start');
}
else {
var cookie = request.cookies.passwordAttemps;
if (cookie === undefined)
response.cookie('passwordAttemps',1);
else {
var attemps = ++request.cookies.attemps
response.cookie('passwordAttemps', attemps)
}
response.redirect('/');
}
});
}
});
})
I think I could get rid of using and writing a typical session implementation and depend somehow on the similar token based auth the API has.
What you have there is on the right track and basically replaces some of the functionality of cookies. There are a few things to consider though, and you've touched on some of them already.
While using a UUID (v4 I'm guessing?) is good in that it's nondeterministic and "random", on its own the token is worthless. Should redis lose data the token no longer has any context. Nor can you enforce expirations without help from redis. Compare this to a JWT which can carry context on its own, can be decrypted by anybody with the correct key, can handle expirations, and can enforce further common application level constraints (issuer, audience, etc).
Rate limiting. There are a number of ways to handle this and few of them are tied directly to your choice of token scheme aside from the fact that you'd probably use the token as the key to identify a user across requests in the rate limiter.
Transparently passing the token in both a web app and on other clients (mobile app, desktop app, etc) can be a huge pain. In order to access private resources the user will need to pass the token in the request somewhere, likely the headers, and in the case of a web app this means manual intervention on your part to include the token in each request. This means hand coded ajax requests for all authenticated requests. While this can be annoying, at least it's possible to do, and if you're writing a single page app it's likely you'd do that anyways. The same can be said for any mobile or desktop client. Since you already have to make the HTTP request directly in code anyways, why does it matter? Now imagine the scenario where an HTTP GET endpoint, which returns an html page, can only be accessed with proper authentication. In the case of a web app the user is very likely going to access this via a browser redirect or by typing it directly into the URL bar. How is the token added to the request? Other than using cookies, which you're explicitly not using because mobile and desktop clients do not implement them, this is not really possible. However, if your API clients can always modify the HTTP request structure this isn't really a problem.
Now for a shameless plug, our team has a library we use for this. It's mostly used internally and as such is pretty opinionated on its dependencies (express, redis), but hopefully it can help you here. In fact, that library is pretty much just a JWT wrapper around what you have in place. If you decide to use it and notice any issues or deficiencies feel free to file any issues on github. Otherwise there are a whole bunch of other JWT based session management modules on npm that look promising. I would check those out regardless as there are very likely better modules out there than ours. Again, ours is used internally and came about from a pretty specific set of use cases so the chances that it captures all of yours are pretty slim. On the other hand, it sounds like you're using a similar stack so maybe the shoe fits.
If you do use ours it may seem odd that there's a split in the API surface on that module in that you can choose to store data directly in the JWT claims or in redis. This was deliberate and I think your examples illustrate a good use case for both sides. Typically what we do is store the user's email and name in the JWT claims, then store more dynamic session data in redis on their session. For example, upon logging in you'd add the issuer, audience, and user's email to the JWT claims but leave off anything related to "userAttempts". Then upon failed attempts you would add or modify the "userAttempts" on the session data stored in redis related to that JWT. Once a JWT is set it's not possible to modify its contents without generating a new one, so be aware that if you decide to keep relatively dynamic data in the JWT you'll have a constant exchange of old and new JWT's between the server and client.
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.