Skip to content

Commit 7978bc6

Browse files
revoke token access
1 parent 8bf23e0 commit 7978bc6

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

apps/client/src/routes/$workspaceId/token.tsx

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createFileRoute } from "@tanstack/react-router";
1+
import { createFileRoute, useRouter } from "@tanstack/react-router";
22
import { Duration, Effect } from "effect";
33
import { ApiClient } from "../../lib/api-client";
44
import { WEBSITE_URL } from "../../lib/constants";
@@ -29,6 +29,7 @@ export const Route = createFileRoute("/$workspaceId/token")({
2929
function RouteComponent() {
3030
const { workspaceId } = Route.useParams();
3131
const { tokens, token } = Route.useLoaderData();
32+
const router = useRouter();
3233

3334
const [, onIssueToken, issuing] = useActionEffect((formData: FormData) =>
3435
Effect.gen(function* () {
@@ -48,6 +49,21 @@ function RouteComponent() {
4849
})
4950
);
5051

52+
const [, onRevoke, revoking] = useActionEffect((formData: FormData) =>
53+
Effect.gen(function* () {
54+
const api = yield* ApiClient;
55+
56+
const clientId = formData.get("clientId") as string;
57+
58+
yield* api.client.syncAuth.revokeToken({
59+
path: { workspaceId, clientId },
60+
headers: { "x-api-key": token },
61+
});
62+
63+
yield* Effect.promise(() => router.invalidate({ sync: true }));
64+
})
65+
);
66+
5167
return (
5268
<div>
5369
<h1>Tokens</h1>
@@ -88,13 +104,26 @@ function RouteComponent() {
88104
: "N/A"}
89105
</td>
90106
<td>
91-
{token.revokedAt
92-
? new Date(token.revokedAt).toLocaleDateString(undefined, {
107+
{token.revokedAt ? (
108+
<span>
109+
{new Date(token.revokedAt).toLocaleDateString(undefined, {
93110
year: "numeric",
94111
month: "long",
95112
day: "numeric",
96-
})
97-
: "N/A"}
113+
})}
114+
</span>
115+
) : (
116+
<form action={onRevoke}>
117+
<input
118+
type="hidden"
119+
name="clientId"
120+
value={token.clientId}
121+
/>
122+
<button type="submit" disabled={revoking}>
123+
Revoke access
124+
</button>
125+
</form>
126+
)}
98127
</td>
99128
<td>
100129
<button

apps/server/src/group/sync-auth.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,34 @@ export const SyncAuthGroupLive = HttpApiBuilder.group(
199199
Effect.mapError((error) => error.message)
200200
)
201201
)
202-
.handle("revokeToken", ({ path: { workspaceId } }) =>
203-
Effect.fail("Not implemented")
202+
.handle("revokeToken", ({ path: { workspaceId, clientId } }) =>
203+
Effect.gen(function* () {
204+
yield* Effect.log(`Revoking token for workspace ${workspaceId}`);
205+
206+
const revokedAt = yield* DateTime.now;
207+
208+
yield* query({
209+
Request: Schema.Struct({
210+
workspaceId: Schema.String,
211+
clientId: Schema.String,
212+
}),
213+
execute: (db, { workspaceId, clientId }) =>
214+
db
215+
.update(tokenTable)
216+
.set({ revokedAt: DateTime.toDate(revokedAt) })
217+
.where(
218+
and(
219+
eq(tokenTable.workspaceId, workspaceId),
220+
eq(tokenTable.clientId, clientId)
221+
)
222+
),
223+
})({ workspaceId, clientId });
224+
225+
return true;
226+
}).pipe(
227+
Effect.tapErrorCause(Effect.logError),
228+
Effect.mapError((error) => error.message)
229+
)
204230
);
205231
})
206232
).pipe(

0 commit comments

Comments
 (0)