Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 94 additions & 108 deletions server/package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
"author": "",
"license": "ISC",
"dependencies": {
"@twurple/api": "^5.3.3",
"@twurple/eventsub-http": "^5.3.3",
"@twurple/eventsub-ws": "^5.3.3",
"@twurple/pubsub": "^5.3.3",
"@twurple/api": "^6.2.1",
"@twurple/eventsub-http": "^6.2.1",
"@twurple/eventsub-ws": "^6.2.1",
"@twurple/pubsub": "^6.2.1",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
Expand Down
10 changes: 4 additions & 6 deletions server/src/middlewares/errorHandlersMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { AppError } from "@utils/ErrorHandlerUtil";
import { Request, Response } from "express";

export const errorResponder = (error: AppError, req: Request, res: Response) => {
res.header("Content-Type", "application/json");
import { NextFunction, Request, Response } from "express";

export const errorResponder = (error: AppError, req: Request, res: Response, next: NextFunction) => {
const status = error.statusCode || 400;
res.status(status).send({ message: error.message, status: status });
};

export const invalidPathHandler = (req: Request, res: Response) => {
export const invalidPathHandler = (req: Request, res: Response, next: NextFunction) => {
const statusCode = 404;

res.status(statusCode);
res.statusCode = statusCode;
res.send({ message: "Invalid path", status: statusCode });
};
162 changes: 87 additions & 75 deletions server/src/middlewares/twitchHandlersMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,87 @@
import { NextFunction, Request, Response } from "express";
import { ApiClient } from "@twurple/api";
import { createNewAuth, getAuthToken } from "@services/auth";
import { getConfigs } from "@services/configs";
import { RefreshingAuthProvider } from "@twurple/auth";
import ClientTmiHandler from "../stream/TwitchTmiHandler";
import StreamHandler from "../stream/StreamHandler";
import { getTwitchAuthUrl } from "../auth/auth";
import { logger } from "@utils/loggerUtil";
import { botPassword, botUsername, clientId, clientSecret, encryptionKey } from "@configs/envVariables";
import { decryptToken } from "@utils/tokenUtil";

const authorizationTwitch = async (req: Request, res: Response, next: NextFunction) => {
const tokenDB = await getAuthToken();
if (!tokenDB) {
const authUrl = getTwitchAuthUrl();
return res.redirect(authUrl.toString());
}
const decryptedAccessToken = decryptToken(tokenDB.accessToken, tokenDB.ivAccessToken, encryptionKey);
const decryptedRefreshToken = decryptToken(tokenDB.refreshToken, tokenDB.ivRefreshToken, encryptionKey);

const authProvider = new RefreshingAuthProvider(
{
clientId,
clientSecret,
onRefresh: async (newTokenData) => {
await createNewAuth(newTokenData);
}
},
{
...tokenDB,
accessToken: decryptedAccessToken,
refreshToken: decryptedRefreshToken
}
);

try {
const twitchApi = new ApiClient({ authProvider });
const authorizedUser = await twitchApi.users.getMe();
const configDB = await getConfigs();

if (!authorizedUser || !configDB) {
throw Error("Something went wrong, try login again");
}

const clientTmi = await ClientTmiHandler.getInstance({
userToListen: authorizedUser.name,
username: botUsername,
password: botPassword
});

StreamHandler.getInstance({
twitchApi: twitchApi,
config: configDB,
authorizedUser: authorizedUser,
socketIO: req.io,
clientTmi: clientTmi
});

req.io.emit("forceReconnect");

return next();
} catch (err) {
// await removeAuthToken();
if (err instanceof Error) {
res.status(400).send({ message: "Something went wrong. Please log in again" });
logger.error(`Error occured while using ApiClient ${err}`);
} else {
res.status(500).send({ message: "Interfnal server error" });
logger.error(`An unknown error occured while using ApiClient ${err}`);
}
}
};

export default authorizationTwitch;
import { NextFunction, Request, Response } from "express";
import { ApiClient } from "@twurple/api";
import { createNewAuth, getAuthToken, removeAuthToken } from "@services/auth";
import { getConfigs } from "@services/configs";
import { RefreshingAuthProvider, getTokenInfo } from "@twurple/auth";
import ClientTmiHandler from "../stream/TwitchTmiHandler";
import StreamHandler from "../stream/StreamHandler";
import { getTwitchAuthUrl } from "../auth/auth";
import { logger } from "@utils/loggerUtil";
import { botPassword, botUsername, clientId, clientSecret, encryptionKey } from "@configs/envVariables";
import { decryptToken } from "@utils/tokenUtil";

const authorizationTwitch = async (req: Request, res: Response, next: NextFunction) => {
const tokenDB = await getAuthToken();
if (!tokenDB) {
const authUrl = getTwitchAuthUrl();
return res.redirect(authUrl.toString());
}
const decryptedAccessToken = decryptToken(tokenDB.accessToken, tokenDB.ivAccessToken, encryptionKey);
const decryptedRefreshToken = decryptToken(tokenDB.refreshToken, tokenDB.ivRefreshToken, encryptionKey);

const authProvider = new RefreshingAuthProvider({
clientId,
clientSecret,
onRefresh: async (userId, newTokenData) => {
const { accessToken, refreshToken, expiresIn, obtainmentTimestamp, scope } = newTokenData;
await createNewAuth({
accessToken: accessToken,
refreshToken: refreshToken || "",
expiresIn: expiresIn || 0,
obtainmentTimestamp: obtainmentTimestamp,
scope: scope,
userId: userId
});
}
});

await authProvider.addUserForToken({
accessToken: decryptedAccessToken,
refreshToken: decryptedRefreshToken,
expiresIn: tokenDB.expiresIn,
obtainmentTimestamp: tokenDB.obtainmentTimestamp
});

try {
const tokeninfo = await getTokenInfo(decryptedAccessToken);
// Pretty sure it's always string so null assertion
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const authorizedUser = { id: tokeninfo.userId!, name: tokeninfo.userName! };
const twitchApi = new ApiClient({ authProvider });

const configDB = await getConfigs();

if (!authorizedUser.id || !authorizedUser.name || !configDB) {
throw Error("Something went wrong, try login again");
}

const clientTmi = await ClientTmiHandler.getInstance({
userToListen: authorizedUser.name,
username: botUsername,
password: botPassword
});

StreamHandler.getInstance({
twitchApi: twitchApi,
config: configDB,
authorizedUser: { id: authorizedUser.id, name: authorizedUser.name },
socketIO: req.io,
clientTmi: clientTmi
});

req.io.emit("forceReconnect");

return next();
} catch (err) {
await removeAuthToken();
if (err instanceof Error) {
res.status(400).send({ message: "Something went wrong. Please log in again" });
logger.error(`Error occured while using ApiClient ${err}`);
} else {
res.status(500).send({ message: "Interfnal server error" });
logger.error(`An unknown error occured while using ApiClient ${err}`);
}
}
};

export default authorizationTwitch;
5 changes: 3 additions & 2 deletions server/src/models/authModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ const AuthSchema: Schema<AuthDocument> = new Schema(
ivAccessToken: { type: Buffer },
refreshToken: { type: String, required: true },
ivRefreshToken: { type: Buffer },
expiresIn: { type: Number, require: true, default: 0 },
expiresIn: { type: Number, required: true, default: 0 },
obtainmentTimestamp: { type: Number, required: true, default: 0 },
scope: [String]
scope: [String],
userId: { type: String }
},
{
capped: { size: 100000, max: 1 }
Expand Down
1 change: 1 addition & 0 deletions server/src/models/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export interface AuthModel {
expiresIn: number;
obtainmentTimestamp: number;
scope: string[];
userId: string;
}

export type AuthDocument = AuthModel & Document;
Expand Down
10 changes: 5 additions & 5 deletions server/src/services/auth/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export interface AuthCreateData {
accessToken: string | null;
refreshToken: string | null;
expiresIn?: number | null;
obtainmentTimestamp?: number;
import { AuthModel } from "@models/types";

export interface AuthCreateData
extends Pick<AuthModel, "accessToken" | "refreshToken" | "expiresIn" | "obtainmentTimestamp" | "scope"> {
scope?: string[];
userId?: string;
}
2 changes: 1 addition & 1 deletion server/src/services/users/usersService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export const createUser = async (userData: UserCreateData) => {
const user = await User.create(userData);
return user;
} catch (err) {
logger.error(`Error occured while getting users twitch names. ${err}`);
logger.error(`Error occured while create user. ${err}`);
handleAppError(err);
}
};
Expand Down
Loading