feat: Use persisted access tokens for authentication

This commit is contained in:
Faruk AYDIN
2024-04-22 16:57:34 +02:00
parent 73c929f25e
commit 6a7cdf2570
47 changed files with 74 additions and 57 deletions

View File

@@ -1,7 +1,6 @@
import { allow, rule, shield } from 'graphql-shield';
import jwt from 'jsonwebtoken';
import appConfig from '../config/app.js';
import User from '../models/user.js';
import AccessToken from '../models/access-token.js';
export const isAuthenticated = async (_parent, _args, req) => {
const token = req.headers['authorization'];
@@ -9,10 +8,22 @@ export const isAuthenticated = async (_parent, _args, req) => {
if (token == null) return false;
try {
const { userId } = jwt.verify(token, appConfig.appSecretKey);
const accessToken = await AccessToken.query().findOne({
token,
revoked_at: null,
});
const expirationTime =
new Date(accessToken.createdAt).getTime() + accessToken.expiresIn * 1000;
if (Date.now() > expirationTime) {
return false;
}
const user = await accessToken.$relatedQuery('user');
req.currentUser = await User.query()
.findById(userId)
.findById(user.id)
.leftJoinRelated({
role: true,
permissions: true,

View File

@@ -17,7 +17,7 @@ describe('isAuthenticated', () => {
it('should return true if token is valid and there is a user', async () => {
const user = await createUser();
const token = createAuthTokenByUserId(user.id);
const token = await createAuthTokenByUserId(user.id);
const req = { headers: { authorization: token } };
expect(await isAuthenticated(null, null, req)).toBe(true);
@@ -25,7 +25,7 @@ describe('isAuthenticated', () => {
it('should return false if token is valid and but there is no user', async () => {
const user = await createUser();
const token = createAuthTokenByUserId(user.id);
const token = await createAuthTokenByUserId(user.id);
await user.$query().delete();
const req = { headers: { authorization: token } };

View File

@@ -1,10 +1,16 @@
import jwt from 'jsonwebtoken';
import appConfig from '../config/app.js';
import crypto from 'crypto';
import User from '../models/user.js';
import AccessToken from '../models/access-token.js';
const TOKEN_EXPIRES_IN = '14d';
const TOKEN_EXPIRES_IN = 14 * 24 * 60 * 60; // 14 days in seconds
const createAuthTokenByUserId = (userId) => {
const token = jwt.sign({ userId }, appConfig.appSecretKey, {
const createAuthTokenByUserId = async (userId) => {
const user = await User.query().findById(userId).throwIfNotFound();
const token = await crypto.randomBytes(48).toString('hex');
await AccessToken.query().insert({
token,
userId: user.id,
expiresIn: TOKEN_EXPIRES_IN,
});

View File

@@ -76,8 +76,8 @@ export default function configurePassport(app) {
failureRedirect: '/',
failureFlash: true,
}),
(req, res) => {
const token = createAuthTokenByUserId(req.currentUser.id);
async (req, res) => {
const token = await createAuthTokenByUserId(req.currentUser.id);
const redirectUrl = new URL(
`/login/callback?token=${token}`,