mirror of
https://github.com/outline/outline.git
synced 2025-03-28 14:34:35 +00:00
fix: Previously provisioned JWT's should be revoked on signout (#3639)
* feat: auth.delete endpoint * test
This commit is contained in:
@ -253,6 +253,12 @@ export default class AuthStore {
|
||||
|
||||
@action
|
||||
logout = async (savePath = false) => {
|
||||
if (!this.token) {
|
||||
return;
|
||||
}
|
||||
|
||||
client.post(`/auth.delete`);
|
||||
|
||||
// remove user and team from localStorage
|
||||
Storage.set(AUTH_STORE, {
|
||||
user: null,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SaveOptions } from "sequelize";
|
||||
import type { SaveOptions } from "sequelize";
|
||||
import {
|
||||
ForeignKey,
|
||||
AfterSave,
|
||||
@ -159,6 +159,7 @@ class Event extends IdModel {
|
||||
"users.create",
|
||||
"users.update",
|
||||
"users.signin",
|
||||
"users.signout",
|
||||
"users.promote",
|
||||
"users.demote",
|
||||
"users.invite",
|
||||
|
@ -48,6 +48,30 @@ describe("#auth.info", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#auth.delete", () => {
|
||||
it("should make the access token unusable", async () => {
|
||||
const user = await buildUser();
|
||||
const res = await server.post("/api/auth.delete", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
},
|
||||
});
|
||||
expect(res.status).toEqual(200);
|
||||
|
||||
const res2 = await server.post("/api/auth.info", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
},
|
||||
});
|
||||
expect(res2.status).toEqual(401);
|
||||
});
|
||||
|
||||
it("should require authentication", async () => {
|
||||
const res = await server.post("/api/auth.delete");
|
||||
expect(res.status).toEqual(401);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#auth.config", () => {
|
||||
it("should return available SSO providers", async () => {
|
||||
env.DEPLOYMENT = "hosted";
|
||||
|
@ -2,9 +2,10 @@ import invariant from "invariant";
|
||||
import Router from "koa-router";
|
||||
import { find } from "lodash";
|
||||
import { parseDomain } from "@shared/utils/domains";
|
||||
import { sequelize } from "@server/database/sequelize";
|
||||
import env from "@server/env";
|
||||
import auth from "@server/middlewares/authentication";
|
||||
import { Team, TeamDomain } from "@server/models";
|
||||
import { Event, Team, TeamDomain } from "@server/models";
|
||||
import { presentUser, presentTeam, presentPolicies } from "@server/presenters";
|
||||
import ValidateSSOAccessTask from "@server/queues/tasks/ValidateSSOAccessTask";
|
||||
import providers from "../auth/providers";
|
||||
@ -125,4 +126,31 @@ router.post("auth.info", auth(), async (ctx) => {
|
||||
};
|
||||
});
|
||||
|
||||
router.post("auth.delete", auth(), async (ctx) => {
|
||||
const { user } = ctx.state;
|
||||
|
||||
await sequelize.transaction(async (transaction) => {
|
||||
await user.rotateJwtSecret({ transaction });
|
||||
await Event.create(
|
||||
{
|
||||
name: "users.signout",
|
||||
actorId: user.id,
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
data: {
|
||||
name: user.name,
|
||||
},
|
||||
ip: ctx.request.ip,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
@ -13,6 +13,7 @@ export type UserEvent =
|
||||
| {
|
||||
name: "users.create" // eslint-disable-line
|
||||
| "users.signin"
|
||||
| "users.signout"
|
||||
| "users.update"
|
||||
| "users.suspend"
|
||||
| "users.activate"
|
||||
|
Reference in New Issue
Block a user