diff --git a/client/src/app/pages/dashboard/users/UserIntegrationsPage.tsx b/client/src/app/pages/dashboard/users/UserIntegrationsPage.tsx
index dae7f9f2c..5b88edd95 100644
--- a/client/src/app/pages/dashboard/users/UserIntegrationsPage.tsx
+++ b/client/src/app/pages/dashboard/users/UserIntegrationsPage.tsx
@@ -703,7 +703,32 @@ function KAIIntegrationStatus({
: `You are not authenticated with ${kaiType.toUpperCase()}`}
{data.authStatus ? (
-
There's no configuration to do here. You're all good!
+ <>
+
+
+ The below button will revoke your authentication with{" "}
+ {kaiType.toUpperCase()}.
+
+ You only need to do this if you've revoked it on {kaiType.toLowerCase()}!
+ >
) : (
You should authenticate yourself by going to{" "}
@@ -720,9 +745,7 @@ function APIKeysPage({ reqUser }: { reqUser: PublicUserDocument }) {
const [apiKeys, setApiKeys] = useState([]);
const [showModal, setShowModal] = useState(false);
- const { data, error } = useApiQuery(
- `/users/${reqUser.id}/api-tokens`
- );
+ const { data, error } = useApiQuery(`/users/${reqUser.id}/api-tokens`);
useEffect(() => {
if (data) {
diff --git a/docs/docs/api/routes/user-integrations.md b/docs/docs/api/routes/user-integrations.md
index 9c46ca301..168b91389 100644
--- a/docs/docs/api/routes/user-integrations.md
+++ b/docs/docs/api/routes/user-integrations.md
@@ -42,6 +42,32 @@ GET /api/v1/users/1/integrations/kai/flo
*****
+## Revoke this user's authentication with this kaiType
+
+`DELETE /api/v1/users/:userID/integrations/kai/:kaiType`
+
+
+**Kamaitachi Only**
+
+!!! note
+ A KaiType is either "flo", "min", or "eag". Since these three services
+ share backends, they all use the same authentication mechanisms, and share
+ endpoints like this.
+
+### Permissions
+
+- Self-key: This request must be made using Cookie authentication, which means it cannot be used with API keys.
+
+### Parameters
+
+None.
+
+### Response
+
+Empty Object.
+
+*****
+
## Update a user's access_token and refresh_token from an intermediate code.
`POST /api/v1/users/:userID/integrations/kai/:kaiType/oauth2callback`
diff --git a/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts b/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts
index 56b33ad4b..0477be033 100644
--- a/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts
+++ b/server/src/server/router/api/v1/users/_userID/integrations/kai/_kaiType/router.ts
@@ -12,7 +12,7 @@ import prValidate from "server/middleware/prudence-validate";
import { RequireKamaitachi } from "server/middleware/type-require";
import fetch from "utils/fetch";
import { NotNullish } from "utils/misc";
-import { GetKaiAuth } from "utils/queries/auth";
+import { GetKaiAuth, RevokeKaiAuth } from "utils/queries/auth";
import { GetTachiData } from "utils/req-tachi-data";
import { FormatUserDoc } from "utils/user";
import { URL } from "url";
@@ -46,6 +46,36 @@ router.get("/", async (req, res) => {
});
});
+/**
+ * Revoke your authentication for this kaiType.
+ * @note - Express's types infer arg0 of "/" to mean no params, for some reason.
+ * the generic overrides this behaviour.
+ *
+ * @name DELETE /api/v1/users/:userID/integrations/kai/:kaiType
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+router.delete("/", async (req, res) => {
+ const user = GetTachiData(req, "requestedUser");
+ const kaiType = NotNullish(req.params.kaiType).toUpperCase() as "EAG" | "FLO" | "MIN";
+
+ const authDoc = await GetKaiAuth(user.id, kaiType);
+
+ if (!authDoc) {
+ return res.status(409).json({
+ success: false,
+ description: `You are not authorised with this service.`,
+ });
+ }
+
+ await RevokeKaiAuth(user.id, kaiType);
+
+ return res.status(200).json({
+ success: true,
+ description: `Revoked authentication for ${kaiType}.`,
+ body: {},
+ });
+});
+
const KAI_OAUTH2_RETURN_SCHEMA = {
access_token: "string",
refresh_token: "string",
diff --git a/server/src/utils/queries/auth.ts b/server/src/utils/queries/auth.ts
index 07ca5ddbb..50003d1e1 100644
--- a/server/src/utils/queries/auth.ts
+++ b/server/src/utils/queries/auth.ts
@@ -10,6 +10,13 @@ export function GetKaiAuth(userID: integer, service: "EAG" | "FLO" | "MIN") {
});
}
+export function RevokeKaiAuth(userID: integer, service: "EAG" | "FLO" | "MIN") {
+ return db["kai-auth-tokens"].remove({
+ userID,
+ service,
+ });
+}
+
export async function GetKaiAuthGuaranteed(
userID: integer,
service: "EAG" | "FLO" | "MIN",