import { InvalidRequest, UnsupportedResponseType, InvalidClient, UnauthorizedClient, InvalidScope, AccessDenied, Forbidden } from '../error' import { data as dataResponse } from '../response' import * as model from '../model' import wrap from '../wrap' export const authorization = wrap(async (req, res) => { let clientId = null let redirectUri = null let responseType = null let grantTypes = [] let scope = null let user = null if (!req.query.redirect_uri) { throw new InvalidRequest('redirect_uri field is mandatory for authorization endpoint') } redirectUri = req.query.redirect_uri console.debug('Parameter redirect uri is', redirectUri) if (!req.query.client_id) { throw new InvalidRequest('client_id field is mandatory for authorization endpoint') } // Check for client_secret (prevent passing it) if (req.query.client_secret) { throw new InvalidRequest('client_secret field should not be passed to the authorization endpoint') } clientId = req.query.client_id console.debug('Parameter client_id is', clientId) if (!req.query.response_type) { throw new InvalidRequest('response_type field is mandatory for authorization endpoint') } responseType = req.query.response_type console.debug('Parameter response_type is', responseType) // Support multiple types const responseTypes = responseType.split(' ') for (const i in responseTypes) { switch (responseTypes[i]) { case 'code': grantTypes.push('authorization_code') break case 'token': grantTypes.push('implicit') break // case 'id_token': case 'none': grantTypes.push(responseTypes[i]) break default: throw new UnsupportedResponseType('Unknown response_type parameter passed') } } // Filter out duplicates grantTypes = grantTypes.filter(function (value, index, self) { return self.indexOf(value) === index }) // "None" type cannot be combined with others if (grantTypes.length > 1 && grantTypes.indexOf('none') !== -1) { throw new InvalidRequest('Grant type "none" cannot be combined with other grant types') } console.debug('Parameter grant_type is', grantTypes.join(' ')) const client = await req.oauth2.model.client.fetchById(clientId) if (!client) { throw new InvalidClient('Client not found') } // TODO: multiple redirect URI if (!req.oauth2.model.client.getRedirectUri(client)) { throw new UnsupportedResponseType('The client has not set a redirect uri') } else if (!req.oauth2.model.client.checkRedirectUri(client, redirectUri)) { throw new InvalidRequest('Wrong RedirectUri provided') } console.debug('redirect_uri check passed') // The client needs to support all grant types for (const i in grantTypes) { if (!req.oauth2.model.client.checkGrantType(client, grantTypes[i]) && grantTypes[i] !== 'none') { throw new UnauthorizedClient('This client does not support grant type ' + grantTypes[i]) } } console.debug('Grant type check passed') scope = req.oauth2.model.client.transformScope(req.query.scope) scope = req.oauth2.model.client.checkScope(client, scope) if (!scope) { throw new InvalidScope('Client does not allow access to this scope') } console.debug('Scope check passed') user = await req.oauth2.model.user.fetchFromRequest(req) if (!user) { throw new InvalidRequest('There is no currently logged in user') } else { if (!user.username) { throw new Forbidden(user) } console.debug('User fetched from request') } let resObj = {} let consented = false if (req.method === 'GET') { if (client.verified === 1) { consented = true } else { consented = await model.user.consented(user.id, client.id, scope) } // Ask for consent if (!consented) return req.oauth2.decision(req, res, client, scope, user, redirectUri) } // Consent pushed, ensure valid session if (req.method === 'POST' && req.session.csrf && !(req.body.csrf && req.body.csrf === req.session.csrf)) { throw new InvalidRequest('Invalid session') } // Save consent if (!consented) { if (!req.body || (typeof req.body.decision) === 'undefined') { throw new InvalidRequest('No decision parameter passed') } else if (req.body.decision === '0') { throw new AccessDenied('User denied access to the resource') } console.debug('Decision check passed') await model.user.consent(user.id, client.id, scope) } for (const i in grantTypes) { let data = null switch (grantTypes[i]) { case 'authorization_code': data = await model.code.create(model.user.getId(user), model.client.getId(client), scope, model.code.ttl) resObj = Object.assign({ code: data }, resObj) break case 'implicit': data = await model.accessToken.create(model.user.getId(user), model.client.getId(client), scope, model.accessToken.ttl) resObj = Object.assign({ token_type: 'bearer', access_token: data, expires_in: req.oauth2.model.accessToken.ttl }, resObj) break case 'none': resObj = {} break default: throw new UnsupportedResponseType('Unknown response_type parameter passed') } } // Return non-code response types as fragment instead of query return dataResponse(req, res, resObj, redirectUri, responseType !== 'code') }, true)