179 lines
5.3 KiB
JavaScript
179 lines
5.3 KiB
JavaScript
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)
|