From 2163be4227d02420cb78cee71d422c77261c8c10 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 24 Apr 2024 15:04:27 +0000 Subject: [PATCH 1/4] feat: add DELETE /access-tokens/:token --- .../api/v1/access-tokens/revoke-access-token.js | 15 +++++++++++++++ packages/backend/src/models/access-token.js | 4 ++++ .../backend/src/routes/api/v1/access-tokens.js | 9 ++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.js diff --git a/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.js b/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.js new file mode 100644 index 00000000..95c7ffbc --- /dev/null +++ b/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.js @@ -0,0 +1,15 @@ +export default async (request, response) => { + const token = request.params.token; + + const accessToken = await request.currentUser + .$relatedQuery('accessTokens') + .findOne({ + token, + revoked_at: null, + }) + .throwIfNotFound(); + + await accessToken.revoke(); + + response.status(204).send(); +}; diff --git a/packages/backend/src/models/access-token.js b/packages/backend/src/models/access-token.js index 7afb4776..f40a185f 100644 --- a/packages/backend/src/models/access-token.js +++ b/packages/backend/src/models/access-token.js @@ -27,6 +27,10 @@ class AccessToken extends Base { }, }, }); + + async revoke() { + return await this.$query().patch({ revokedAt: new Date().toISOString() }); + } } export default AccessToken; diff --git a/packages/backend/src/routes/api/v1/access-tokens.js b/packages/backend/src/routes/api/v1/access-tokens.js index 8d7ed14c..b41c3e0f 100644 --- a/packages/backend/src/routes/api/v1/access-tokens.js +++ b/packages/backend/src/routes/api/v1/access-tokens.js @@ -1,9 +1,16 @@ import { Router } from 'express'; import asyncHandler from 'express-async-handler'; import createAccessTokenAction from '../../../controllers/api/v1/access-tokens/create-access-token.js'; - +import revokeAccessTokenAction from '../../../controllers/api/v1/access-tokens/revoke-access-token.js'; +import { authenticateUser } from '../../../helpers/authentication.js'; const router = Router(); router.post('/', asyncHandler(createAccessTokenAction)); +router.delete( + '/:token', + authenticateUser, + asyncHandler(revokeAccessTokenAction) +); + export default router; From 1a75d81268e33807cc155018a20c3dd59202525d Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 24 Apr 2024 15:53:52 +0000 Subject: [PATCH 2/4] test: add test for revoking access token --- .../access-tokens/revoke-access-token.test.js | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js diff --git a/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js b/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js new file mode 100644 index 00000000..b931696b --- /dev/null +++ b/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js @@ -0,0 +1,53 @@ +import { expect, describe, it, beforeEach } from 'vitest'; +import request from 'supertest'; +import app from '../../../../app.js'; +import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id'; +import { createUser } from '../../../../../test/factories/user.js'; +import AccessToken from '../../../../models/access-token.js'; + +describe('DELETE /api/v1/access-tokens/:token', () => { + let token; + beforeEach(async () => { + const currentUser = await createUser({ + email: 'user@automatisch.io', + password: 'password', + }); + token = await createAuthTokenByUserId(currentUser.id); + }); + + it('should respond with HTTP 204 with correct token', async () => { + await request(app) + .delete(`/api/v1/access-tokens/${token}`) + .set('Authorization', token) + .expect(204); + + const revokedToken = await AccessToken.query().findOne({ token }); + + expect(revokedToken).toBeDefined(); + expect(revokedToken.revokedAt).not.toBeNull(); + }); + + it('should respond with HTTP 401 with incorrect credentials', async () => { + await request(app) + .delete(`/api/v1/access-tokens/${token}`) + .set('Authorization', 'wrong-token') + .expect(401); + + const unrevokedToken = await AccessToken.query().findOne({ token }); + + expect(unrevokedToken).toBeDefined(); + expect(unrevokedToken.revokedAt).toBeNull(); + }); + + it('should respond with HTTP 404 with correct credentials, but non-valid token', async () => { + await request(app) + .delete('/api/v1/access-tokens/wrong-token') + .set('Authorization', token) + .expect(404); + + const unrevokedToken = await AccessToken.query().findOne({ token }); + + expect(unrevokedToken).toBeDefined(); + expect(unrevokedToken.revokedAt).toBeNull(); + }); +}); From 68d1719b11d0ec2884850ce1b697214c5398324d Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Fri, 26 Apr 2024 09:25:27 +0000 Subject: [PATCH 3/4] feat: add new methods to cors options --- packages/backend/src/config/cors-options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/config/cors-options.js b/packages/backend/src/config/cors-options.js index c96a2d4a..b4386b1c 100644 --- a/packages/backend/src/config/cors-options.js +++ b/packages/backend/src/config/cors-options.js @@ -2,7 +2,7 @@ import appConfig from './app.js'; const corsOptions = { origin: appConfig.webAppUrl, - methods: 'POST', + methods: 'GET,HEAD,POST,DELETE', credentials: true, optionsSuccessStatus: 200, }; From 2166a3220e45d75884552e2deea2d9e3a105a869 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Fri, 26 Apr 2024 12:02:38 +0000 Subject: [PATCH 4/4] style: add break line before variable declaration and beforeEach --- .../controllers/api/v1/access-tokens/revoke-access-token.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js b/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js index b931696b..1651418a 100644 --- a/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js +++ b/packages/backend/src/controllers/api/v1/access-tokens/revoke-access-token.test.js @@ -7,6 +7,7 @@ import AccessToken from '../../../../models/access-token.js'; describe('DELETE /api/v1/access-tokens/:token', () => { let token; + beforeEach(async () => { const currentUser = await createUser({ email: 'user@automatisch.io',