import { HTTPException } from 'hono/http-exception' import type { Token } from '../../types' import { toQueryParams } from '../../utils/objectToQuery' import type { TwitchErrorResponse, TwitchUserResponse, TwitchTokenResponse, TwitchUser, Scopes, } from './types' type TwitchAuthFlow = { client_id: string client_secret: string redirect_uri: string scope: Scopes[] state: string code: string | undefined force_verify: boolean | undefined } export class AuthFlow { client_id: string client_secret: string redirect_uri: string scope: string state: string code: string | undefined token: Token | undefined refresh_token: Token | undefined granted_scopes: string[] | undefined user: Partial | undefined force_verify: boolean | undefined constructor({ client_id, client_secret, redirect_uri, scope, state, code, force_verify, }: TwitchAuthFlow) { this.client_id = client_id this.client_secret = client_secret this.redirect_uri = redirect_uri this.scope = scope.join(' ') this.state = state this.code = code this.refresh_token = undefined this.force_verify = force_verify this.granted_scopes = undefined this.user = undefined } redirect() { const parsedOptions = toQueryParams({ client_id: this.client_id, force_verify: this.force_verify, redirect_uri: this.redirect_uri, response_type: 'code', scope: this.scope, state: this.state, }) return `https://id.twitch.tv/oauth2/authorize?${parsedOptions}` } private async getTokenFromCode() { const parsedOptions = toQueryParams({ client_id: this.client_id, client_secret: this.client_secret, code: this.code, grant_type: 'authorization_code', redirect_uri: this.redirect_uri, }) const url = 'https://id.twitch.tv/oauth2/token' const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: parsedOptions, }).then((res) => res.json() as Promise) if ('error' in response) { throw new HTTPException(400, { message: response.error }) } if ('access_token' in response) { this.token = { token: response.access_token, expires_in: response.expires_in, } } if ('refresh_token' in response) { this.refresh_token = { token: response.refresh_token, expires_in: 0, } } if ('scope' in response) { this.granted_scopes = response.scope } } async getUserData() { await this.getTokenFromCode() const response = (await fetch('https://api.twitch.tv/helix/users', { headers: { authorization: `Bearer ${this.token?.token}`, 'Client-ID': this.client_id, }, }).then((res) => res.json())) as TwitchUserResponse | TwitchErrorResponse if ('error' in response) { throw new HTTPException(400, { message: JSON.stringify(response) }) } if ('message' in response) { throw new HTTPException(400, { message: JSON.stringify(response) }) } if ('data' in response) { this.user = response.data[0] } } }