Skip to content

Commit

Permalink
fix: make structured token's end-user "sub" pairwise if configured
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Aug 18, 2019
1 parent a7c197a commit 24a08c2
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 13 deletions.
20 changes: 16 additions & 4 deletions lib/models/formats/jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ function getClaim(token, claim) {
module.exports = (provider, { opaque }) => {
async function getSigningAlgAndKey(ctx, token, clientId) {
let client;
if (clientId) {
client = await provider.Client.find(clientId);
assert(client);
if (clientId) { // TODO: in v7.x require token.client to be set
client = token.client || await provider.Client.find(clientId);
assert(client && client.clientId === clientId);
}

const { keystore, configuration } = instance(provider);
Expand Down Expand Up @@ -45,15 +45,27 @@ module.exports = (provider, { opaque }) => {
async getValueAndPayload() {
const [, payload] = await opaque.getValueAndPayload.call(this);
const {
jti, accountId: sub, iat, exp, scope, aud, clientId: azp, 'x5t#S256': x5t, 'jkt#S256': jkt, extra,
jti, iat, exp, scope, aud, clientId: azp, 'x5t#S256': x5t, 'jkt#S256': jkt, extra,
} = payload;
let { accountId: sub } = payload;

let value;
if (this.jwt) {
value = this.jwt;
} else {
const ctx = ctxRef.get(this);
const { key, alg } = await getSigningAlgAndKey(ctx, this, azp);

if (sub) {
// TODO: in v7.x require token.client to be set
const client = this.client || await provider.Client.find(azp);
assert(client && client.clientId === azp);
if (client.sectorIdentifier) {
const pairwiseIdentifier = instance(provider).configuration('pairwiseIdentifier');
sub = await pairwiseIdentifier(ctx, sub, client);
}
}

const tokenPayload = {
...extra,
jti,
Expand Down
16 changes: 14 additions & 2 deletions lib/models/formats/jwt_ietf.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { strict: assert } = require('assert');

const instance = require('../../helpers/weak_cache');
const JWT = require('../../helpers/jwt');
const ctxRef = require('../ctx_ref');

Expand All @@ -11,9 +12,9 @@ module.exports = (provider, { opaque, jwt }) => ({
async getValueAndPayload() {
const [, payload] = await opaque.getValueAndPayload.call(this);
const {
jti, accountId: sub, iat, exp, scope, clientId, 'x5t#S256': x5t, 'jkt#S256': jkt, extra,
jti, iat, exp, scope, clientId, 'x5t#S256': x5t, 'jkt#S256': jkt, extra,
} = payload;
let { aud } = payload;
let { aud, accountId: sub } = payload;

if (Array.isArray(aud)) {
if (aud.length > 1) {
Expand All @@ -29,6 +30,17 @@ module.exports = (provider, { opaque, jwt }) => ({
} else {
const ctx = ctxRef.get(this);
const { key, alg } = await jwt.getSigningAlgAndKey(ctx, this, clientId);

if (sub) {
// TODO: in v7.x require token.client to be set
const client = this.client || await provider.Client.find(clientId);
assert(client && client.clientId === clientId);
if (client.sectorIdentifier) {
const pairwiseIdentifier = instance(provider).configuration('pairwiseIdentifier');
sub = await pairwiseIdentifier(ctx, sub, client);
}
}

const tokenPayload = {
...extra,
jti,
Expand Down
16 changes: 14 additions & 2 deletions lib/models/formats/paseto.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const { createPrivateKey } = require('crypto');
const paseto = require('../../helpers/paseto');
const instance = require('../../helpers/weak_cache');
const nanoid = require('../../helpers/nanoid');
const ctxRef = require('../ctx_ref');

module.exports = (provider, { opaque }) => {
let key;
Expand All @@ -28,9 +29,9 @@ module.exports = (provider, { opaque }) => {
async getValueAndPayload() {
const [, payload] = await opaque.getValueAndPayload.call(this);
const {
jti, accountId: sub, iat, exp, scope, clientId, 'x5t#S256': x5t, 'jkt#S256': jkt, extra,
jti, iat, exp, scope, clientId, 'x5t#S256': x5t, 'jkt#S256': jkt, extra,
} = payload;
let { aud } = payload;
let { aud, accountId: sub } = payload;

if (Array.isArray(aud)) {
if (aud.length > 1) {
Expand All @@ -48,6 +49,17 @@ module.exports = (provider, { opaque }) => {
getSigningKey();
}

if (sub) {
// TODO: in v7.x require token.client to be set
const client = this.client || await provider.Client.find(clientId);
assert(client && client.clientId === clientId);
if (client.sectorIdentifier) {
const ctx = ctxRef.get(this);
const pairwiseIdentifier = instance(provider).configuration('pairwiseIdentifier');
sub = await pairwiseIdentifier(ctx, sub, client);
}
}

const tokenPayload = {
...extra,
jti,
Expand Down
49 changes: 49 additions & 0 deletions test/storage/jwt.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,55 @@ if (FORMAT === 'jwt') {
});
});

it('for pairwise AccessToken', async function () {
const kind = 'AccessToken';
const upsert = spy(this.TestAdapter.for('AccessToken'), 'upsert');
const client = await this.provider.Client.find('pairwise');
const token = new this.provider.AccessToken({ client, ...fullPayload });
const jwt = await token.save();

assert.calledWith(upsert, string, {
accountId,
aud,
claims,
clientId: 'pairwise',
exp: number,
grantId,
gty,
iat: number,
jti: upsert.getCall(0).args[0],
jwt: string,
kind,
scope,
sid,
'x5t#S256': s256,
'jkt#S256': s256,
sessionUid,
expiresWithSession,
extra,
});

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({
...extra,
aud,
azp: 'pairwise',
exp,
iat,
iss: this.provider.issuer,
jti,
scope,
sub: 'pairwise-sub',
cnf: {
'x5t#S256': s256,
'jkt#S256': s256,
},
});
});

it('for ClientCredentials', async function () {
const kind = 'ClientCredentials';
const upsert = spy(this.TestAdapter.for('ClientCredentials'), 'upsert');
Expand Down
49 changes: 49 additions & 0 deletions test/storage/jwt_ietf.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,55 @@ if (FORMAT === 'jwt-ietf') {
});
});

it('for pairwise AccessToken', async function () {
const kind = 'AccessToken';
const upsert = spy(this.TestAdapter.for('AccessToken'), 'upsert');
const client = await this.provider.Client.find('pairwise');
const token = new this.provider.AccessToken({ client, ...fullPayload });
const jwt = await token.save();

assert.calledWith(upsert, string, {
accountId,
aud,
claims,
clientId: 'pairwise',
exp: number,
grantId,
gty,
iat: number,
jti: upsert.getCall(0).args[0],
'jwt-ietf': string,
kind,
scope,
sid,
'x5t#S256': s256,
'jkt#S256': s256,
sessionUid,
expiresWithSession,
extra,
});

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({
...extra,
aud,
client_id: 'pairwise',
exp,
iat,
iss: this.provider.issuer,
jti,
scope,
sub: 'pairwise-sub',
cnf: {
'x5t#S256': s256,
'jkt#S256': s256,
},
});
});

it('for ClientCredentials', async function () {
const kind = 'ClientCredentials';
const upsert = spy(this.TestAdapter.for('ClientCredentials'), 'upsert');
Expand Down
48 changes: 48 additions & 0 deletions test/storage/paseto.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,54 @@ if (FORMAT === 'paseto') {
});
});

it('for pairwise AccessToken', async function () {
const kind = 'AccessToken';
const upsert = spy(this.TestAdapter.for('AccessToken'), 'upsert');
const client = await this.provider.Client.find('pairwise');
const token = new this.provider.AccessToken({ client, ...fullPayload });
const paseto = await token.save();

assert.calledWith(upsert, string, {
accountId,
aud,
claims,
clientId: 'pairwise',
exp: number,
grantId,
gty,
iat: number,
jti: upsert.getCall(0).args[0],
paseto: string,
kind,
scope,
sid,
'x5t#S256': s256,
'jkt#S256': s256,
sessionUid,
expiresWithSession,
extra,
});

const { iat, jti, exp } = upsert.getCall(0).args[1];
const payload = decode(paseto);
expect(payload).to.eql({
...extra,
aud,
client_id: 'pairwise',
kid,
exp: new Date(exp * 1000).toISOString(),
iat: new Date(iat * 1000).toISOString(),
iss: this.provider.issuer,
jti,
scope,
sub: 'pairwise-sub',
cnf: {
'x5t#S256': s256,
'jkt#S256': s256,
},
});
});

it('for ClientCredentials', async function () {
const kind = 'ClientCredentials';
const upsert = spy(this.TestAdapter.for('ClientCredentials'), 'upsert');
Expand Down
20 changes: 15 additions & 5 deletions test/storage/storage.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ config.features.registration = {
foo() {},
},
};
config.subjectTypes = ['public', 'pairwise'];
config.pairwiseIdentifier = () => 'pairwise-sub';

module.exports = {
config,
clients: [{
client_id: 'client',
client_secret: 'secret',
redirect_uris: ['https://client.example.com/cb'],
}],
clients: [
{
client_id: 'client',
client_secret: 'secret',
redirect_uris: ['https://client.example.com/cb'],
},
{
client_id: 'pairwise',
client_secret: 'secret',
redirect_uris: ['https://client.example.com/cb'],
subject_type: 'pairwise',
},
],
};

0 comments on commit 24a08c2

Please sign in to comment.