@rlanz/ally-twitch
Version:
<p align="center"> <img src="https://github.com/RomainLanz/ally-twitch/assets/2793951/d3ccf29b-6169-4563-b932-5b15258a7fa3" alt="@rlanz/ally-twitch"> </p
148 lines (124 loc) • 3.69 kB
text/typescript
/**
* @rlanz/ally-twitch
*
* @license MIT
* @copyright Romain Lanz <romain.lanz@pm.me>
*/
import { Oauth2Driver } from '@adonisjs/ally'
import type { HttpContext } from '@adonisjs/core/http'
import type { ApiRequestContract, RedirectRequestContract } from '@adonisjs/ally/types'
import type { TwitchDriverConfig, TwitchScopes, TwitchToken } from './types/main.js'
export class TwitchDriver extends Oauth2Driver<TwitchToken, TwitchScopes> {
/**
* The URL to hit to exchange the authorization code for the access token
*/
protected accessTokenUrl = 'https://id.twitch.tv/oauth2/token'
/**
* The URL for the redirect request. The user will be redirected on this page
* to authorize the request.
*/
protected authorizeUrl = 'https://id.twitch.tv/oauth2/authorize'
/**
* The URL to hit to get the user details
*/
protected userInfoUrl = 'https://api.twitch.tv/helix/users'
/**
* The param name for the authorization code
*/
protected codeParamName = 'code'
/**
* The param name for the error
*/
protected errorParamName = 'error'
/**
* Cookie name for storing the "twitch_oauth_state"
*/
protected stateCookieName = 'twitch_oauth_state'
/**
* Parameter name to be used for sending and receiving the state
* from Twitch
*/
protected stateParamName = 'state'
/**
* Parameter name for defining the scopes
*/
protected scopeParamName = 'scope'
/**
* Scopes separator
*/
protected scopesSeparator = ' '
constructor(
ctx: HttpContext,
public config: TwitchDriverConfig
) {
super(ctx, config)
this.loadState()
}
protected configureRedirectRequest(request: RedirectRequestContract<TwitchScopes>) {
request.scopes(this.config.scopes || ['user:read:email'])
request.param('response_type', 'code')
request.param('grant_type', 'authorization_code')
}
/**
* Returns the HTTP request with the authorization header set
*/
protected getAuthenticatedRequest(url: string, token: string) {
const request = this.httpClient(url)
request.header('Authorization', `Bearer ${token} `)
request.header('Client-id', this.config.clientId)
request.header('Accept', 'application/json')
request.parseAs('json')
return request
}
/**
* Fetches the user info from the Twitch API
*/
protected async getUserInfo(token: string, callback?: (request: ApiRequestContract) => void) {
const request = this.getAuthenticatedRequest(this.userInfoUrl, token)
if (typeof callback === 'function') {
callback(request)
}
const body = await request.get()
const user = body.data[0]
return {
id: user.id,
nickName: user.login,
name: user.display_name,
email: user.email,
emailVerificationState: 'unsupported' as const,
avatarUrl: user.profile_image_url,
original: user,
}
}
/**
* Find if the current error code is for access denied
*/
accessDenied(): boolean {
const error = this.getError()
if (!error) {
return false
}
return error === 'access_denied'
}
/**
* Returns details for the authorized user
*/
async user(callback?: (request: ApiRequestContract) => void) {
const token = await this.accessToken(callback)
const user = await this.getUserInfo(token.token, callback)
return {
...user,
token,
}
}
/**
* Finds the user by the access token
*/
async userFromToken(token: string, callback?: (request: ApiRequestContract) => void) {
const user = await this.getUserInfo(token, callback)
return {
...user,
token: { token, type: 'bearer' as const },
}
}
}