Skip to content

Commit 580fa4f

Browse files
committedSep 6, 2024··
feat: add role metadata
1 parent cb288a5 commit 580fa4f

File tree

4 files changed

+75
-21
lines changed

4 files changed

+75
-21
lines changed
 

‎pages/api/login.ts

+55-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { sign } from 'jsonwebtoken';
33
import { NextApiRequest, NextApiResponse } from 'next';
44
import fetch from 'node-fetch';
55

6+
import { getAuthHeader } from '../../lib/oauth';
7+
import prisma from '../../lib/prisma';
68
import { config } from '../../utils/config';
7-
import { DiscordUser } from '../../utils/types';
9+
import { DiscordUser, TrelloMember } from '../../utils/types';
810

9-
const scope = ['identify'].join(' ');
11+
const scope = ['identify', 'role_connections.write'].join(' ');
1012
const REDIRECT_URI = `${config.appUri}/api/login`;
1113

1214
const OAUTH_QS = new URLSearchParams({
@@ -36,7 +38,11 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
3638
scope
3739
}).toString();
3840

39-
const { access_token = null, token_type = 'Bearer' } = await fetch('https://discord.com/api/oauth2/token', {
41+
const {
42+
access_token = null,
43+
refresh_token = null,
44+
token_type = 'Bearer'
45+
} = await fetch('https://discord.com/api/oauth2/token', {
4046
headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'TacoAuth (https://github.com/trello-talk/TacoAuth, v1.0.0)' },
4147
method: 'POST',
4248
body
@@ -53,6 +59,52 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
5359

5460
if (!('id' in me)) return res.redirect(OAUTH_URI);
5561

62+
const user = await prisma.user.upsert({
63+
where: { userID: me.id },
64+
create: {
65+
userID: me.id,
66+
discordToken: access_token,
67+
discordRefresh: refresh_token
68+
},
69+
update: {
70+
discordToken: access_token,
71+
discordRefresh: refresh_token
72+
}
73+
});
74+
75+
let trelloMember: TrelloMember | null = null;
76+
77+
if (user.trelloID && user.trelloToken) {
78+
const params = new URLSearchParams({
79+
fields: ['id', 'username', 'fullName', 'avatarUrl', 'initials', 'url'].join(',')
80+
});
81+
82+
trelloMember = await fetch(`https://api.trello.com/1/members/me/?${params.toString()}`, {
83+
headers: { Authorization: await getAuthHeader(user.trelloToken, 'GET', 'https://api.trello.com/1/members/me') }
84+
})
85+
.then((res) => res.json() as unknown as TrelloMember)
86+
.catch(() => null);
87+
}
88+
89+
const memberConnected = trelloMember && !!trelloMember.id && !!trelloMember.username;
90+
await fetch(`https://discord.com/api/users/@me/applications/${config.clientId}/role-connection`, {
91+
method: 'PUT',
92+
headers: {
93+
Authorization: `${token_type} ${access_token}`,
94+
'User-Agent': 'TacoAuth (https://github.com/trello-talk/TacoAuth, v1.0.0)',
95+
'Content-Type': 'application/json'
96+
},
97+
body: JSON.stringify(
98+
memberConnected
99+
? {
100+
platform_name: 'Trello',
101+
platform_username: trelloMember.username,
102+
metadata: { connected: true }
103+
}
104+
: { metadata: { connected: false } }
105+
)
106+
});
107+
56108
const token = sign(me, config.jwtSecret, { expiresIn: '7d' });
57109

58110
res.setHeader(

‎pages/api/trello/connect.ts

+19-6
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import { NextApiRequest, NextApiResponse } from 'next';
33
import { getAccessToken, getAuthHeader } from '../../../lib/oauth';
44
import prisma from '../../../lib/prisma';
55
import { parseUser } from '../../../utils';
6+
import { config as appConfig } from '../../../utils/config';
67
import { TrelloMember } from '../../../utils/types';
78

8-
const TRELLO_BASE = 'https://api.trello.com/1';
9-
const TRELLO_MEMBER = '/members/me';
10-
119
export const config = {
1210
api: {
1311
bodyParser: {
@@ -24,22 +22,22 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
2422
const { token = null, verifier = null } = req.body;
2523
if (typeof token !== 'string' && typeof verifier !== 'string') return res.status(400).send({ error: 'Invalid body' });
2624

27-
const tokens = await getAccessToken(token, verifier, user.id).catch(null);
25+
const tokens = await getAccessToken(token, verifier, user.id).catch(() => null);
2826
if (!tokens) return res.status(400).send({ error: 'Invalid token' });
2927

3028
const params = new URLSearchParams({
3129
fields: ['id', 'username', 'fullName', 'avatarUrl', 'initials', 'url'].join(',')
3230
});
3331

3432
const body: TrelloMember = await fetch(`https://api.trello.com/1/members/me/?${params.toString()}`, {
35-
headers: { Authorization: await getAuthHeader(tokens.accessToken, 'GET', TRELLO_BASE + TRELLO_MEMBER) }
33+
headers: { Authorization: await getAuthHeader(tokens.accessToken, 'GET', 'https://api.trello.com/1/members/me') }
3634
})
3735
.then((res) => res.json())
3836
.catch(() => null);
3937
if (!body) return res.status(400).send({ error: 'Invalid body was given by Trello...' });
4038
if (!body.id) return res.status(400).send({ error: 'A Trello user ID body was not given by Trello...' });
4139

42-
await prisma.user.upsert({
40+
const dbUser = await prisma.user.upsert({
4341
where: { userID: user.id },
4442
create: {
4543
userID: user.id,
@@ -52,6 +50,21 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
5250
}
5351
});
5452

53+
if (dbUser.discordToken)
54+
await fetch(`https://discord.com/api/users/@me/applications/${appConfig.clientId}/role-connection`, {
55+
method: 'PUT',
56+
headers: {
57+
Authorization: `Bearer ${dbUser.discordToken}`,
58+
'User-Agent': 'TacoAuth (https://github.com/trello-talk/TacoAuth, v1.0.0)',
59+
'Content-Type': 'application/json'
60+
},
61+
body: JSON.stringify({
62+
platform_name: 'Trello',
63+
platform_username: body.username,
64+
metadata: { connected: true }
65+
})
66+
});
67+
5568
console.info(`[${new Date().toISOString()}}] User ${user.username}#${user.discriminator} (${user.id}) authorized Trello account ${body.username}`);
5669

5770
return res.status(200).send({ data: body });

‎pages/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export default function Index(props: Props) {
7777
<img src={getAvatarUrl(props.user)} className="w-12 h-12 rounded-full" />
7878
<span>
7979
Hello, <span className="font-medium">{props.user.username}</span>
80-
<span className="opacity-50">#{props.user.discriminator}</span>
80+
{props.user.discriminator !== '0' && <span className="opacity-50">#{props.user.discriminator}</span>}
8181
</span>
8282
</h1>
8383
<div className="flex flex-col justify-center items-center p-6 gap-4 w-full">

‎pages/trello-callback.tsx

-11
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,6 @@ export default function TrelloCallback() {
1010
<meta name="viewport" content="width=device-width,initial-scale=1" />
1111
<meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
1212
<meta httpEquiv="Content-Language" content="en" />
13-
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
14-
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
15-
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
16-
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#93a01e" />
17-
<meta name="og:site_name" content="Taco" />
18-
<meta name="og:title" content="Taco Authentication" />
19-
<meta name="og:description" content="Connect your Trello account to Taco, a Discord bot that manages Trelllo boards." />
20-
<meta name="og:locale" content="en_US" />
21-
<meta name="og:image" content="/android-chrome-512x512.png" />
22-
<meta name="msapplication-TileColor" content="#93a01e" />
23-
<meta name="theme-color" content="#93a01e" />
2413
</Head>
2514
<div className="min-h-screen bg-gradient-to-t from-neutral-800 to-zinc-900 text-white font-body flex items-center justify-center flex-col py-12 sm:px-12">
2615
<Spinner className="animate-spin h-20 w-20 text-white" />

0 commit comments

Comments
 (0)
Please sign in to comment.