Skip to content

Commit 53babe6

Browse files
committed
refactor(PAR): consume PAR after user interactions instead of before
1 parent 18efa70 commit 53babe6

File tree

7 files changed

+33
-10
lines changed

7 files changed

+33
-10
lines changed

example/my_adapter.js

+1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ class MyAdapter {
135135
* - iat {number} - timestamp of the interaction's creation
136136
* - returnTo {string} - after resolving interactions send the user-agent to this url
137137
* - deviceCode {string} - [DeviceCode user flows only] deviceCode reference
138+
* - parJti {string} - [PAR user flows only] PushedAuthorizationCode uid reference
138139
* - params {object} - parsed recognized parameters object
139140
* - lastSubmission {object} - previous interaction result submission
140141
* - trusted {string[]} - parameter names that come from a trusted source

lib/actions/authorization/interactions.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ export default async function interactions(resumeRouteName, ctx, next) {
108108
session: oidc.session,
109109
grant: oidc.grant,
110110
cid: oidc.entities.Interaction?.cid || nanoid(),
111-
...(oidc.deviceCode ? { deviceCode: oidc.deviceCode.jti } : undefined),
111+
deviceCode: oidc.deviceCode?.jti,
112+
parJti: oidc.entities.PushedAuthorizationRequest?.jti || oidc.entities.Interaction?.parJti,
112113
});
113114

114115
let ttl = instance(ctx.oidc.provider).configuration('ttl.Interaction');

lib/actions/authorization/load_pushed_authorization_request.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ export default async function loadPushedAuthorizationRequest(ctx) {
77
const requestObject = await ctx.oidc.provider.PushedAuthorizationRequest.find(id, {
88
ignoreExpiration: true,
99
});
10-
if (!requestObject || requestObject.isExpired) {
11-
throw new InvalidRequestUri('request_uri is invalid or expired');
10+
11+
if (!requestObject || !requestObject.isValid) {
12+
throw new InvalidRequestUri('request_uri is invalid, expired, or was already used');
1213
}
13-
ctx.oidc.entity('PushedAuthorizationRequest', requestObject);
1414

15-
await requestObject.destroy();
15+
ctx.oidc.entity('PushedAuthorizationRequest', requestObject);
1616

1717
return requestObject;
1818
}

lib/actions/authorization/respond.js

+15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import instance from '../../helpers/weak_cache.js';
2+
import { InvalidRequestUri } from '../../helpers/errors.js';
23

34
/*
45
* Based on the authorization request response mode either redirects with parameters in query or
@@ -10,6 +11,20 @@ import instance from '../../helpers/weak_cache.js';
1011
* @emits: authorization.success
1112
*/
1213
export default async function respond(ctx, next) {
14+
let pushedAuthorizationRequest = ctx.oidc.entities.PushedAuthorizationRequest;
15+
16+
if (!pushedAuthorizationRequest && ctx.oidc.entities.Interaction?.parJti) {
17+
pushedAuthorizationRequest = await ctx.oidc.provider.PushedAuthorizationRequest.find(
18+
ctx.oidc.entities.Interaction.parJti,
19+
{ ignoreExpiration: true },
20+
);
21+
}
22+
23+
if (pushedAuthorizationRequest?.consumed) {
24+
throw new InvalidRequestUri('request_uri is invalid, expired, or was already used');
25+
}
26+
await pushedAuthorizationRequest?.consume();
27+
1328
const out = await next();
1429

1530
const { oidc: { params } } = ctx;

lib/models/interaction.js

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export default (provider) => class Interaction extends hasFormat(provider, 'Inte
6767
'lastSubmission',
6868
'deviceCode',
6969
'cid',
70+
'parJti',
7071
];
7172
}
7273
};

lib/models/pushed_authorization_request.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import instance from '../helpers/weak_cache.js';
22

3+
import apply from './mixins/apply.js';
34
import hasFormat from './mixins/has_format.js';
5+
import consumable from './mixins/consumable.js';
46

5-
export default (provider) => class PushedAuthorizationRequest extends hasFormat(provider, 'PushedAuthorizationRequest', instance(provider).BaseModel) {
7+
export default (provider) => class PushedAuthorizationRequest extends apply([
8+
consumable,
9+
hasFormat(provider, 'PushedAuthorizationRequest', instance(provider).BaseModel),
10+
]) {
611
static get IN_PAYLOAD() {
712
return [
813
...super.IN_PAYLOAD,

test/pushed_authorization_requests/pushed_authorization_requests.test.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ describe('Pushed Request Object', () => {
201201
.expect(303)
202202
.expect(auth.validatePresence(['code']));
203203

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

207207
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 () {
@@ -234,7 +234,7 @@ describe('Pushed Request Object', () => {
234234
.expect(303)
235235
.expect(auth.validatePresence(['code']));
236236

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

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

562562
it('handles expired or invalid pushed authorization request object', async function () {
@@ -571,7 +571,7 @@ describe('Pushed Request Object', () => {
571571
.expect(auth.validateState)
572572
.expect(auth.validateClientLocation)
573573
.expect(auth.validateError('invalid_request_uri'))
574-
.expect(auth.validateErrorDescription('request_uri is invalid or expired'));
574+
.expect(auth.validateErrorDescription('request_uri is invalid, expired, or was already used'));
575575
});
576576
});
577577
});

0 commit comments

Comments
 (0)