@kishorgembali I couldn't get the await csrf(); lines to work. Instead, I had to chain the middleware, but that could be an issue with our specific containerized deployment.
In the end, we also decided against double submit because it seemed the Next.js API routes were always directly accessible even then from any browser so we didn't want that.
We went with the synchronizer token pattern in the end. To achieve that, we basically use the Next.js server to hold the CSRF secret and then the middleware just confirms the CSRF token against that.
ohk. Got it. Thank you @dejvo. Is it possible to share any reference or sample implementation, I am new to Next JS, dont want to reinvent the wheel. Just checking if it is something available. Appreciate your quick response.
Basically the way the CSRF is implemented is like this using the csrf NPM module:
import Tokens from 'csrf';
import { GlobalRef } from './global-ref';
const tokens = new Tokens();
const secret = new GlobalRef('domain.csrfSecret');
export const createSecret = () => {
secret.value = tokens.secretSync();
};
export const resetSecret = () => {
secret.value = undefined;
};
export const createToken = () => {
if (!secret.value) {
return new Error('No secret set');
}
return tokens.create(secret.value as string);
};
export const verifyToken = (csrfToken: string) => {
if (!secret.value) {
return new Error('No secret set');
}
return tokens.verify(secret.value as string, csrfToken);
};
It uses this GlobalRef hack I found online and tweaked to store the CSRF secret:
export class GlobalRef<T> {
private readonly sym: symbol;
constructor(uniqueName: string) {
this.sym = Symbol.for(uniqueName);
}
get value() {
return (global as never)[this.sym] as T;
}
set value(value: T) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(global as any)[this.sym] = value;
}
}
Then you can use those functions in your API endpoints in Next.js. Like I said, I had trouble implementing the middleware how it is in the documentation so I used a callback like this:
export const verifyCsrf = async (
req: NextApiRequest,
res: NextApiResponse,
callback
) => {
if (req.headers['csrf-token']) {
if (verifyCsrfToken(req.headers['csrf-token'].toString())) {
await callback();
} else {
console.error('No permissions to perform the request');
res.status(403).json({
code: 'access_denied',
message: 'No permissions to perform the request',
});
}
} else {
console.error('No permissions to perform the request');
res.status(403).json({
code: 'access_denied',
message: 'No permissions to perform the request',
});
}
};
Bingo that was it! Thanks for the tutorial. Hopefully I can get it working with my project.
Thanks Adel. It is really helpful.
@dejvo - I am going to implement this as part of a POC, are you able to make it work. Just would like to know if you come across any other challenges.
@kishorgembali I couldn't get the
await csrf();
lines to work. Instead, I had to chain the middleware, but that could be an issue with our specific containerized deployment.In the end, we also decided against double submit because it seemed the Next.js API routes were always directly accessible even then from any browser so we didn't want that.
We went with the synchronizer token pattern in the end. To achieve that, we basically use the Next.js server to hold the CSRF secret and then the middleware just confirms the CSRF token against that.
ohk. Got it. Thank you @dejvo. Is it possible to share any reference or sample implementation, I am new to Next JS, dont want to reinvent the wheel. Just checking if it is something available. Appreciate your quick response.
Basically the way the CSRF is implemented is like this using the
csrf
NPM module:It uses this
GlobalRef
hack I found online and tweaked to store the CSRF secret:Then you can use those functions in your API endpoints in Next.js. Like I said, I had trouble implementing the middleware how it is in the documentation so I used a callback like this:
And then call it like this in an endpoint: