Skip to content
This repository was archived by the owner on Sep 17, 2024. It is now read-only.

Commit d2012c6

Browse files
committed
feat(tokens): Add a modify_meta script.
1 parent 801efc4 commit d2012c6

File tree

8 files changed

+158
-69
lines changed

8 files changed

+158
-69
lines changed

modules/tokens/module.json

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
11
{
2-
"name": "Tokens",
3-
"description": "Create & verify tokens for authorization purposes.",
4-
"icon": "lock",
5-
"tags": [
6-
"core",
7-
"utility"
8-
],
9-
"authors": [
10-
"rivet-gg",
11-
"NathanFlurry"
12-
],
13-
"status": "stable",
14-
"scripts": {
15-
"create": {
16-
"name": "Create Token"
17-
},
18-
"fetch": {
19-
"name": "Fetch Token",
20-
"description": "Get a token by its ID."
21-
},
22-
"fetch_by_token": {
23-
"name": "Fetch by Token",
24-
"description": "Get a token by its secret token."
25-
},
26-
"revoke": {
27-
"name": "Revoke Token",
28-
"description": "Revoke a token, preventing it from being used again."
29-
},
30-
"validate": {
31-
"name": "Validate Token",
32-
"description": "Validate a token. Throws an error if the token is invalid."
33-
},
34-
"extend": {
35-
"name": "Extend Token",
36-
"description": "Extend or remove the expiration date of a token. (Only works on valid tokens.)"
37-
}
38-
},
39-
"errors": {
40-
"token_not_found": {
41-
"name": "Token Not Found"
42-
},
43-
"token_revoked": {
44-
"name": "Token Revoked"
45-
},
46-
"token_expired": {
47-
"name": "Token Expired"
48-
}
49-
}
2+
"name": "Tokens",
3+
"description": "Create & verify tokens for authorization purposes.",
4+
"icon": "lock",
5+
"tags": [
6+
"core",
7+
"utility"
8+
],
9+
"authors": [
10+
"rivet-gg",
11+
"NathanFlurry"
12+
],
13+
"status": "stable",
14+
"scripts": {
15+
"create": {
16+
"name": "Create Token"
17+
},
18+
"fetch": {
19+
"name": "Fetch Token",
20+
"description": "Get a token by its ID."
21+
},
22+
"fetch_by_token": {
23+
"name": "Fetch by Token",
24+
"description": "Get a token by its secret token."
25+
},
26+
"revoke": {
27+
"name": "Revoke Token",
28+
"description": "Revoke a token, preventing it from being used again."
29+
},
30+
"validate": {
31+
"name": "Validate Token",
32+
"description": "Validate a token. Throws an error if the token is invalid."
33+
},
34+
"extend": {
35+
"name": "Extend Token",
36+
"description": "Extend or remove the expiration date of a token. (Only works on valid tokens.)"
37+
},
38+
"modify_meta": {
39+
"name": "Modify Token Metadata",
40+
"description": "Modify the token's associated metadata, additionally returning the old metadata. (Only works on valid tokens.)"
41+
}
42+
},
43+
"errors": {
44+
"token_not_found": {
45+
"name": "Token Not Found"
46+
},
47+
"token_revoked": {
48+
"name": "Token Revoked"
49+
},
50+
"token_expired": {
51+
"name": "Token Expired"
52+
}
53+
}
5054
}

modules/tokens/scripts/extend.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { ScriptContext } from "../module.gen.ts";
2-
import { TokenWithSecret, tokenFromRow } from "../utils/types.ts";
2+
import { tokenFromRow, TokenWithSecret } from "../utils/types.ts";
33

44
export interface Request {
5-
token: string;
6-
newExpiration: string | null;
5+
token: string;
6+
newExpiration: string | null;
77
}
88

