Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: update jose and openid-client lib #397

Merged
merged 9 commits into from
Jan 18, 2022
84 changes: 26 additions & 58 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
]
},
"engines": {
"node": ">=12.19.0 <=15.11.0"
"node": ">=12.19.0 <=16.13.0"
christian-hawk marked this conversation as resolved.
Show resolved Hide resolved
},
"scripts": {
"start": "NODE_ENV=production node server/app.js",
Expand Down Expand Up @@ -43,12 +43,12 @@
"express-session": "^1.17.2",
"global-agent": "^3.0.0",
"got": "^11.8.1",
"jose": "^2.0.4",
"jose": "^4.3.7",
"jsonwebtoken": "^8.4.0",
"memcached": "^2.2.2",
"memorystore": "^1.6.6",
"morgan": "^1.9.1",
"openid-client": "^4.7.5",
"openid-client": "^5.1.1",
"passport": "^0.4.0",
"passport-dropbox-oauth2": "^1.1.0",
"passport-facebook": "^3.0.0",
Expand Down
18 changes: 11 additions & 7 deletions server/utils/openid-client-helper.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { JWK: { generateSync }, JWKS: { KeyStore, asKeyStore } } = require('jose')
const jose = require('jose')
const { Issuer } = require('openid-client')
const path = require('path')
const fs = require('fs')
Expand All @@ -12,12 +12,17 @@ const clientJWKSFilePath = path.join(`${process.cwd()}/server`, 'jwks')
* @returns undefined
*/
async function generateJWKS (provider) {
const keyType = generateSync('RSA')
const keyStore = new KeyStore(keyType)
const fileName = path.join(fileUtils.makeDir(clientJWKSFilePath), provider.id + '.json')
if (!fs.existsSync(fileName)) {
await fileUtils.writeDataToFile(fileName, JSON.stringify(keyStore.toJWKS(true)))
if (fs.existsSync(fileName)) {
return
}

const { privateKey, publicKey } = await jose.generateKeyPair('RS256')
const privateJwk = await jose.exportJWK(privateKey)
const publicJwk = await jose.exportJWK(publicKey)
const kid = await jose.calculateJwkThumbprint(publicJwk)
privateJwk.kid = kid
await fileUtils.writeDataToFile(fileName, JSON.stringify({ keys: [privateJwk] }))
}

const clients = []
Expand Down Expand Up @@ -55,8 +60,7 @@ async function getClient (provider) {
// generate jwks
await generateJWKS(provider)
const jwks = require(path.join(fileUtils.makeDir(clientJWKSFilePath), `${provider.id}.json`))
const ks = asKeyStore(jwks)
client = new issuer.Client(options, ks.toJWKS(true))
client = new issuer.Client(options, jwks)
} else {
client = new issuer.Client(options)
}
Expand Down
20 changes: 17 additions & 3 deletions test/openid-client-helper.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('Integration Test OpenID Client Helper', () => {
const testProvider = passportConfigAuthorizedResponse.providers.find(p => p.id === 'oidccedev6privatejwt')
let kid = null
const jwksFilePath = `../server/jwks/${testProvider.id}.json`
let jwks

describe('generateJWKS test', () => {
const generateJWKS = rewiredOpenIDClientHelper.__get__('generateJWKS')
Expand All @@ -19,16 +20,29 @@ describe('Integration Test OpenID Client Helper', () => {
assert.exists(jwksFilePath, `${jwksFilePath} file not found`)
})

it('make sure jwks has keys and kid', () => {
const jwks = require(jwksFilePath)
it('jwks should have keys', () => {
jwks = require(jwksFilePath)
assert.isArray(jwks.keys, 'keys not found in jwks')
})

it('jwks should have kid', () => {
kid = jwks.keys[0].kid
assert.exists(kid, 'kid not found in jwks')
})

it('jwks should have n', () => {
const n = jwks.keys[0].n
assert.exists(n, 'n not found in jwks')
})

it('jwks should have kty', () => {
const kty = jwks.keys[0].kty
assert.exists(kty, 'kty not found in jwks')
})

it('make sure generateJWKS not regenerating jwks again and rewrite existing jwks data', async () => {
await generateJWKS(testProvider)
const jwks = require(jwksFilePath)
jwks = require(jwksFilePath)
assert.equal(kid, jwks.keys[0].kid, `${kid} is not matching with ${jwks.keys[0].kid}`)
})
})
Expand Down
47 changes: 47 additions & 0 deletions test/openid-client-helper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ const rewiredOpenIDClientHelper = rewire('../server/utils/openid-client-helper')
const InitMock = require('./testdata/init-mock')
const config = require('config')
const nock = require('nock')
const sinon = require('sinon')
const jose = require('jose')
const { v4: uuidv4 } = require('uuid')
const fileUtils = require('../server/utils/file-utils')

const assert = chai.assert
const passportConfigAuthorizedResponse = config.get('passportConfigAuthorizedResponse')
Expand All @@ -14,6 +18,11 @@ describe('Test OpenID Client Helper', () => {

describe('generateJWKS test', () => {
const generateJWKS = rewiredOpenIDClientHelper.__get__('generateJWKS')
const callGenerateJWKS = async () => {
try {
await generateJWKS({ id: uuidv4() })
} catch (e) {}
}

it('should exist', () => {
assert.exists(generateJWKS)
Expand All @@ -22,6 +31,44 @@ describe('Test OpenID Client Helper', () => {
it('should be function', () => {
assert.isFunction(generateJWKS, 'generateJWKS is not a function')
})

it('should call fileUtils.makeDir once', async () => {
const makeDirSpy = sinon.spy(fileUtils, 'makeDir')
await callGenerateJWKS()
assert.isTrue(makeDirSpy.calledOnce)
sinon.restore()
})

it('should call generateKeyPair once', async () => {
const generateKeyPairSpy = sinon.spy()
sinon.stub(jose, 'generateKeyPair').value(generateKeyPairSpy)
await callGenerateJWKS()
assert.isTrue(generateKeyPairSpy.calledOnce)
sinon.restore()
})

it('should call exportJWK twice', async () => {
const exportJWKSpy = sinon.spy()
sinon.stub(jose, 'exportJWK').value(exportJWKSpy)
await callGenerateJWKS()
assert.isTrue(exportJWKSpy.calledTwice)
sinon.restore()
})

it('should call calculateJwkThumbprint once', async () => {
const calculateJwkThumbprintSpy = sinon.spy()
sinon.stub(jose, 'calculateJwkThumbprint').value(calculateJwkThumbprintSpy)
await callGenerateJWKS()
assert.isTrue(calculateJwkThumbprintSpy.calledOnce)
sinon.restore()
})

it('should call fileUtils.writeDataToFile once', async () => {
const writeDataToFileSpy = sinon.spy(fileUtils, 'writeDataToFile')
await callGenerateJWKS()
assert.isTrue(writeDataToFileSpy.calledOnce)
sinon.restore()
})
})

describe('getIssuer test', () => {
Expand Down