From 265e40001e0c057be688a8f83546824f995089b8 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Mon, 12 Aug 2019 11:25:49 +0200 Subject: [PATCH] fix: bring paseto token claims inline with jwt-ietf --- lib/models/formats/paseto.js | 8 +- test/storage/jwt.test.js | 214 +-------------------------------- test/storage/jwt_ietf.test.js | 219 +--------------------------------- test/storage/paseto.test.js | 214 +-------------------------------- 4 files changed, 11 insertions(+), 644 deletions(-) diff --git a/lib/models/formats/paseto.js b/lib/models/formats/paseto.js index 175a88bec..3b5f24d5a 100644 --- a/lib/models/formats/paseto.js +++ b/lib/models/formats/paseto.js @@ -28,7 +28,7 @@ module.exports = (provider, { opaque }) => { async getValueAndPayload() { const [, payload] = await opaque.getValueAndPayload.call(this); const { - jti, accountId: sub, iat, exp, scope, clientId: azp, 'x5t#S256': x5t, 'jkt#S256': jkt, extra, + jti, accountId: sub, iat, exp, scope, clientId, 'x5t#S256': x5t, 'jkt#S256': jkt, extra, } = payload; let { aud } = payload; @@ -51,14 +51,16 @@ module.exports = (provider, { opaque }) => { const tokenPayload = { ...extra, jti, - sub, + sub: sub || clientId, kid, iat: iat ? new Date(iat * 1000).toISOString() : undefined, exp: exp ? new Date(exp * 1000).toISOString() : undefined, scope, + client_id: clientId, iss: provider.issuer, - ...(aud ? { aud, azp } : { aud: azp }), + ...(aud ? { aud } : { aud: clientId }), ...(x5t || jkt ? { cnf: {} } : undefined), + // TODO: make auth_time, acr, amr available }; if (x5t) { diff --git a/test/storage/jwt.test.js b/test/storage/jwt.test.js index 597bf5d32..4c111b923 100644 --- a/test/storage/jwt.test.js +++ b/test/storage/jwt.test.js @@ -57,7 +57,7 @@ if (FORMAT === 'jwt') { afterEach(function () { [ - 'AuthorizationCode', 'AccessToken', 'RefreshToken', 'ClientCredentials', 'InitialAccessToken', 'RegistrationAccessToken', 'DeviceCode', + 'AccessToken', 'ClientCredentials', ].forEach((model) => { if (this.TestAdapter.for(model).upsert.restore) { this.TestAdapter.for(model).upsert.restore(); @@ -113,156 +113,6 @@ if (FORMAT === 'jwt') { }); }); - it('for AuthorizationCode', async function () { - const kind = 'AuthorizationCode'; - const upsert = spy(this.TestAdapter.for('AuthorizationCode'), 'upsert'); - const token = new this.provider.AuthorizationCode(fullPayload); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - amr, - authTime, - claims, - clientId, - codeChallenge, - codeChallengeMethod, - consumed, - exp: number, - grantId, - iat: number, - jti: upsert.getCall(0).args[0], - jwt: string, - kind, - nonce, - redirectUri, - resource, - scope, - sid, - sessionUid, - expiresWithSession, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'JWT'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - }); - }); - - it('for RefreshToken', async function () { - const kind = 'RefreshToken'; - const upsert = spy(this.TestAdapter.for('RefreshToken'), 'upsert'); - const token = new this.provider.RefreshToken(fullPayload); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - iiat, - rotations, - amr, - authTime, - claims, - clientId, - consumed, - exp: number, - grantId, - gty, - iat: number, - jti: upsert.getCall(0).args[0], - jwt: string, - kind, - nonce, - resource, - scope, - sid, - 'x5t#S256': s256, - 'jkt#S256': s256, - sessionUid, - expiresWithSession, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'JWT'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - cnf: { - 'x5t#S256': s256, - 'jkt#S256': s256, - }, - }); - }); - - it('for DeviceCode', async function () { - const kind = 'DeviceCode'; - const upsert = spy(this.TestAdapter.for('DeviceCode'), 'upsert'); - const token = new this.provider.DeviceCode(fullPayload); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - amr, - authTime, - claims, - clientId, - codeChallenge, - codeChallengeMethod, - consumed, - deviceInfo, - error, - errorDescription, - exp: number, - grantId, - gty, - iat: number, - jti: upsert.getCall(0).args[0], - jwt: string, - kind, - nonce, - params, - resource, - scope, - sid, - userCode, - sessionUid, - expiresWithSession, - inFlight, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'JWT'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - }); - }); - it('for ClientCredentials', async function () { const kind = 'ClientCredentials'; const upsert = spy(this.TestAdapter.for('ClientCredentials'), 'upsert'); @@ -303,68 +153,6 @@ if (FORMAT === 'jwt') { }); }); - it('for InitialAccessToken', async function () { - const kind = 'InitialAccessToken'; - const upsert = spy(this.TestAdapter.for('InitialAccessToken'), 'upsert'); - const token = new this.provider.InitialAccessToken({ - expiresIn: 100, - ...fullPayload, - }); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - exp: number, - iat: number, - jti: upsert.getCall(0).args[0], - jwt: string, - kind, - policies, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'JWT'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - exp, - iat, - iss: this.provider.issuer, - jti, - }); - }); - - it('for RegistrationAccessToken', async function () { - const kind = 'RegistrationAccessToken'; - const upsert = spy(this.TestAdapter.for('RegistrationAccessToken'), 'upsert'); - const token = new this.provider.RegistrationAccessToken({ - expiresIn: 100, - ...fullPayload, - }); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - clientId, - policies, - exp: number, - iat: number, - jti: upsert.getCall(0).args[0], - jwt: string, - kind, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'JWT'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - }); - }); - describe('invalid signing alg resolved', () => { before(bootstrap(__dirname)); diff --git a/test/storage/jwt_ietf.test.js b/test/storage/jwt_ietf.test.js index 0634c98fe..22ec9bdbf 100644 --- a/test/storage/jwt_ietf.test.js +++ b/test/storage/jwt_ietf.test.js @@ -57,7 +57,7 @@ if (FORMAT === 'jwt-ietf') { afterEach(function () { [ - 'AuthorizationCode', 'AccessToken', 'RefreshToken', 'ClientCredentials', 'InitialAccessToken', 'RegistrationAccessToken', 'DeviceCode', + 'AccessToken', 'ClientCredentials', ].forEach((model) => { if (this.TestAdapter.for(model).upsert.restore) { this.TestAdapter.for(model).upsert.restore(); @@ -113,159 +113,6 @@ if (FORMAT === 'jwt-ietf') { }); }); - it('for AuthorizationCode', async function () { - const kind = 'AuthorizationCode'; - const upsert = spy(this.TestAdapter.for('AuthorizationCode'), 'upsert'); - const token = new this.provider.AuthorizationCode(fullPayload); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - amr, - authTime, - claims, - clientId, - codeChallenge, - codeChallengeMethod, - consumed, - exp: number, - grantId, - iat: number, - jti: upsert.getCall(0).args[0], - 'jwt-ietf': string, - kind, - nonce, - redirectUri, - resource, - scope, - sid, - sessionUid, - expiresWithSession, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'at+jwt'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - client_id: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - }); - }); - - it('for RefreshToken', async function () { - const kind = 'RefreshToken'; - const upsert = spy(this.TestAdapter.for('RefreshToken'), 'upsert'); - const token = new this.provider.RefreshToken(fullPayload); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - iiat, - rotations, - amr, - authTime, - claims, - clientId, - consumed, - exp: number, - grantId, - gty, - iat: number, - jti: upsert.getCall(0).args[0], - 'jwt-ietf': string, - kind, - nonce, - resource, - scope, - sid, - 'x5t#S256': s256, - 'jkt#S256': s256, - sessionUid, - expiresWithSession, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'at+jwt'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - client_id: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - cnf: { - 'x5t#S256': s256, - 'jkt#S256': s256, - }, - }); - }); - - it('for DeviceCode', async function () { - const kind = 'DeviceCode'; - const upsert = spy(this.TestAdapter.for('DeviceCode'), 'upsert'); - const token = new this.provider.DeviceCode(fullPayload); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - amr, - authTime, - claims, - clientId, - codeChallenge, - codeChallengeMethod, - consumed, - deviceInfo, - error, - errorDescription, - exp: number, - grantId, - gty, - iat: number, - jti: upsert.getCall(0).args[0], - 'jwt-ietf': string, - kind, - nonce, - params, - resource, - scope, - sid, - userCode, - sessionUid, - expiresWithSession, - inFlight, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'at+jwt'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - client_id: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - }); - }); - it('for ClientCredentials', async function () { const kind = 'ClientCredentials'; const upsert = spy(this.TestAdapter.for('ClientCredentials'), 'upsert'); @@ -307,70 +154,6 @@ if (FORMAT === 'jwt-ietf') { }); }); - it('for InitialAccessToken', async function () { - const kind = 'InitialAccessToken'; - const upsert = spy(this.TestAdapter.for('InitialAccessToken'), 'upsert'); - const token = new this.provider.InitialAccessToken({ - expiresIn: 100, - ...fullPayload, - }); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - exp: number, - iat: number, - jti: upsert.getCall(0).args[0], - 'jwt-ietf': string, - kind, - policies, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'at+jwt'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - exp, - iat, - iss: this.provider.issuer, - jti, - }); - }); - - it('for RegistrationAccessToken', async function () { - const kind = 'RegistrationAccessToken'; - const upsert = spy(this.TestAdapter.for('RegistrationAccessToken'), 'upsert'); - const token = new this.provider.RegistrationAccessToken({ - expiresIn: 100, - ...fullPayload, - }); - const jwt = await token.save(); - - assert.calledWith(upsert, string, { - clientId, - policies, - exp: number, - iat: number, - jti: upsert.getCall(0).args[0], - 'jwt-ietf': string, - kind, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const header = decode(jwt.split('.')[0]); - expect(header).to.have.property('typ', 'at+jwt'); - const payload = decode(jwt.split('.')[1]); - expect(payload).to.eql({ - aud: clientId, - client_id: clientId, - sub: clientId, - exp, - iat, - iss: this.provider.issuer, - jti, - }); - }); - describe('invalid signing alg resolved', () => { before(bootstrap(__dirname)); diff --git a/test/storage/paseto.test.js b/test/storage/paseto.test.js index dfd80c09a..1ae448222 100644 --- a/test/storage/paseto.test.js +++ b/test/storage/paseto.test.js @@ -58,7 +58,7 @@ if (FORMAT === 'paseto') { afterEach(function () { [ - 'AuthorizationCode', 'AccessToken', 'RefreshToken', 'ClientCredentials', 'InitialAccessToken', 'RegistrationAccessToken', 'DeviceCode', + 'AccessToken', 'ClientCredentials', ].forEach((model) => { if (this.TestAdapter.for(model).upsert.restore) { this.TestAdapter.for(model).upsert.restore(); @@ -98,7 +98,7 @@ if (FORMAT === 'paseto') { expect(payload).to.eql({ ...extra, aud, - azp: clientId, + client_id: clientId, kid, exp: new Date(exp * 1000).toISOString(), iat: new Date(iat * 1000).toISOString(), @@ -113,153 +113,6 @@ if (FORMAT === 'paseto') { }); }); - it('for AuthorizationCode', async function () { - const kind = 'AuthorizationCode'; - const upsert = spy(this.TestAdapter.for('AuthorizationCode'), 'upsert'); - const token = new this.provider.AuthorizationCode(fullPayload); - const paseto = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - amr, - authTime, - claims, - clientId, - codeChallenge, - codeChallengeMethod, - consumed, - exp: number, - grantId, - iat: number, - jti: upsert.getCall(0).args[0], - paseto: string, - kind, - nonce, - redirectUri, - resource, - scope, - sid, - sessionUid, - expiresWithSession, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const payload = decode(paseto); - expect(payload).to.eql({ - aud: clientId, - kid, - exp: new Date(exp * 1000).toISOString(), - iat: new Date(iat * 1000).toISOString(), - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - }); - }); - - it('for RefreshToken', async function () { - const kind = 'RefreshToken'; - const upsert = spy(this.TestAdapter.for('RefreshToken'), 'upsert'); - const token = new this.provider.RefreshToken(fullPayload); - const paseto = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - iiat, - rotations, - amr, - authTime, - claims, - clientId, - consumed, - exp: number, - grantId, - gty, - iat: number, - jti: upsert.getCall(0).args[0], - paseto: string, - kind, - nonce, - resource, - scope, - sid, - 'x5t#S256': s256, - 'jkt#S256': s256, - sessionUid, - expiresWithSession, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const payload = decode(paseto); - expect(payload).to.eql({ - aud: clientId, - kid, - exp: new Date(exp * 1000).toISOString(), - iat: new Date(iat * 1000).toISOString(), - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - cnf: { - 'x5t#S256': s256, - 'jkt#S256': s256, - }, - }); - }); - - it('for DeviceCode', async function () { - const kind = 'DeviceCode'; - const upsert = spy(this.TestAdapter.for('DeviceCode'), 'upsert'); - const token = new this.provider.DeviceCode(fullPayload); - const paseto = await token.save(); - - assert.calledWith(upsert, string, { - accountId, - acr, - amr, - authTime, - claims, - clientId, - codeChallenge, - codeChallengeMethod, - consumed, - deviceInfo, - error, - errorDescription, - exp: number, - grantId, - gty, - iat: number, - jti: upsert.getCall(0).args[0], - paseto, - kind, - nonce, - params, - resource, - scope, - sid, - userCode, - sessionUid, - expiresWithSession, - inFlight, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const payload = decode(paseto); - expect(payload).to.eql({ - aud: clientId, - kid, - exp: new Date(exp * 1000).toISOString(), - iat: new Date(iat * 1000).toISOString(), - iss: this.provider.issuer, - jti, - scope, - sub: accountId, - }); - }); - it('for ClientCredentials', async function () { const kind = 'ClientCredentials'; const upsert = spy(this.TestAdapter.for('ClientCredentials'), 'upsert'); @@ -285,12 +138,13 @@ if (FORMAT === 'paseto') { expect(payload).to.eql({ ...extra, aud, - azp: clientId, + client_id: clientId, kid, exp: new Date(exp * 1000).toISOString(), iat: new Date(iat * 1000).toISOString(), iss: this.provider.issuer, jti, + sub: clientId, scope, cnf: { 'x5t#S256': s256, @@ -299,66 +153,6 @@ if (FORMAT === 'paseto') { }); }); - it('for InitialAccessToken', async function () { - const kind = 'InitialAccessToken'; - const upsert = spy(this.TestAdapter.for('InitialAccessToken'), 'upsert'); - const token = new this.provider.InitialAccessToken({ - expiresIn: 100, - ...fullPayload, - }); - const paseto = await token.save(); - - assert.calledWith(upsert, string, { - exp: number, - iat: number, - jti: upsert.getCall(0).args[0], - paseto: string, - kind, - policies, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const payload = decode(paseto); - expect(payload).to.eql({ - kid, - exp: new Date(exp * 1000).toISOString(), - iat: new Date(iat * 1000).toISOString(), - iss: this.provider.issuer, - jti, - }); - }); - - it('for RegistrationAccessToken', async function () { - const kind = 'RegistrationAccessToken'; - const upsert = spy(this.TestAdapter.for('RegistrationAccessToken'), 'upsert'); - const token = new this.provider.RegistrationAccessToken({ - expiresIn: 100, - ...fullPayload, - }); - const paseto = await token.save(); - - assert.calledWith(upsert, string, { - clientId, - policies, - exp: number, - iat: number, - jti: upsert.getCall(0).args[0], - paseto: string, - kind, - }); - - const { iat, jti, exp } = upsert.getCall(0).args[1]; - const payload = decode(paseto); - expect(payload).to.eql({ - aud: clientId, - kid, - exp: new Date(exp * 1000).toISOString(), - iat: new Date(iat * 1000).toISOString(), - iss: this.provider.issuer, - jti, - }); - }); - describe('paseto when keys are missing', () => { before(bootstrap(__dirname, { config: 'noed25519' }));