@solid/oidc-auth-manager
Version:
An OpenID Connect (OIDC) authentication manager (OP, RP and RS) for decentralized peer-to-peer authentication
377 lines (302 loc) • 11.6 kB
JavaScript
'use strict'
const chai = require('chai')
const dirtyChai = require('dirty-chai')
chai.use(dirtyChai)
const sinon = require('sinon')
const sinonChai = require('sinon-chai')
chai.use(sinonChai)
chai.should()
const expect = chai.expect
const HttpMocks = require('node-mocks-http')
const LoginConsentRequest = require('../../src/handlers/login-consent-request')
function createOpAuthRequest (overwrite) {
return Object.assign({
req: {
body: {},
app: {
locals: {
ldp: {
serverUri: 'https://pod.example'
}
}
},
session: {
consentedOrigins: ['https://example.com']
}
},
res: HttpMocks.createResponse(),
subject: {},
params: {
redirect_uri: 'https://example.com'
},
host: {}
}, overwrite)
}
describe('LoginConsentRequest', () => {
describe('constructor()', () => {
it('should initialize a new instance', () => {
const params = { consent: true, scope: 'openid' }
const options = {
opAuthRequest: {},
params,
response: {}
}
const request = new LoginConsentRequest(options)
expect(request.opAuthRequest).to.equal(options.opAuthRequest)
expect(request.params).to.equal(options.params)
expect(request.response).to.equal(options.response)
})
})
describe('extractParams()', () => {
it('should use req.query if present', () => {
const req = { query: { client_id: '1234' } }
const res = HttpMocks.createResponse()
const opAuthRequest = { req, res }
const params = LoginConsentRequest.extractParams(opAuthRequest)
expect(params.client_id).to.equal(req.query.client_id)
})
it('should use req.body if req.query is not present', () => {
const req = { body: { client_id: '1234' } }
const res = HttpMocks.createResponse()
const opAuthRequest = { req, res }
const params = LoginConsentRequest.extractParams(opAuthRequest)
expect(params.client_id).to.equal(req.body.client_id)
})
})
describe('from()', () => {
it('should return an initialized instance', () => {
const body = { consent: true, scope: 'openid' }
const req = { body }
const res = HttpMocks.createResponse()
const opAuthRequest = { req, res }
const request = LoginConsentRequest.from(opAuthRequest)
expect(request.opAuthRequest).to.equal(opAuthRequest)
expect(request.params).to.equal(req.body)
expect(request.response).to.equal(res)
})
})
describe('handle()', () => {
it('should return the opAuthRequest object', () => {
const opAuthRequest = createOpAuthRequest()
return LoginConsentRequest.handle(opAuthRequest)
.then(returnedRequest => {
expect(returnedRequest).to.equal(opAuthRequest)
})
})
it('should invoke obtainConsent()', () => {
const opAuthRequest = createOpAuthRequest()
const obtainConsent = sinon.spy(LoginConsentRequest, 'obtainConsent')
return LoginConsentRequest.handle(opAuthRequest)
.then(() => {
expect(obtainConsent).to.have.been.called()
obtainConsent.resetHistory()
})
})
it('should pass through opAuthRequest if skipConsent is set', () => {
const opAuthRequest = createOpAuthRequest()
const skipConsent = true
return LoginConsentRequest.handle(opAuthRequest, skipConsent)
.then(() => {
expect(LoginConsentRequest.obtainConsent).to.not.have.been.called()
LoginConsentRequest.obtainConsent.resetHistory()
})
})
})
describe('clientId getter', () => {
it('should return the client_id param', () => {
const res = HttpMocks.createResponse()
const body = { client_id: '1234' }
const opAuthRequest = { req: { body }, res }
const request = LoginConsentRequest.from(opAuthRequest)
expect(request.clientId).to.equal('1234')
})
})
describe('isLocalRpClient()', () => {
it('should be false if host has no local client initialized', () => {
const params = { client_id: '1234' }
const res = HttpMocks.createResponse()
const opAuthRequest = createOpAuthRequest({ res })
const request = new LoginConsentRequest({ params, res, opAuthRequest })
expect(request.isLocalRpClient('1234')).to.be.false()
})
it('should be false if params has no client id', () => {
const params = {}
const res = HttpMocks.createResponse()
const opAuthRequest = createOpAuthRequest({ res })
const request = new LoginConsentRequest({ params, res, opAuthRequest })
expect(request.isLocalRpClient(undefined)).to.be.false()
})
it('should be false if host local app origin does not equal param server uri', () => {
const params = {}
const res = HttpMocks.createResponse()
const opAuthRequest = createOpAuthRequest({
res
})
const request = new LoginConsentRequest({ params, res, opAuthRequest })
expect(request.isLocalRpClient('https://example.com')).to.be.false()
})
it('should be true if host local app origin equals param server uri', () => {
const params = {}
const res = HttpMocks.createResponse()
const opAuthRequest = createOpAuthRequest({
res
})
const request = new LoginConsentRequest({ params, res, opAuthRequest })
expect(request.isLocalRpClient('https://pod.example')).to.be.true()
})
})
describe('obtainConsent()', () => {
describe('if request is for a local rp client', () => {
let req, res, opAuthRequest
const host = { localClientId: '1234' }
const clientId = '1234'
beforeEach(() => {
req = { body: { scope: 'openid', client_id: clientId } }
res = HttpMocks.createResponse()
opAuthRequest = createOpAuthRequest({ res, host })
opAuthRequest = Object.assign(opAuthRequest, {
req: Object.assign(opAuthRequest.req, {
body: req.body
})
})
})
it('should mark successful consent automatically', () => {
const request = LoginConsentRequest.from(opAuthRequest)
return LoginConsentRequest.obtainConsent(request)
.then(opAuthRequest => {
expect(opAuthRequest.consent).to.be.true()
expect(opAuthRequest.scope).to.equal('openid')
})
})
it('should not call checkSavedConsentFor()', () => {
const request = LoginConsentRequest.from(opAuthRequest)
const checkSavedConsentFor = sinon.spy(request, 'checkSavedConsentFor')
return LoginConsentRequest.obtainConsent(request)
.then(() => {
expect(checkSavedConsentFor).to.not.have.been.called()
})
})
})
describe('if body.consent param is present', () => {
let req, res, opAuthRequest
const host = {}
const clientId = '1234'
beforeEach(() => {
req = { body: { consent: true, scope: 'openid', client_id: clientId } }
res = HttpMocks.createResponse()
opAuthRequest = createOpAuthRequest({ res, host })
opAuthRequest = Object.assign(opAuthRequest, {
req: Object.assign(opAuthRequest.req, {
body: req.body
})
})
})
it('should call saveConsentForClient()', () => {
const request = LoginConsentRequest.from(opAuthRequest)
request.saveConsentForClient = sinon.mock().returns(Promise.resolve())
return LoginConsentRequest.obtainConsent(request)
.then(() => {
expect(request.saveConsentForClient).to.have.been.called()
})
})
it('should set consent property on request', () => {
const request = LoginConsentRequest.from(opAuthRequest)
return LoginConsentRequest.obtainConsent(request)
.then(opAuthRequest => {
expect(opAuthRequest.consent).to.be.true()
})
})
it('should set scope property on request', () => {
const request = LoginConsentRequest.from(opAuthRequest)
return LoginConsentRequest.obtainConsent(request)
.then(opAuthRequest => {
expect(opAuthRequest.scope).to.equal('openid')
})
})
it('should not render any pages', () => {
const render = sinon.stub(opAuthRequest.res, 'render')
const request = LoginConsentRequest.from(opAuthRequest)
return LoginConsentRequest.obtainConsent(request)
.then(opAuthRequest => {
expect(render).to.not.have.been.called()
})
})
})
describe('if body.consent param is NOT present', () => {
let req, res, opAuthRequest
beforeEach(() => {
req = { body: { scope: 'openid' } }
res = HttpMocks.createResponse()
opAuthRequest = createOpAuthRequest({ res })
opAuthRequest = Object.assign(opAuthRequest, {
req: Object.assign(opAuthRequest.req, {
session: {
consentedOrigins: []
},
body: req.body
})
})
})
describe('if user consent has been previously saved', () => {
it('should have marked the request as successful', () => {
const request = LoginConsentRequest.from(opAuthRequest)
request.checkSavedConsentFor = sinon.mock()
.returns(Promise.resolve(true))
return LoginConsentRequest.obtainConsent(request)
.then(opAuthRequest => {
expect(opAuthRequest.consent).to.be.true()
expect(opAuthRequest.scope).to.equal('openid')
})
})
it('should not have called renderConsentPage()', () => {
})
})
describe('if user consent has NOT been previously saved', () => {
it('should call redirectToConsent()', () => {
const request = LoginConsentRequest.from(opAuthRequest)
request.checkSavedConsentFor = sinon.mock()
.returns(Promise.resolve(false))
request.response.render = sinon.mock()
const renderConsentPage = sinon.spy(request, 'redirectToConsent')
return LoginConsentRequest.obtainConsent(request)
.catch(() => {})
.then(() => {
expect(renderConsentPage).to.have.been.called()
})
})
it('should not have marked success', () => {
const request = LoginConsentRequest.from(opAuthRequest)
request.checkSavedConsentFor = sinon.mock()
.returns(Promise.resolve(false))
request.response.render = sinon.mock()
return LoginConsentRequest.obtainConsent(request)
.catch((opAuthRequest) => opAuthRequest)
.then(opAuthRequest => {
expect(opAuthRequest.consent).to.not.exist()
expect(opAuthRequest.scope).to.not.exist()
})
})
})
})
})
describe('redirectToConsent()', () => {
it('should call res.redirect', () => {
const res = HttpMocks.createResponse()
const redirect = sinon.stub(res, 'redirect')
let opAuthRequest = createOpAuthRequest({ res })
opAuthRequest = Object.assign(opAuthRequest, {
req: Object.assign(opAuthRequest.req, {
session: {
consentedOrigins: []
}
})
})
const request = LoginConsentRequest.from(opAuthRequest)
return LoginConsentRequest.obtainConsent(request)
.catch(() => {})
.then(() => {
expect(redirect).to.have.been.called()
})
})
})
})