/* eslint camelcase: 0 */ import {OAuth2LoginProvider, OAuth2LoginProviderConfig} from './OAuth2LoginProvider' import {Authenticatable} from '../../types' import {Request} from '../../../http/lifecycle/Request' import {ErrorWithContext, uuid4, fetch} from '../../../util' /** * OAuth2LoginProvider implementation that authenticates users against a * Starship CoreID server. */ export class CoreIDLoginProvider extends OAuth2LoginProvider { protected async callback(request: Request): Promise { // Get authentication_code from the request const code = request.safe('code').string() // Get OAuth2 token from CoreID const token = await this.getToken(code) // Get user from endpoint const userData = await this.getUserData(token) // Return authenticatable instance const existing = await this.security.repository.getByIdentifier(userData.uid) if ( existing ) { this.updateUser(existing, userData) return existing } const user = await this.security.repository.createFromCredentials(userData.uid, uuid4()) this.updateUser(user, userData) return user } /** Given an access token, look up the associated user's information. */ protected async getUserData(token: string): Promise { const userResponse = await fetch( this.config.userUrl, { method: 'GET', headers: { Authorization: `Bearer ${token}`, }, }, ) const userData: any = await userResponse.json() if ( !userData?.data?.uid ) { throw new ErrorWithContext('Unable to extract user from response', { userData, }) } return userData.data } /** Given a login code, redeem it for an access token. */ protected async getToken(code: string): Promise { const body: string[] = [ 'code=' + encodeURIComponent(code), 'client_id=' + encodeURIComponent(this.config.clientId), 'client_secret=' + encodeURIComponent(this.config.clientSecret), 'grant_type=authorization_code', ] const response = await fetch( this.config.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: body.join('&'), }, ) const data = await response.json() const token = (data as any).access_token if ( !token ) { throw new ErrorWithContext('Unable to obtain access token from response', { data, }) } return String(token) } /** Update values on the Authenticatable from user data. */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types protected updateUser(user: any, data: any): void { user.firstName = data.first_name user.lastName = data.last_name user.email = data.email user.tagline = data.tagline user.photoUrl = data.profile_photo } }