Skip to content

Commit

Permalink
refactor(PAR): consume PAR after user interactions instead of before
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Jan 16, 2024
1 parent 18efa70 commit 53babe6
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 10 deletions.
1 change: 1 addition & 0 deletions example/my_adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class MyAdapter {
* - iat {number} - timestamp of the interaction's creation
* - returnTo {string} - after resolving interactions send the user-agent to this url
* - deviceCode {string} - [DeviceCode user flows only] deviceCode reference
* - parJti {string} - [PAR user flows only] PushedAuthorizationCode uid reference
* - params {object} - parsed recognized parameters object
* - lastSubmission {object} - previous interaction result submission
* - trusted {string[]} - parameter names that come from a trusted source
Expand Down
3 changes: 2 additions & 1 deletion lib/actions/authorization/interactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ export default async function interactions(resumeRouteName, ctx, next) {
session: oidc.session,
grant: oidc.grant,
cid: oidc.entities.Interaction?.cid || nanoid(),
...(oidc.deviceCode ? { deviceCode: oidc.deviceCode.jti } : undefined),
deviceCode: oidc.deviceCode?.jti,
parJti: oidc.entities.PushedAuthorizationRequest?.jti || oidc.entities.Interaction?.parJti,
});

let ttl = instance(ctx.oidc.provider).configuration('ttl.Interaction');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ export default async function loadPushedAuthorizationRequest(ctx) {
const requestObject = await ctx.oidc.provider.PushedAuthorizationRequest.find(id, {
ignoreExpiration: true,
});
if (!requestObject || requestObject.isExpired) {
throw new InvalidRequestUri('request_uri is invalid or expired');

if (!requestObject || !requestObject.isValid) {
throw new InvalidRequestUri('request_uri is invalid, expired, or was already used');
}
ctx.oidc.entity('PushedAuthorizationRequest', requestObject);

await requestObject.destroy();
ctx.oidc.entity('PushedAuthorizationRequest', requestObject);

return requestObject;
}
15 changes: 15 additions & 0 deletions lib/actions/authorization/respond.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import instance from '../../helpers/weak_cache.js';
import { InvalidRequestUri } from '../../helpers/errors.js';

/*
* Based on the authorization request response mode either redirects with parameters in query or
Expand All @@ -10,6 +11,20 @@ import instance from '../../helpers/weak_cache.js';
* @emits: authorization.success
*/
export default async function respond(ctx, next) {
let pushedAuthorizationRequest = ctx.oidc.entities.PushedAuthorizationRequest;

if (!pushedAuthorizationRequest && ctx.oidc.entities.Interaction?.parJti) {
pushedAuthorizationRequest = await ctx.oidc.provider.PushedAuthorizationRequest.find(
ctx.oidc.entities.Interaction.parJti,
{ ignoreExpiration: true },
);
}

if (pushedAuthorizationRequest?.consumed) {
throw new InvalidRequestUri('request_uri is invalid, expired, or was already used');
}
await pushedAuthorizationRequest?.consume();

const out = await next();

const { oidc: { params } } = ctx;
Expand Down
1 change: 1 addition & 0 deletions lib/models/interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export default (provider) => class Interaction extends hasFormat(provider, 'Inte
'lastSubmission',
'deviceCode',
'cid',
'parJti',
];
}
};
7 changes: 6 additions & 1 deletion lib/models/pushed_authorization_request.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import instance from '../helpers/weak_cache.js';

import apply from './mixins/apply.js';
import hasFormat from './mixins/has_format.js';
import consumable from './mixins/consumable.js';

export default (provider) => class PushedAuthorizationRequest extends hasFormat(provider, 'PushedAuthorizationRequest', instance(provider).BaseModel) {
export default (provider) => class PushedAuthorizationRequest extends apply([
consumable,
hasFormat(provider, 'PushedAuthorizationRequest', instance(provider).BaseModel),
]) {
static get IN_PAYLOAD() {
return [
...super.IN_PAYLOAD,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ describe('Pushed Request Object', () => {
.expect(303)
.expect(auth.validatePresence(['code']));

expect(await this.provider.PushedAuthorizationRequest.find(id)).not.to.be.ok;
expect(await this.provider.PushedAuthorizationRequest.find(id)).to.be.ok.and.have.property('consumed').and.is.ok;
});

it('allows the request_uri to be used (when request object was not used but client has request_object_signing_alg for its optional use)', async function () {
Expand Down Expand Up @@ -234,7 +234,7 @@ describe('Pushed Request Object', () => {
.expect(303)
.expect(auth.validatePresence(['code']));

expect(await this.provider.PushedAuthorizationRequest.find(id)).not.to.be.ok;
expect(await this.provider.PushedAuthorizationRequest.find(id)).to.be.ok.and.have.property('consumed').and.is.ok;
});
});
});
Expand Down Expand Up @@ -556,7 +556,7 @@ describe('Pushed Request Object', () => {
.expect(303)
.expect(auth.validatePresence(['code']));

expect(await this.provider.PushedAuthorizationRequest.find(id)).not.to.be.ok;
expect(await this.provider.PushedAuthorizationRequest.find(id)).to.be.ok.and.have.property('consumed').and.is.ok;
});

it('handles expired or invalid pushed authorization request object', async function () {
Expand All @@ -571,7 +571,7 @@ describe('Pushed Request Object', () => {
.expect(auth.validateState)
.expect(auth.validateClientLocation)
.expect(auth.validateError('invalid_request_uri'))
.expect(auth.validateErrorDescription('request_uri is invalid or expired'));
.expect(auth.validateErrorDescription('request_uri is invalid, expired, or was already used'));
});
});
});
Expand Down

0 comments on commit 53babe6

Please sign in to comment.