Skip to content

Commit e6e3be2

Browse files
committed
♻️ minimize auth, avoid storing discord credentials client side
1 parent 77f4217 commit e6e3be2

File tree

5 files changed

+53
-64
lines changed

5 files changed

+53
-64
lines changed

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default tseslint.config(
1818
globals: {
1919
console: true,
2020
document: true,
21+
localStorage: true,
2122
},
2223
parserOptions: {
2324
parser: tseslint.parser,

src/app/app.vue

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
<script lang="ts" setup>
2-
import DiscordClient from "@/lib/discord";
32
import RealmClient from "@/lib/realm";
43
import { onMounted, provide } from "vue";
54
import GlobalMenu from "./components/global-menu.vue";
65
7-
// make sure we're logged into discord
8-
const discord = new DiscordClient();
6+
// make sure we're logged into realm
7+
const realm = await RealmClient.create();
98
10-
// get discord user info
11-
const user = await discord.getDiscordUser();
12-
13-
// log into realm
14-
const realm = await RealmClient.create(user, discord.token);
15-
16-
provide("discordClient", discord);
179
provide("realmClient", realm);
18-
provide("user", user);
1910
2011
onMounted(() => {
2112
document.getElementById("apploading")!.remove();

src/app/lib/discord.ts

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import Cookies from "js-cookie";
2-
31
export interface Thing {
42
id: string;
53
}
@@ -23,31 +21,16 @@ export default class DiscordClient {
2321
const urlToken = urlMatch ? urlMatch[1] : null;
2422
const redirectUri = window.location.href.replace(/[#?].*$/, "");
2523

26-
if (urlToken) {
27-
Cookies.set("discordToken", urlToken, {
28-
expires: 30,
29-
sameSite: "strict",
30-
secure: true,
31-
});
32-
window.location.assign(redirectUri);
33-
34-
throw "Reloading page to use Discord token from URL";
35-
}
36-
37-
const token = Cookies.get("discordToken");
38-
39-
if (!token) {
24+
if (!urlToken) {
4025
const oauthScope = ["identify", "guilds", "guilds.members.read"];
41-
42-
Cookies.remove("discordToken");
4326
window.location.assign(
4427
`https://discord.com/oauth2/authorize?client_id=${this.clientId}&response_type=token&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${oauthScope.join("+")}`,
4528
);
4629

4730
throw "Retrieving Discord token";
4831
}
4932

50-
this.token = token;
33+
this.token = urlToken;
5134
}
5235

5336
async discordApi(url: string): Promise<Response> {
@@ -57,12 +40,7 @@ export default class DiscordClient {
5740
if (r.status === 429) {
5841
alert("Too many requests. Slow down.");
5942
throw "Too many requests";
60-
} else if (r.status >= 400 && r.status < 500) {
61-
Cookies.remove("discordToken");
62-
console.error(r);
63-
window.location.reload();
64-
throw "Discord API error; reloading page";
65-
} else if (r.status >= 500) {
43+
} else if (r.status >= 400) {
6644
alert("Error contacting Discord API");
6745
throw "Error contacting Discord API";
6846
}
@@ -71,11 +49,6 @@ export default class DiscordClient {
7149
});
7250
}
7351

74-
async logout() {
75-
this.token = "";
76-
Cookies.remove("discordToken");
77-
}
78-
7952
async getDiscordUser(): Promise<User> {
8053
return await this.discordApi("/oauth2/@me")
8154
.then((r) => r.json())

src/app/lib/realm.ts

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,69 @@
11
import Cookies from "js-cookie";
2-
import { User } from "./discord";
2+
import DiscordClient from "./discord";
33

44
const realmApi = import.meta.env.VITE_APP_REALM_API;
55

6+
export type RealmUser = {
7+
id: string;
8+
name: string;
9+
avatar: string;
10+
};
11+
612
/** Realm REST API client */
713
export default class RealmClient {
814
private token: string;
9-
private readonly user_id: string;
15+
private readonly user: RealmUser;
1016

11-
constructor(user: User, token: string) {
17+
constructor(user: RealmUser, token: string) {
1218
this.token = token;
13-
this.user_id = user.id;
19+
this.user = user;
1420
}
1521

16-
static async create(user: User, discordToken: string) {
22+
static async create() {
1723
let realmToken = Cookies.get("realmToken");
24+
let userInfo: RealmUser | null = JSON.parse(
25+
localStorage.getItem("realm.user") ?? "null",
26+
);
27+
28+
if (!realmToken || !userInfo) {
29+
const discord = new DiscordClient();
30+
const user = await discord.getDiscordUser();
1831

19-
if (!realmToken) {
20-
realmToken = await fetch(`${realmApi}/auth/login`, {
32+
const loginResponse = await fetch(`${realmApi}/auth/login`, {
2133
body: JSON.stringify({
22-
token: discordToken,
34+
token: discord.token,
2335
user_id: user.id,
2436
}),
2537
headers: { "Content-Type": "application/json" },
2638
method: "POST",
27-
})
28-
.then((r) => {
29-
if (r.status !== 200) {
30-
alert("Error logging into Realm");
31-
throw "Error logging into Realm";
32-
}
39+
}).then((r) => {
40+
if (r.status !== 200) {
41+
alert("Error logging into Realm");
42+
throw "Error logging into Realm";
43+
}
3344

34-
return r.json();
35-
})
36-
.then((d) => d.token);
45+
return r.json();
46+
});
47+
realmToken = loginResponse.token;
48+
userInfo = {
49+
id: loginResponse.user.id,
50+
name: loginResponse.user.name,
51+
avatar: loginResponse.user.avatar,
52+
};
53+
localStorage.setItem("realm.user", JSON.stringify(userInfo));
3754
Cookies.set("realmToken", realmToken!, {
3855
expires: 30,
3956
sameSite: "strict",
4057
secure: true,
4158
});
59+
60+
const redirectUri = window.location.href.replace(/[#?].*$/, "");
61+
window.location.assign(redirectUri);
62+
63+
throw "Reloading page to use new session";
4264
}
4365

44-
return new RealmClient(user, realmToken!);
66+
return new RealmClient(userInfo, realmToken!);
4567
}
4668

4769
async realmApi(url: string, opts?: Partial<RequestInit>): Promise<Response> {
@@ -51,7 +73,7 @@ export default class RealmClient {
5173
headers: {
5274
...opts?.headers,
5375
"Content-Type": "application/json",
54-
"X-Realm-User": this.user_id,
76+
"X-Realm-User": this.user.id,
5577
"X-Realm-Token": this.token,
5678
},
5779
}).then((r) => {

src/app/views/main.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
<script lang="ts" setup>
22
import DiscordAvatar from "@/components/discord-avatar.vue";
3-
import { User } from "@/lib/discord";
43
import { doneLoading } from "@/lib/global";
54
import RealmClient from "@/lib/realm";
65
import { inject, onMounted } from "vue";
76
87
const realm: RealmClient = inject("realmClient")!;
9-
const user: User = inject("user");
108
119
// filter to guilds shared with the bot
1210
const sharedGuilds = await realm.getSharedGuilds();
@@ -18,8 +16,12 @@ onMounted(() => doneLoading());
1816
<h1>Realm TTRPG</h1>
1917
<p>
2018
You are logged in as
21-
<DiscordAvatar class="am di mr-xxs" :user="user" :size="32"></DiscordAvatar>
22-
<strong>{{ user.username }}</strong
19+
<DiscordAvatar
20+
class="am di mr-xxs"
21+
:user="realm.user"
22+
:size="32"
23+
></DiscordAvatar>
24+
<strong>{{ realm.user.name }}</strong
2325
>.
2426
</p>
2527
<p>

0 commit comments

Comments
 (0)