mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-12 10:00:39 -04:00
feat(api): use jwt_access_token (in development) (#53997)
Co-authored-by: Tom <20648924+moT01@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
bb2efe7618
commit
aacfb281fb
86
api/src/plugins/code-flow-auth.ts
Normal file
86
api/src/plugins/code-flow-auth.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { FastifyPluginCallback, FastifyReply, FastifyRequest } from 'fastify';
|
||||
import fp from 'fastify-plugin';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { isBefore } from 'date-fns';
|
||||
import { type user } from '@prisma/client';
|
||||
|
||||
import { COOKIE_DOMAIN, JWT_SECRET } from '../utils/env';
|
||||
import { AccessToken } from '../utils/tokens';
|
||||
|
||||
declare module 'fastify' {
|
||||
interface FastifyReply {
|
||||
setAccessTokenCookie: (
|
||||
this: FastifyReply,
|
||||
accessToken: AccessToken
|
||||
) => void;
|
||||
}
|
||||
|
||||
interface FastifyRequest {
|
||||
// TODO: is the full user the correct type here?
|
||||
user?: user;
|
||||
}
|
||||
|
||||
interface FastifyInstance {
|
||||
authorize: (req: FastifyRequest, reply: FastifyReply) => void;
|
||||
}
|
||||
}
|
||||
|
||||
const codeFlowAuth: FastifyPluginCallback = (fastify, _options, done) => {
|
||||
fastify.decorateReply(
|
||||
'setAccessTokenCookie',
|
||||
function (accessToken: AccessToken) {
|
||||
const signedToken = jwt.sign({ accessToken }, JWT_SECRET);
|
||||
void this.setCookie('jwt_access_token', signedToken, {
|
||||
path: '/',
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'lax',
|
||||
domain: COOKIE_DOMAIN,
|
||||
signed: true,
|
||||
maxAge: accessToken.ttl
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const TOKEN_REQUIRED = 'Access token is required for this request';
|
||||
const TOKEN_INVALID = 'Your access token is invalid';
|
||||
const TOKEN_EXPIRED = 'Access token is no longer valid';
|
||||
|
||||
const send401 = (reply: FastifyReply, message: string) =>
|
||||
reply.status(401).send({ type: 'info', message });
|
||||
|
||||
fastify.decorate(
|
||||
'authorize',
|
||||
async function (req: FastifyRequest, reply: FastifyReply) {
|
||||
const tokenCookie = req.cookies.jwt_access_token;
|
||||
if (!tokenCookie) return send401(reply, TOKEN_REQUIRED);
|
||||
|
||||
const unsignedToken = req.unsignCookie(tokenCookie);
|
||||
if (!unsignedToken.valid) return send401(reply, TOKEN_REQUIRED);
|
||||
|
||||
const jwtAccessToken = unsignedToken.value;
|
||||
|
||||
try {
|
||||
jwt.verify(jwtAccessToken!, JWT_SECRET);
|
||||
} catch {
|
||||
return send401(reply, TOKEN_INVALID);
|
||||
}
|
||||
|
||||
const {
|
||||
accessToken: { created, ttl, userId }
|
||||
} = jwt.decode(jwtAccessToken!) as { accessToken: AccessToken };
|
||||
const valid = isBefore(Date.now(), Date.parse(created) + ttl);
|
||||
if (!valid) return send401(reply, TOKEN_EXPIRED);
|
||||
|
||||
const user = await fastify.prisma.user.findUnique({
|
||||
where: { id: userId }
|
||||
});
|
||||
if (!user) return send401(reply, TOKEN_INVALID);
|
||||
req.user = user;
|
||||
}
|
||||
);
|
||||
|
||||
done();
|
||||
};
|
||||
|
||||
export default fp(codeFlowAuth);
|
||||
Reference in New Issue
Block a user