Merge pull request #1835 from automatisch/logout-endpoint
feat: add DELETE /access-tokens/:token
This commit is contained in:
@@ -2,7 +2,7 @@ import appConfig from './app.js';
|
|||||||
|
|
||||||
const corsOptions = {
|
const corsOptions = {
|
||||||
origin: appConfig.webAppUrl,
|
origin: appConfig.webAppUrl,
|
||||||
methods: 'POST',
|
methods: 'GET,HEAD,POST,DELETE',
|
||||||
credentials: true,
|
credentials: true,
|
||||||
optionsSuccessStatus: 200,
|
optionsSuccessStatus: 200,
|
||||||
};
|
};
|
||||||
|
@@ -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();
|
||||||
|
};
|
@@ -0,0 +1,54 @@
|
|||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
@@ -27,6 +27,10 @@ class AccessToken extends Base {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async revoke() {
|
||||||
|
return await this.$query().patch({ revokedAt: new Date().toISOString() });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AccessToken;
|
export default AccessToken;
|
||||||
|
@@ -1,9 +1,16 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import asyncHandler from 'express-async-handler';
|
import asyncHandler from 'express-async-handler';
|
||||||
import createAccessTokenAction from '../../../controllers/api/v1/access-tokens/create-access-token.js';
|
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();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/', asyncHandler(createAccessTokenAction));
|
router.post('/', asyncHandler(createAccessTokenAction));
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
'/:token',
|
||||||
|
authenticateUser,
|
||||||
|
asyncHandler(revokeAccessTokenAction)
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
Reference in New Issue
Block a user