import {Controller} from '../../http/Controller' import {Injectable} from '../../di' import {ResponseObject, Route} from '../../http/routing/Route' import {Request} from '../../http/lifecycle/Request' import {Session} from '../../http/session/Session' import {OAuth2Client, ClientRepository, OAuth2Scope, ScopeRepository} from './types' import {HTTPError} from '../../http/HTTPError' import {HTTPStatus, Maybe} from '../../util' import {view} from '../../http/response/ViewResponseFactory' @Injectable() export class OAuth2Server extends Controller { public static routes(): void { Route.get('/oauth2/authorize') .passingRequest() .calls(OAuth2Server, x => x.promptForAuthorization.bind(x)) } async promptForAuthorization(request: Request): Promise { // Look up the client in the client repo const client = await this.getClient(request) // Make sure the requested flow type is valid for this client const session = request.make(Session) const flowType = request.safe('response_type').in(client.allowedFlows) const redirectUri = request.safe('redirect_uri').in(client.allowedRedirectUris) session.set('oauth2.authorize.clientId', client.id) session.set('oauth2.authorize.flow', flowType) session.set('oauth2.authorize.redirectUri', redirectUri) // Set the state if necessary const state = request.input('state') || '' if ( state ) { session.set('oauth2.authorize.state', String(state)) } else { session.forget('oauth2.authorize.state') } // If the request specified a scope, validate it and set it in the session const scope = await this.getScope(request, client) // Show a view prompting the user to approve the access return view('@extollo:oauth2:authorize', { clientName: client.display, scopeDescription: scope?.description, }) } protected async getClient(request: Request): Promise { const clientRepo = request.make(ClientRepository) const clientId = request.safe('client_id').string() const client = await clientRepo.find(clientId) if ( !client ) { throw new HTTPError(HTTPStatus.BAD_REQUEST, 'Invalid client configuration', { clientId, }) } return client } protected async getScope(request: Request, client: OAuth2Client): Promise> { const session = request.make(Session) const scopeName = String(request.input('scope') || '') let scope: Maybe = undefined if ( scopeName ) { const scopeRepo = request.make(ScopeRepository) scope = await scopeRepo.findByName(scopeName) if ( !scope || !client.allowedScopeIds.includes(scope.id) ) { throw new HTTPError(HTTPStatus.BAD_REQUEST, 'Invalid scope', { scopeName, }) } session.set('oauth2.authorize.scope', scope.id) } else { session.forget('oauth2.authorize.state') } return scope } }