Merge pull request #1825 from automatisch/access-tokens
feat: Use persisted access tokens for authentication
This commit is contained in:
@@ -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,
|
||||
|
@@ -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 } };
|
||||
|
@@ -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,
|
||||
});
|
||||
|
||||
|
@@ -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}`,
|
||||
|
Reference in New Issue
Block a user