Added routes for handling 3rd party OAuth.
This commit is contained in:
108
src/index.ts
108
src/index.ts
@ -205,12 +205,12 @@ app.post('/api/keys', apiMiddlewares, async (req: any, res: any, next: any) => {
|
||||
const userId = req.user.impersonation?.id ?? req.user.id;
|
||||
const keys = await db.one('SELECT count(*) FROM "ApiKey" WHERE "userId" = $1', userId);
|
||||
if (keys.count > 10) {
|
||||
res.status(400).send('too many keys');
|
||||
res.status(403).send('Too many keys');
|
||||
return;
|
||||
}
|
||||
const label = req.body.label;
|
||||
if (!label) {
|
||||
res.status(400).send('no label is attached.');
|
||||
res.status(400).send('No label is attached.');
|
||||
return;
|
||||
}
|
||||
const key = uuidv4();
|
||||
@ -228,6 +228,8 @@ app.delete('/api/keys', apiMiddlewares, async (req: any, res: any, next: any) =>
|
||||
res.status(400).send('key does not exist.');
|
||||
return;
|
||||
}
|
||||
|
||||
await db.none('DELETE FROM "ApiKey" WHERE id = $1', req.body.key);
|
||||
res.send({ key: req.body.key });
|
||||
});
|
||||
|
||||
@ -243,7 +245,7 @@ app.get('/api/twitch/redemptions', apiMiddlewares, async (req: any, res: any, ne
|
||||
|
||||
const twitch = JSON.parse(await resp.readBody());
|
||||
if (!twitch?.data) {
|
||||
console.log('Failed to fetch twitch data:', account, twitch?.data);
|
||||
console.log('Failed to fetch twitch data:', account, twitch.data);
|
||||
res.status(401).send({ error: 'Could not fetch Twitch channel redemption data.' });
|
||||
return;
|
||||
}
|
||||
@ -252,9 +254,9 @@ app.get('/api/twitch/redemptions', apiMiddlewares, async (req: any, res: any, ne
|
||||
});
|
||||
|
||||
app.get("/api/auth/twitch/users", apiMiddlewares, async (req: any, res: any) => {
|
||||
const username = req.query.login.toLowerCase();
|
||||
const username = req.query.login?.toLowerCase();
|
||||
if (!username) {
|
||||
res.send({ user: null });
|
||||
res.status(400).send({ user: null });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -269,7 +271,7 @@ app.get("/api/auth/twitch/users", apiMiddlewares, async (req: any, res: any) =>
|
||||
});
|
||||
const twitch = JSON.parse(await resp.readBody());
|
||||
if (!twitch?.data) {
|
||||
res.send({ user: null });
|
||||
res.status(403).send({ user: null });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -277,6 +279,100 @@ app.get("/api/auth/twitch/users", apiMiddlewares, async (req: any, res: any) =>
|
||||
res.send({ user });
|
||||
});
|
||||
|
||||
app.post("/api/auth/connections", apiMiddlewares, async (req: any, res: any) => {
|
||||
const name = req.body.name;
|
||||
const type = req.body.type?.toLowerCase();
|
||||
const client_id = req.body.client_id;
|
||||
const grant_type = req.body.grant_type?.toLowerCase();
|
||||
if (!name || !type || !client_id || !grant_type) {
|
||||
const missing = [name, type, client_id, grant_type]
|
||||
res.status(400).send({ error: 'Missing fields in the body.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const AuthData: { [service: string]: { type: string, endpoint: string, grantType: string, scopes: string[], redirect: string } } = {
|
||||
'nightbot': {
|
||||
type: 'nightbot',
|
||||
endpoint: 'https://api.nightbot.tv/oauth2/authorize',
|
||||
grantType: 'token',
|
||||
scopes: ['song_requests', 'song_requests_queue', 'song_requests_playlist'],
|
||||
redirect: 'https://beta.tomtospeech.com/connections/callback'
|
||||
},
|
||||
'twitch': {
|
||||
type: 'twitch',
|
||||
endpoint: 'https://id.twitch.tv/oauth2/authorize',
|
||||
grantType: 'token',
|
||||
scopes: [
|
||||
'chat:read',
|
||||
'bits:read',
|
||||
'channel:read:polls',
|
||||
'channel:read:predictions',
|
||||
'channel:read:subscriptions',
|
||||
'channel:read:vips',
|
||||
'moderator:read:blocked_terms',
|
||||
'chat:read',
|
||||
'channel:moderate',
|
||||
'channel:read:redemptions',
|
||||
'channel:manage:redemptions',
|
||||
'channel:manage:predictions',
|
||||
'user:read:chat',
|
||||
'channel:bot',
|
||||
'moderator:read:followers',
|
||||
'channel:read:ads',
|
||||
'moderator:read:chatters',
|
||||
],
|
||||
redirect: 'https://beta.tomtospeech.com/connections/callback'
|
||||
},
|
||||
};
|
||||
|
||||
const url = AuthData[type].endpoint;
|
||||
const redirect = AuthData[type].redirect;
|
||||
const scopes = AuthData[type].scopes.join(' ');
|
||||
const nounce = uuidv4();
|
||||
await db.none('INSERT INTO "ConnectionState" ("name", "type", "clientId", "grantType", "state", "userId") VALUES ($1, $2, $3, $4, $5, $6)'
|
||||
+ ' ON CONFLICT ("userId", "name") DO UPDATE SET "type" = $2, "clientId" = $3, "grantType" = $4, "state" = $5;',
|
||||
[name, type, client_id, grant_type, nounce, req.user.id]);
|
||||
|
||||
const redirect_uri = url + '?client_id=' + client_id + '&force_verify=true&redirect_uri=' + redirect + '&response_type=token&scope=' + scopes + '&state=' + nounce;
|
||||
res.send({ success: true, error: null, data: redirect_uri });
|
||||
});
|
||||
|
||||
app.get("/api/auth/connections", async (req: Request, res: Response) => {
|
||||
const state = req.query['state'];
|
||||
const access_token = req.query['token'];
|
||||
let expires_in = req.query['expires_in'];
|
||||
if (!state || !access_token) {
|
||||
res.status(400).send({ error: 'Missing fields in the body.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const connection = await db.oneOrNone('SELECT "name", "type", "clientId", "grantType", "userId" FROM "ConnectionState" WHERE "state" = $1', [state]);
|
||||
if (!connection) {
|
||||
res.status(400).send({ error: 'Failed to link the account.' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (connection.type == 'twitch') {
|
||||
const rest = new httpm.HttpClient(null);
|
||||
const response = await rest.get('https://id.twitch.tv/oauth2/validate', {
|
||||
'Authorization': 'OAuth ' + access_token,
|
||||
});
|
||||
const json = JSON.parse(await response.readBody());
|
||||
expires_in = json.expires_in;
|
||||
}
|
||||
|
||||
if (!expires_in) {
|
||||
res.status(400).send({ error: 'Could not determine the expiration of the token.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const expires_at = new Date();
|
||||
expires_at.setSeconds(expires_at.getSeconds() + parseInt(expires_in.toString()) - 300);
|
||||
res.send({
|
||||
data: { connection, expires_at },
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/api/auth/twitch/callback", async (req: any, res: any) => {
|
||||
const query = `client_id=${process.env.AUTH_CLIENT_ID}&client_secret=${process.env.AUTH_CLIENT_SECRET}&code=${req.body.code}&grant_type=authorization_code&redirect_uri=${process.env.AUTH_REDIRECT_URI}`
|
||||
const rest = new httpm.HttpClient(null);
|
||||
|
Reference in New Issue
Block a user