Skip to content

Commit

Permalink
refactor(JAR): Request Objects are no longer checked for one time use
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Jan 16, 2024
1 parent 9131cd5 commit 18efa70
Show file tree
Hide file tree
Showing 4 changed files with 8 additions and 119 deletions.
18 changes: 8 additions & 10 deletions lib/actions/authorization/check_dpop_jkt.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,18 @@ import epochTime from '../../helpers/epoch_time.js';
* when provided, otherwise defaults dpop_jkt to it.
*/
export default async function checkDpopJkt(ctx, next) {
const { params, route } = ctx.oidc;
const { params } = ctx.oidc;

const dPoP = await dpopValidate(ctx);
if (dPoP) {
if (route !== 'pushed_authorization_request') {
const { ReplayDetection } = ctx.oidc.provider;
const unique = await ReplayDetection.unique(
ctx.oidc.client.clientId,
dPoP.jti,
epochTime() + DPOP_OK_WINDOW,
);
const { ReplayDetection } = ctx.oidc.provider;
const unique = await ReplayDetection.unique(
ctx.oidc.client.clientId,
dPoP.jti,
epochTime() + DPOP_OK_WINDOW,
);

ctx.assert(unique, new InvalidRequest('DPoP proof JWT Replay detected'));
}
ctx.assert(unique, new InvalidRequest('DPoP proof JWT Replay detected'));

if (params.dpop_jkt && params.dpop_jkt !== dPoP.thumbprint) {
throw new InvalidRequest('DPoP proof key thumbprint does not match dpop_jkt');
Expand Down
27 changes: 0 additions & 27 deletions lib/actions/authorization/process_request_object.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import * as JWT from '../../helpers/jwt.js';
import instance from '../../helpers/weak_cache.js';
import { InvalidRequest, InvalidRequestObject, OIDCProviderError } from '../../helpers/errors.js';
import isPlainObject from '../../helpers/_/is_plain_object.js';
import dpopValidate, { DPOP_OK_WINDOW } from '../../helpers/validate_dpop.js';
import epochTime from '../../helpers/epoch_time.js';

/*
* Decrypts and validates the content of provided request parameter and replaces the parameters
Expand Down Expand Up @@ -190,31 +188,6 @@ export default async function processRequestObject(PARAM_LIST, rejectDupesMiddle
}
}

if (!pushedRequestObject && payload.jti && payload.exp && payload.iss) {
if (route === 'pushed_authorization_request') {
const dPoP = await dpopValidate(ctx);
if (dPoP) {
const { ReplayDetection } = ctx.oidc.provider;
const unique = await ReplayDetection.unique(
ctx.oidc.client.clientId,
dPoP.jti,
epochTime() + DPOP_OK_WINDOW,
);

ctx.assert(unique, new InvalidRequest('DPoP proof JWT Replay detected'));
}
}
const unique = await ctx.oidc.provider.ReplayDetection.unique(
payload.iss,
payload.jti,
payload.exp + conf.clockTolerance,
);

if (!unique) {
throw new InvalidRequestObject('request object replay detected');
}
}

if (trusted) {
ctx.oidc.trusted = Object.keys(request);
} else if (ctx.oidc.insecureRequestUri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,37 +329,6 @@ describe('Pushed Request Object', () => {
expect(spy).to.have.property('calledOnce', true);
});

it('checks for request object replay', async function () {
const request = await JWT.sign({
jti: randomBytes(16).toString('base64url'),
response_type: 'code',
client_id: clientId,
iss: clientId,
aud: this.provider.issuer,
}, this.key, 'HS256', {
expiresIn: 30,
});

await this.agent.post('/request')
.auth(clientId, 'secret')
.type('form')
.send({ request })
.expect(201)
.expect(({ body }) => {
expect(body).to.have.keys('expires_in', 'request_uri');
});

await this.agent.post('/request')
.auth(clientId, 'secret')
.type('form')
.send({ request })
.expect(400)
.expect(({ body }) => {
expect(body).to.have.property('error', 'invalid_request_object');
expect(body).to.have.property('error_description').and.matches(/^request object replay detected/);
});
});

it('defaults to MAX_TTL when no expires_in is present', async function () {
const spy = sinon.spy();
this.provider.once('pushed_authorization_request.success', spy);
Expand Down
51 changes: 0 additions & 51 deletions test/request/jwt_request.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,57 +395,6 @@ describe('request parameter features', () => {
}));
});

it('supports optional replay prevention', async function () {
const client = await this.provider.Client.find('client-with-HS-sig');
let [key] = client.symmetricKeyStore.selectForSign({ alg: 'HS256' });
key = await importJWK(key);

const request = await JWT.sign({
jti: randomBytes(16).toString('base64url'),
client_id: 'client-with-HS-sig',
response_type: 'code',
redirect_uri: 'https://client.example.com/cb',
scope: 'openid',
}, key, 'HS256', {
issuer: 'client-with-HS-sig', audience: this.provider.issuer, expiresIn: 30,
});

await this.wrap({
agent: this.agent,
route,
verb,
auth: {
request,
scope: 'openid',
client_id: 'client-with-HS-sig',
response_type: 'code',
},
})
.expect(successCode)
.expect(successFnCheck);

const spy = sinon.spy();
this.provider.once(errorEvt, spy);

await this.wrap({
agent: this.agent,
route,
verb,
auth: {
request,
scope: 'openid',
client_id: 'client-with-HS-sig',
response_type: 'code',
},
})
.expect(errorCode)
.expect(() => {
expect(spy.calledOnce).to.be.true;
expect(spy.args[0][1]).to.have.property('message', 'invalid_request_object');
expect(spy.args[0][1]).to.have.property('error_description').that.matches(/^request object replay detected/);
});
});

it('doesnt allow request inception', function () {
const spy = sinon.spy();
this.provider.once(errorEvt, spy);
Expand Down

0 comments on commit 18efa70

Please sign in to comment.