99
export interface Response {
@@ -14,22 +14,22 @@ export async function run(
1414
ctx: ScriptContext,
1515
req: Request,
1616
): Promise<Response> {
17-
// Ensure the token hasn't expired or been revoked yet
18-
const { token } = await ctx.modules.tokens.validate({
19-
token: req.token,
20-
});
17+
// Ensure the token hasn't expired or been revoked yet
18+
const { token } = await ctx.modules.tokens.validate({
19+
token: req.token,
20+
});
2121

22-
// Update the token's expiration date
23-
const newToken = await ctx.db.token.update({
24-
where: {
25-
id: token.id,
26-
},
27-
data: {
28-
expireAt: req.newExpiration,
29-
},
30-
});
22+
// Update the token's expiration date
23+
const newToken = await ctx.db.token.update({
24+
where: {
25+
id: token.id,
26+
},
27+
data: {
28+
expireAt: req.newExpiration,
29+
},
30+
});
3131

32-
// Return the updated token
32+
// Return the updated token
3333
return {
3434
token: tokenFromRow(newToken),
3535
};

modules/tokens/scripts/modify_meta.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ScriptContext } from "../module.gen.ts";
2+
import { Token, tokenFromRow } from "../utils/types.ts";
3+
4+
export interface Request {
5+
token: string;
6+
newMeta: { [key: string]: any };
7+
}
8+
9+
export interface Response {
10+
token: Token;
11+
oldMeta: { [key: string]: any };
12+
}
13+
14+
export async function run(
15+
ctx: ScriptContext,
16+
req: Request,
17+
): Promise<Response> {
18+
// Ensure the token hasn't expired or been revoked yet
19+
const { token } = await ctx.modules.tokens.validate({
20+
token: req.token,
21+
});
22+
23+
// Update the token's expiration date
24+
const newToken = await ctx.db.token.update({
25+
where: {
26+
id: token.id,
27+
},
28+
data: {
29+
meta: req.newMeta,
30+
},
31+
});
32+
33+
// Return the updated token
34+
return {
35+
token: tokenFromRow(newToken),
36+
oldMeta: token.meta,
37+
};
38+
}

modules/tokens/scripts/revoke.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Prisma, ScriptContext } from "../module.gen.ts";
1+
import { ScriptContext } from "../module.gen.ts";
22

33
export interface Request {
44
tokenIds: string[];

modules/tokens/tests/meta.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { test, TestContext } from "../module.gen.ts";
2+
import {
3+
assertEquals,
4+
assertExists,
5+
assertRejects,
6+
} from "https://deno.land/std@0.217.0/assert/mod.ts";
7+
import { RuntimeError } from "../module.gen.ts";
8+
9+
const METADATA_PRE = { state: "beforeModify" };
10+
const METADATA_POST = { state: "afterModify" };
11+
12+
test("modify_meta", async (ctx: TestContext) => {
13+
const { token } = await ctx.modules.tokens.create({
14+
type: "test",
15+
meta: METADATA_PRE,
16+
});
17+
18+
const { tokens: [tokenPre] } = await ctx.modules.tokens.fetch({
19+
tokenIds: [token.id],
20+
});
21+
assertExists(tokenPre);
22+
// assertEquals(typeof tokenPre.meta, "string");
23+
assertEquals(tokenPre.meta, METADATA_PRE);
24+
25+
const { oldMeta: fetchedOldMeta, token: tokenPost } = await ctx.modules.tokens
26+
.modifyMeta({
27+
token: token.token,
28+
newMeta: METADATA_POST,
29+
});
30+
assertExists(tokenPost);
31+
assertEquals(tokenPost.meta, METADATA_POST);
32+
assertEquals(fetchedOldMeta, METADATA_PRE);
33+
34+
await ctx.modules.tokens.revoke({ tokenIds: [token.id] });
35+
36+
const err = await assertRejects(() =>
37+
ctx.modules.tokens.modifyMeta({
38+
token: token.token,
39+
newMeta: METADATA_POST,
40+
}), RuntimeError);
41+
assertEquals(err.code, "token_revoked");
42+
});

modules/tokens/tests/validate.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { RuntimeError, test, TestContext } from "../module.gen.ts";
22
import {
33
assertEquals,
4-
assertRejects,
54
assertGreater,
5+
assertRejects,
66
} from "https://deno.land/std@0.217.0/assert/mod.ts";
77

88
test(
@@ -97,6 +97,6 @@ test(
9797
}, {
9898
...token,
9999
expireAt: null,
100-
})
100+
});
101101
},
102102
);

modules/tokens/utils/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ export function tokenFromRow(
1818
): TokenWithSecret {
1919
return {
2020
...row,
21+
// NOTE: Not sure why this is necessary— prisma seems to be stringifying
22+
// all JSON values before returning them.
23+
//
24+
// Should look into more.
25+
meta: row.meta ? JSON.parse(row.meta.toString()) : row.meta,
2126
createdAt: row.createdAt.toISOString(),
2227
expireAt: row.expireAt?.toISOString() ?? null,
2328
revokedAt: row.revokedAt?.toISOString() ?? null,

tests/basic/deno.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)