diff --git a/nt-web-app/entity/TwitchGetUsersResponse.ts b/nt-web-app/entity/TwitchGetUsersResponse.ts deleted file mode 100644 index 3284d603..00000000 --- a/nt-web-app/entity/TwitchGetUsersResponse.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface TwitchGetUsersResponse{ - data: TwitchUserData[] -} - -export interface TwitchUserData { - id: string, - login: string, - display_name: string, - type: string, - broadcaster_type: string, - description: string, - profile_image_url: string, - offline_image_url: string, - view_count: number, - email: string, - created_at: string -} \ No newline at end of file diff --git a/nt-web-app/entity/User.ts b/nt-web-app/entity/User.ts index bbf2d6ee..ba95b203 100644 --- a/nt-web-app/entity/User.ts +++ b/nt-web-app/entity/User.ts @@ -1,12 +1,12 @@ -import {Entity, PrimaryColumn, Column, UpdateDateColumn, CreateDateColumn} from "typeorm" +import { Entity, PrimaryColumn, Column, UpdateDateColumn, CreateDateColumn } from "typeorm" -export type LoginProvider = 'local'|'twitch' +export type LoginProvider = string /** * @module DBUser */ -@Entity({name: "user", synchronize: true}) -export class User{ +@Entity({ name: "user", synchronize: true }) +export class User { @PrimaryColumn() id: string @@ -16,7 +16,7 @@ export class User{ @Column() display_name: string - private _access: Role|null = null; + private _access: Role | null = null; @Column({ type: "text", nullable: false }) get access(): string { @@ -30,8 +30,8 @@ export class User{ return ''; } - set access(value: string|null) { - if(!value){ + set access(value: string | null) { + if (!value) { this._access = new RoleImpl() return } @@ -51,9 +51,9 @@ export class User{ uaccess?: number - constructor(id: string, twitch_user_name: string, access: Role, provider: LoginProvider) { + constructor(id: string, user_name: string, access: Role, provider: LoginProvider) { this._access = access - this.display_name = twitch_user_name + this.display_name = user_name this.id = id this.provider = provider this.uaccess = 0 @@ -109,4 +109,4 @@ export const defaultRoles = { canListLobbies: true, elevatedRoomPermissions: false, isModerator: false -} as Role \ No newline at end of file +} as Role diff --git a/nt-web-app/entity/identity.ts b/nt-web-app/entity/identity.ts new file mode 100644 index 00000000..6c980770 --- /dev/null +++ b/nt-web-app/entity/identity.ts @@ -0,0 +1,8 @@ +export interface UserData { + //Subscribder Id + sub: string, + preferred_username: string, + picture?: string, + email?: string, +} + diff --git a/nt-web-app/identity/forgejo.ts b/nt-web-app/identity/forgejo.ts new file mode 100644 index 00000000..ac3728a5 --- /dev/null +++ b/nt-web-app/identity/forgejo.ts @@ -0,0 +1,107 @@ +/* ======= + * NOTES + * ======= + * + * Scope: + * This implementation should be a pretty cut and dry OIDC + * integration, very little should need to be changed to + * interface with any other standard oidc provider. It was + * developed against forgejo since it's the oidc provider + * I had available locally. + * + * Documentation Referenced In Implementation + * https://forgejo.org/docs/v1.19/user/oauth2-provider/ + * https://forgejo.org/docs/v1.19/user/api-usage/ + * + * Vars + * FORGEJO_API_KEY: Key for general api, needs access to read users + * FORGEJO_CLIENT_SECRET: OIDC Client Secret, generated in /user/settings/applications + * FORGEJO_CLIENT_ID: OIDC Client id, generated in /user/settings/applications + * + * FORGEJO_GETUSER_ENDPOINT: Forgejo Static API endpoint that uses general API key to get user info by id + * + * The following vars are all ripped from https://[YOUR-FORGEJO-URL]/.well-known/openid-configuration + * This likely could be used to automatically grab the appropriate url, it simply isn't in this + * implementation for convenience. + * + * FORGEJO_USERINFO_ENDPOINT + * FORGEJO_AUTHORIZATION_ENDPOINT + * FORGEJO_ACCESSTOKEN_ENDPOINT + * + */ + +// ===================== +// Imports and Globals +// ===================== +import axios, { AxiosResponse } from "axios"; +import { UserData } from "../entity/identity"; + +const FORGEJO_API_KEY = process.env.FORGEJO_API_KEY as string +const FORGEJO_CLIENT_SECRET = process.env.FORGEJO_CLIENT_SECRET as string +const FORGEJO_CLIENT_ID = process.env.FORGEJO_CLIENT_ID as string + +const FORGEJO_USERINFO_ENDPOINT = process.env.FORGEJO_USERINFO_ENDPOINT as string +const FORGEJO_AUTHORIZATION_ENDPOINT = process.env.FORGEJO_AUTHORIZATION_ENDPOINT as string +const FORGEJO_GETUSER_ENDPOINT = process.env.FORGEJO_GETUSER_ENDPOINT as string +const FORGEJO_ACCESSTOKEN_ENDPOINT = process.env.FORGEJO_ACCESSTOKEN_ENDPOINT as string + +const provider = "forgejo"; + +// ==================== +// Exported Functions +// ==================== + +const getUser = async (accessToken: string): Promise => { + return axios.get(FORGEJO_USERINFO_ENDPOINT, { + headers: { + 'Authorization': `Bearer ${accessToken}` + } + }) + .then((res) => res.data); +} + +const getUsersById = async (userIds: string[]): Promise => { + const userDataPromises = userIds.map((userId) => + axios.get(`${FORGEJO_GETUSER_ENDPOINT}?access_token=${FORGEJO_API_KEY}&uid=${userId}`) + .then(resp => resp.data) + .then((data: UserData) => data) + ); + return Promise.all(userDataPromises); +} + +const validateAuthServer = (): Promise => { + return Promise.resolve(true); +} + +const getRedirectURL = (redirectURL: string, scope: string, state: string): string => + `${FORGEJO_AUTHORIZATION_ENDPOINT}?response_type=code&client_id=${FORGEJO_CLIENT_ID}&redirect_uri=${redirectURL}&scope=${scope}&state=${state}`; + +const handleRedirectResponse = async (code: string, state: string, redirectUri: string, grantType: string): Promise => { + const response = await axios.post(FORGEJO_ACCESSTOKEN_ENDPOINT, { + client_id: FORGEJO_CLIENT_ID, + client_secret: FORGEJO_CLIENT_SECRET, + code: code, + grant_type: grantType, + redirect_uri: redirectUri, + }, { + headers: { + 'Content-Type': 'application/json', + }, + }); + + const { access_token } = response.data; + + const userData = await getUser(access_token) + + return userData; +}; + +export { + getUser, + getUsersById, + getRedirectURL, + handleRedirectResponse, + validateAuthServer, + + provider, +} diff --git a/nt-web-app/identity/identity.ts b/nt-web-app/identity/identity.ts new file mode 100644 index 00000000..f6913068 --- /dev/null +++ b/nt-web-app/identity/identity.ts @@ -0,0 +1,28 @@ +import { getUser, getUsersById, getRedirectURL, handleRedirectResponse, validateAuthServer, provider } from './twitch'; + +export { + getUser, + getUsersById, + getRedirectURL, + handleRedirectResponse, + provider, + validateAuthServer, +} + + +/* +import { UserData } from '../entity/identity'; + +Potential IdentityProvider Interface to make sure identity providers are handled consistently + +interface IdentityProvider { + getUser(): Promise; + getUsersById(): Promise; + getRedirectURL(): string; + handleRedirectResponse(): UserData; + validateAuthServer(): Promise; + + provider: string; +} +*/ + diff --git a/nt-web-app/identity/twitch.ts b/nt-web-app/identity/twitch.ts new file mode 100644 index 00000000..d789412a --- /dev/null +++ b/nt-web-app/identity/twitch.ts @@ -0,0 +1,172 @@ +/* ======= + * NOTES + * ======= + * + * Scope: + * Interfaces with twitch API to provide identity services and user lookup + * + * Documentation Referenced In Implementation + * https://dev.twitch.tv/docs/authentication/getting-tokens-oauth/#implicit-grant-flow + * + * Vars + * TWITCH_API_KEY: OAuth Secret and API key, accessed in twitch developer dashboard + * TWITCH_CLIENT_ID: OAuth Client id, accessed in twitch developer dashboard + * + */ + +// ===================== +// Imports and Globals +// ===================== +import axios, { AxiosResponse } from "axios"; +import { UserData } from "../entity/identity"; + +const TWITCH_API_KEY = process.env.TWITCH_API_KEY as string +const TWITCH_CLIENT_ID = process.env.TWITCH_CLIENT_ID as string + +// ======================== +// Interfaces and Helpers +// ======================== + +interface TwitchAppAccessToken { + access_token: string, + expires_in: number, + acquired_at?: number, + token_type: string +} + +interface TwitchGetUsersResponse { + data: TwitchUserData[] +} + +interface TwitchUserData { + id: string, + login: string, + display_name: string, + type: string, + broadcaster_type: string, + description: string, + profile_image_url: string, + offline_image_url: string, + view_count: number, + email: string, + created_at: string +} + +const toUserData = (twitchUserData: TwitchUserData): UserData => ({ + sub: twitchUserData.id, + preferred_username: twitchUserData.display_name, + picture: twitchUserData.profile_image_url, + email: twitchUserData.email, +}); + +const getServerAccessToken = async (): Promise => { + if (serverAccessToken) { + let expiration = serverAccessToken.acquired_at!! + serverAccessToken.expires_in * 1000 + if (Date.now() - expiration <= 120000) { //if the expiration is within 2 minutes, lets refresh the token + console.log('Tokens expired!') + serverAccessToken = undefined + } + else return serverAccessToken + } + + if (!serverAccessToken) { + console.log('Fetching access token for Twitch API...') + const access: TwitchAppAccessToken | null = await axios.post('https://id.twitch.tv/oauth2/token', null, { + params: { + client_id: TWITCH_CLIENT_ID, + client_secret: TWITCH_API_KEY, + grant_type: 'client_credentials', + }, + }) + .then((res: AxiosResponse) => res.data) + .catch((e) => { + console.log(e) + return null + }) + console.log('Fetched?') + if (access) { + access.acquired_at = Date.now() + serverAccessToken = access + console.log('Yes!') + } + else { + console.log('Nope!') + } + } + return serverAccessToken!! +} + +// ==================== +// Exported Functions +// ==================== + +const getUser = async (accessToken: string): Promise => { + return axios.get("https://api.twitch.tv/helix/users", { + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Client-Id': TWITCH_CLIENT_ID + } + }) + .then((res: AxiosResponse) => res.data) + .then(data => data.data[0]) + .then(toUserData); +} + +const getUsersById = async (userIds: string[]): Promise => { + let tokens = await getServerAccessToken() + if (!tokens) throw new Error("getUsersById: Unable to communicate with Twitch!") + const query = userIds.map((userId) => `id=${userId}`).join('&') + return axios.get(`https://api.twitch.tv/helix/users?${query}`, { + headers: { + 'Authorization': `Bearer ${tokens.access_token}`, + 'Client-Id': TWITCH_CLIENT_ID + } + }) + .then((res: AxiosResponse) => res.data) + .then(data => data.data) + .then((twitchUsers: TwitchUserData[]) => twitchUsers.map(toUserData)) + .catch((e) => { + console.log(e) + return null + }) +} + +const validateAuthServer = async (): Promise => { + const token = getServerAccessToken(); + return !!token; +} + +const getRedirectURL = (redirectURL: string, scope: string, state: string): string => + `https://id.twitch.tv/oauth2/authorize?response_type=code&client_id=${TWITCH_CLIENT_ID}&redirect_uri=${redirectURL}&scope=${scope}&state=${state}`; + +const handleRedirectResponse = async (code: string, state: string, redirectUri: string, grantType: string): Promise => { + const response = await axios.post('https://id.twitch.tv/oauth2/token', null, { + params: { + client_id: TWITCH_CLIENT_ID, + client_secret: TWITCH_API_KEY, + code: code, + grant_type: grantType, + redirect_uri: redirectUri, + }, + }); + + const { access_token } = response.data; + + const userData = await getUser(access_token) + + return userData; +}; + +let serverAccessToken: TwitchAppAccessToken | undefined = undefined + +const provider = "twitch"; + +export { + getUser, + getUsersById, + getRedirectURL, + handleRedirectResponse, + validateAuthServer, + + provider, +} diff --git a/nt-web-app/nt-webserver.ts b/nt-web-app/nt-webserver.ts index df33a764..157bf488 100644 --- a/nt-web-app/nt-webserver.ts +++ b/nt-web-app/nt-webserver.ts @@ -8,12 +8,12 @@ if (!fs.existsSync(uaccess_file)) { fs.writeFileSync(uaccess_file, '', 'utf-8') } -if(process.env.DEV_MODE === 'true') console.log('!!!Server is in DEV mode. Only developers can create rooms!!!') +if (process.env.DEV_MODE === 'true') console.log('!!!Server is in DEV mode. Only developers can create rooms!!!') import * as http from 'http'; import next from "next"; import { parse } from "url"; -import { getServerAccessToken } from "./utils/TwitchUtils"; +import { validateAuthServer } from "./identity/identity.ts"; import path from 'path'; const dev = process.env.NODE_ENV !== 'production'; @@ -21,13 +21,13 @@ const hostname = 'localhost'; const port = 3000; // when using middleware `hostname` and `port` must be provided below -let tokensPromise = getServerAccessToken() +let authValidPromise = validateAuthServer() const app = next({ dev, hostname, port }); const handle = app.getRequestHandler(); app.prepare().then(async () => { - const tokens = await tokensPromise - if(!tokens?.access_token) throw new Error("Unable to authenticate with twitch!") + const authValid = await authValidPromise + if (!authValid) throw new Error("Unable to access auth server!") http.createServer(async (req, res) => { try { if (!req.url) return @@ -71,4 +71,4 @@ app.prepare().then(async () => { .listen(port, () => { console.log(`> Ready on http://${hostname}:${port}`); }); -}); \ No newline at end of file +}); diff --git a/nt-web-app/pages/api/auth/code.ts b/nt-web-app/pages/api/auth/code.ts index cddbac26..2a7675db 100644 --- a/nt-web-app/pages/api/auth/code.ts +++ b/nt-web-app/pages/api/auth/code.ts @@ -1,20 +1,16 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type { NextApiRequest, NextApiResponse } from 'next' import axios from 'axios'; -import {UserDatasource} from "../../../websocket/Datasource"; -import {defaultRoles, User} from "../../../entity/User"; -import {getUser} from "../../../utils/TwitchUtils"; -import {createAccessToken, createRefreshToken} from "../../../utils/jwtUtils"; -import {PendingConnection} from "../../../entity/PendingConnection"; +import { UserDatasource } from "../../../websocket/Datasource"; +import { defaultRoles, User } from "../../../entity/User"; +import { handleRedirectResponse, provider } from "../../../identity/identity"; +import { createAccessToken, createRefreshToken } from "../../../utils/jwtUtils"; +import { PendingConnection } from "../../../entity/PendingConnection"; import fs from "fs"; -const TWITCH_API_KEY = process.env.TWITCH_API_KEY as string -const TWITCH_CLIENT_ID = process.env.TWITCH_CLIENT_ID as string + const OAUTH_REDIRECT_URI = process.env.OAUTH_REDIRECT_URI as string const NOITA_APP_REDIRECT_URI = process.env.NOITA_APP_REDIRECT_URI as string -const clientID = TWITCH_CLIENT_ID as string; -const clientSecret = TWITCH_API_KEY as string; - type Data = { data?: { refresh_token: string @@ -29,26 +25,14 @@ export default async function handler( ) { const code = req.query.code as string; const state = req.query.state as string; - const redirectUri = OAUTH_REDIRECT_URI+'/api/auth/code'; + const redirectUri = OAUTH_REDIRECT_URI + '/api/auth/code'; const grantType = 'authorization_code'; console.log('/api/auth/code: Start') try { - const response = await axios.post('https://id.twitch.tv/oauth2/token', null, { - params: { - client_id: clientID, - client_secret: clientSecret, - code: code, - grant_type: grantType, - redirect_uri: redirectUri, - }, - }); - - const { access_token, refresh_token, id_token, expires_in } = response.data; - - const userData = await getUser(access_token) + const userData = await handleRedirectResponse(code, state, redirectUri, grantType); const db = await UserDatasource() - if(!db){ + if (!db) { res.status(500).json({ error: 'Failed to retrieve tokens :(' }); console.log('Failed to retrieve tokens :(') return @@ -57,40 +41,41 @@ export default async function handler( const repoPendingConnection = db.getRepository(PendingConnection) const user = await repo.findOneBy({ - id: userData.id, - provider: 'twitch' - }).then(user=>{ - if(!user) return new User(userData.id, userData.display_name, defaultRoles, 'twitch') + id: userData.sub, + provider: provider + }).then(user => { + if (!user) return new User(userData.sub, userData.preferred_username, defaultRoles, provider) return user }) - user.display_name = userData.display_name + user.display_name = userData.preferred_username + repo.save(user) - if (state.length === 8){ + if (state?.length === 8) { const pendingConnection = await repoPendingConnection.findOneBy({ userCode: state }) - if(!pendingConnection){ + if (!pendingConnection) { res.status(400).end("No valid device code :("); return } - pendingConnection.resolvedProvider = 'twitch' + pendingConnection.resolvedProvider = provider pendingConnection.resolvedUserId = user.id pendingConnection.save() res.status(200).end("you can close this."); } - else{ + else { const accessToken = createAccessToken(userData) const refreshToken = createRefreshToken(userData) let e = undefined try { const uaccessDataForUser = fs.readFileSync('.uaccess', 'utf-8') .replace(/[\n\r]/g, '\n').split('\n') - .filter(a=>user.display_name === a || `${user.display_name}:dev` === a) - if(uaccessDataForUser.length === 1){ + .filter(a => user.display_name === a || `${user.display_name}:dev` === a) + if (uaccessDataForUser.length === 1) { e = uaccessDataForUser[0].endsWith(':dev') ? 3 : 2; } } catch (e: any) { diff --git a/nt-web-app/pages/api/auth/login.ts b/nt-web-app/pages/api/auth/login.ts index 279055b2..8f1e87e0 100644 --- a/nt-web-app/pages/api/auth/login.ts +++ b/nt-web-app/pages/api/auth/login.ts @@ -1,8 +1,8 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction import type { NextApiRequest, NextApiResponse } from 'next' -import {randomUUID} from "crypto"; +import { randomUUID } from "crypto"; +import { getRedirectURL } from "../../../identity/identity"; -const TWITCH_CLIENT_ID = process.env.TWITCH_CLIENT_ID as string const OAUTH_REDIRECT_URI = process.env.OAUTH_REDIRECT_URI as string type Data = { @@ -14,11 +14,11 @@ export default async function handler( res: NextApiResponse ) { const deviceCode = req.query.deviceCode - const redirectUri = encodeURIComponent(OAUTH_REDIRECT_URI+'/api/auth/code'); + const redirectUri = encodeURIComponent(OAUTH_REDIRECT_URI + '/api/auth/code'); const scope = encodeURIComponent('openid'); //Scopes here. We should not need any though :P const state = deviceCode ?? randomUUID(); - const url = `https://id.twitch.tv/oauth2/authorize?response_type=code&client_id=${TWITCH_CLIENT_ID}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}`; + const url = getRedirectURL(redirectUri, scope, state.toString()); res.writeHead(302, { Location: url }); res.end(); diff --git a/nt-web-app/pages/api/auth/refresh.ts b/nt-web-app/pages/api/auth/refresh.ts index 1992ff0f..8ca6b3de 100644 --- a/nt-web-app/pages/api/auth/refresh.ts +++ b/nt-web-app/pages/api/auth/refresh.ts @@ -1,8 +1,8 @@ -import type {NextApiRequest, NextApiResponse} from 'next' -import {getUsersById} from "../../../utils/TwitchUtils"; -import {createAccessToken, verifyToken} from "../../../utils/jwtUtils"; -import {UserDatasource} from "../../../websocket/Datasource"; -import {LoginProvider, User} from "../../../entity/User"; +import type { NextApiRequest, NextApiResponse } from 'next' +import { getUsersById, provider } from "../../../identity/identity"; +import { createAccessToken, verifyToken } from "../../../utils/jwtUtils"; +import { UserDatasource } from "../../../websocket/Datasource"; +import { LoginProvider, User } from "../../../entity/User"; import * as jwt from "jsonwebtoken"; const SECRET_ACCESS = process.env.SECRET_JWT_ACCESS as string @@ -54,11 +54,11 @@ export default async function handler( let accessKey: string switch (user.provider as LoginProvider) { - case 'twitch': - //get the user from the Twitch API using TWITCH_CLIENT_ID and TWITCH_API_KEY + case provider: + //get the user from the provider if possible, else throw a 502 const userData = await getUsersById([userSub]) if (!userData || userData.length === 0) { - res.status(502).end("502 Failed to get twitch user data"); + res.status(502).end("502 Failed to get user data from identity provider"); return } //create the access token and give it to the user :) @@ -75,6 +75,9 @@ export default async function handler( expiresIn: '8h' }) break + default: + accessKey = ""; + return res.status(401).end("401 Unauthorized. Identity Provider is incorrect."); } res.status(200).json({ token: accessKey, diff --git a/nt-web-app/utils/TwitchUtils.ts b/nt-web-app/utils/TwitchUtils.ts deleted file mode 100644 index 0547a39a..00000000 --- a/nt-web-app/utils/TwitchUtils.ts +++ /dev/null @@ -1,86 +0,0 @@ -import {TwitchGetUsersResponse, TwitchUserData} from "../entity/TwitchGetUsersResponse"; -import axios, {AxiosResponse} from "axios"; - -const TWITCH_API_KEY = process.env.TWITCH_API_KEY as string -const TWITCH_CLIENT_ID = process.env.TWITCH_CLIENT_ID as string - -const getUser = async (accessToken: string): Promise => { - return axios.get("https://api.twitch.tv/helix/users", { - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'Client-Id': TWITCH_CLIENT_ID - } - }) - .then((res: AxiosResponse)=>res.data) - .then(data=>data.data[0]) -} - -const getUsersById = async (userIds: string[]): Promise => { - let tokens = await getServerAccessToken() - if(!tokens) throw new Error("getUsersById: Unable to communicate with Twitch!") - const query = userIds.map((userId)=>`id=${userId}`).join('&') - return axios.get(`https://api.twitch.tv/helix/users?${query}`, { - headers: { - 'Authorization': `Bearer ${tokens.access_token}`, - 'Client-Id': TWITCH_CLIENT_ID - } - }) - .then((res: AxiosResponse)=>res.data) - .then(data=>data.data) - .catch((e)=> { - console.log(e) - return null - }) -} - -export interface TwitchAppAccessToken{ - access_token: string, - expires_in: number, - acquired_at?: number, - token_type: string -} - -let serverAccessToken: TwitchAppAccessToken|undefined = undefined - -const getServerAccessToken = async(): Promise => { - if(serverAccessToken){ - let expiration = serverAccessToken.acquired_at!! + serverAccessToken.expires_in*1000 - if(Date.now() - expiration <= 120000){ //if the expiration is within 2 minutes, lets refresh the token - console.log('Tokens expired!') - serverAccessToken = undefined - } - else return serverAccessToken - } - - if(!serverAccessToken){ - console.log('Fetching access token for Twitch API...') - const access: TwitchAppAccessToken|null = await axios.post('https://id.twitch.tv/oauth2/token', null, { - params: { - client_id: TWITCH_CLIENT_ID, - client_secret: TWITCH_API_KEY, - grant_type: 'client_credentials', - }, - }) - .then((res: AxiosResponse)=>res.data) - .catch((e)=> { - console.log(e) - return null - }) - console.log('Fetched?') - if(access){ - access.acquired_at = Date.now() - serverAccessToken = access - console.log('Yes!') - } - else{ - console.log('Nope!') - } - } - return serverAccessToken!! -} - -export { - getServerAccessToken, - getUser, - getUsersById -} \ No newline at end of file diff --git a/nt-web-app/utils/jwtUtils.ts b/nt-web-app/utils/jwtUtils.ts index e5c1725f..0c9e12ff 100644 --- a/nt-web-app/utils/jwtUtils.ts +++ b/nt-web-app/utils/jwtUtils.ts @@ -1,33 +1,34 @@ //creating a access token import jwt from "jsonwebtoken"; -import {TwitchUserData} from "../entity/TwitchGetUsersResponse"; +import { UserData } from "../entity/identity"; +import { provider } from "../identity/identity"; const SECRET_ACCESS = process.env.SECRET_JWT_ACCESS as string const SECRET_REFRESH = process.env.SECRET_JWT_REFRESH as string -function createAccessToken(userData: TwitchUserData){ +function createAccessToken(userData: UserData) { return jwt.sign({ - preferred_username: userData.display_name, - sub: userData.id, - profile_image_url: userData.profile_image_url, - provider: 'twitch' + sub: userData.sub, + preferred_username: userData.preferred_username, + profile_image_url: userData.picture, + provider: provider }, SECRET_ACCESS, { expiresIn: '8h' }) -// Creating refresh token not that expiry of refresh -//token is greater than the access token + // Creating refresh token not that expiry of refresh + //token is greater than the access token } -function createRefreshToken(userData: TwitchUserData){ +function createRefreshToken(userData: UserData) { return jwt.sign({ - sub: userData.id, - }, SECRET_REFRESH, {expiresIn: '5d'}) + sub: userData.sub, + }, SECRET_REFRESH, { expiresIn: '5d' }) } const verifyToken = (jwtToken: string, tokenSecret: string): any => { console.log('Verify JWT...') return new Promise((resolve, reject) => { - jwt.verify(jwtToken, tokenSecret, {complete: true}, (err, decoded: any)=>{ + jwt.verify(jwtToken, tokenSecret, { complete: true }, (err, decoded: any) => { if (err) { console.log('Error!') reject(new Error(`JWT verification failed: ${err.message}`)); @@ -43,4 +44,4 @@ export { createAccessToken, createRefreshToken, verifyToken -} \ No newline at end of file +}