diff --git a/docs/README.md b/docs/README.md index 4925033e3..c04fe2c95 100644 --- a/docs/README.md +++ b/docs/README.md @@ -927,8 +927,8 @@ _**default value**_: [Financial-grade API - Part 2: Read and Write API Security Profile](https://openid.net/specs/openid-financial-api-part-2-ID2.html) Enables extra behaviours defined in FAPI Part 1 & 2 that cannot be achieved by other configuration options, namely: - - request object "exp" claim is required - - request object must contain all parameters that are also sent as regular parameters + - Request Object "exp" claim is required + - Request Object must contain all parameters that are also sent as regular parameters - userinfo endpoint becomes a FAPI resource, echoing back the x-fapi-interaction-id header and disabling query string as a mechanism for providing access tokens @@ -1772,7 +1772,7 @@ To change the default client response_types configure `clientDefaults` to be an ### clockTolerance -A `Number` value (in seconds) describing the allowed system clock skew for validating client-provided JWTs, e.g. Request objects, DPoP Proofs and otherwise comparing timestamps +A `Number` value (in seconds) describing the allowed system clock skew for validating client-provided JWTs, e.g. Request Objects, DPoP Proofs and otherwise comparing timestamps _**recommendation**_: Only set this to a reasonable value when needed to cover server-side client and oidc-provider server clock skew. More than 5 minutes (if needed) is probably a sign something else is wrong. diff --git a/lib/actions/authorization/check_client.js b/lib/actions/authorization/check_client.js index 54d7a2b49..da592acdf 100644 --- a/lib/actions/authorization/check_client.js +++ b/lib/actions/authorization/check_client.js @@ -1,5 +1,8 @@ -const { InvalidClient } = require('../../helpers/errors'); +const { strict: assert } = require('assert'); + +const { InvalidClient, InvalidRequestObject } = require('../../helpers/errors'); const presence = require('../../helpers/validate_presence'); +const base64url = require('../../helpers/base64url'); /* * Checks client_id @@ -10,7 +13,43 @@ const presence = require('../../helpers/validate_presence'); * @throws: invalid_client */ module.exports = async function checkClient(ctx, next) { - presence(ctx, 'client_id'); + const { oidc: { params } } = ctx; + + try { + presence(ctx, 'client_id'); + } catch (err) { + const { request } = params; + if (request === undefined) { + throw err; + } + + const parts = request.split('.'); + let decoded; + let clientId; + + try { + assert(parts.length === 3 || parts.length === 5); + parts.forEach((part, i, { length }) => { + if (length === 3 && i === 1) { // JWT Payload + decoded = JSON.parse(base64url.decodeToBuffer(part)); + } else if (length === 5 && i === 0) { // JWE Header + decoded = JSON.parse(base64url.decodeToBuffer(part)); + } + }); + } catch (error) { + throw new InvalidRequestObject(`Request Object is not a valid ${parts.length === 5 ? 'JWE' : 'JWT'}`); + } + + if (decoded) { + clientId = decoded.client_id || decoded.iss; + } + + if (typeof clientId !== 'string' || !clientId) { + throw err; + } + + params.client_id = clientId; + } const client = await ctx.oidc.provider.Client.find(ctx.oidc.params.client_id); diff --git a/lib/actions/authorization/fetch_request_uri.js b/lib/actions/authorization/fetch_request_uri.js index fa3042bc7..c44f977df 100644 --- a/lib/actions/authorization/fetch_request_uri.js +++ b/lib/actions/authorization/fetch_request_uri.js @@ -1,9 +1,7 @@ const { URL } = require('url'); const { strict: assert } = require('assert'); -const { - InvalidRequest, InvalidRequestUri, RequestNotSupported, RequestUriNotSupported, -} = require('../../helpers/errors'); +const { InvalidRequest, InvalidRequestUri } = require('../../helpers/errors'); const instance = require('../../helpers/weak_cache'); const allowedSchemes = new Set(['http:', 'https:', 'urn:']); @@ -21,17 +19,8 @@ const allowedSchemes = new Set(['http:', 'https:', 'urn:']); * @throws: request_uri_not_supported */ module.exports = async function fetchRequestUri(ctx, next) { - const { request, requestUri } = instance(ctx.oidc.provider).configuration('features'); const { params } = ctx.oidc; - if (!request.enabled && params.request !== undefined) { - throw new RequestNotSupported(); - } - - if (!requestUri.enabled && params.request_uri !== undefined) { - throw new RequestUriNotSupported(); - } - if (params.request !== undefined && params.request_uri !== undefined) { throw new InvalidRequest('request and request_uri parameters MUST NOT be used together'); } diff --git a/lib/actions/authorization/index.js b/lib/actions/authorization/index.js index a34035f27..de55be37f 100644 --- a/lib/actions/authorization/index.js +++ b/lib/actions/authorization/index.js @@ -10,6 +10,7 @@ const getTokenAuth = require('../../shared/token_auth'); const checkClient = require('./check_client'); const checkResponseMode = require('./check_response_mode'); +const rejectUnsupported = require('./reject_unsupported'); const rejectRegistration = require('./reject_registration'); const oauthRequired = require('./oauth_required'); const oneRedirectUriClients = require('./one_redirect_uri_clients'); @@ -50,7 +51,6 @@ const DR = 'device_resume'; const ALL = [A, DA, R, CV, DR]; const parseBody = bodyParser.bind(_, 'application/x-www-form-urlencoded'); -const rejectDupeClientId = rejectDupes.bind(_, { only: new Set(['client_id']) }); module.exports = function authorizationAction(provider, endpoint) { const { @@ -115,15 +115,17 @@ module.exports = function authorizationAction(provider, endpoint) { } use(() => deviceAuthorizationClientId, DA ); use(() => paramsMiddleware.bind(_, whitelist), A, DA ); - use(() => rejectDupeClientId, ...ALL); - use(() => checkClient, ...ALL); - use(() => oneRedirectUriClients, A ); use(() => rejectDupesMiddleware, A, DA ); + use(() => rejectUnsupported, A, DA ); + use(() => checkClient, ...ALL); use(() => checkClientGrantType, DA ); use(() => checkResponseMode, A ); - use(() => oauthRequired, A ); use(() => fetchRequestUri, A, DA ); - use(() => processRequestObject.bind(_, whitelist), A, DA ); + use(() => processRequestObject.bind( + _, whitelist, rejectDupesMiddleware, + ), A, DA ); + use(() => oneRedirectUriClients, A ); + use(() => oauthRequired, A ); use(() => rejectRegistration, A, DA ); use(() => oidcRequired, A ); use(() => assignDefaults, A, DA ); diff --git a/lib/actions/authorization/process_request_object.js b/lib/actions/authorization/process_request_object.js index 607ad3d7e..72eb08e4a 100644 --- a/lib/actions/authorization/process_request_object.js +++ b/lib/actions/authorization/process_request_object.js @@ -12,14 +12,14 @@ const checkResponseMode = require('./check_response_mode'); * * @throws: invalid_request_object */ -module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { +module.exports = async function processRequestObject(PARAM_LIST, rejectDupesMiddleware, ctx, next) { const { params, client } = ctx.oidc; if ( client.requestObjectSigningAlg && params.request === undefined ) { - throw new InvalidRequest('request object must be used by this client'); + throw new InvalidRequest('Request Object must be used by this client'); } if (params.request === undefined) { @@ -59,7 +59,7 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { try { decoded = JWT.decode(params.request); } catch (err) { - throw new InvalidRequestObject(`could not parse request object (${err.message})`); + throw new InvalidRequestObject(`could not parse Request Object (${err.message})`); } const { payload, header: { alg } } = decoded; @@ -68,7 +68,7 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { if (PARAM_LIST.has(key)) { if (key === 'claims' && isPlainObject(value)) { acc[key] = JSON.stringify(value); - } else if (key === 'resource' && Array.isArray(value) && conf('features.resourceIndicators.enabled')) { + } else if (Array.isArray(value)) { acc[key] = value; } else if (typeof value !== 'string') { acc[key] = String(value); @@ -80,6 +80,8 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { return acc; }, {}); + rejectDupesMiddleware({ oidc: { params: request } }, () => {}); + if (request.state !== undefined) { params.state = request.state; } @@ -90,14 +92,22 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { } if (request.request !== undefined || request.request_uri !== undefined) { - throw new InvalidRequestObject('request object must not contain request or request_uri properties'); + throw new InvalidRequestObject('Request Object must not contain request or request_uri properties'); } - if (request.response_type !== undefined && request.response_type !== params.response_type) { + if ( + params.response_type + && request.response_type !== undefined + && request.response_type !== params.response_type + ) { throw new InvalidRequestObject('request response_type must equal the one in request parameters'); } - if (request.client_id !== undefined && request.client_id !== params.client_id) { + if ( + params.client_id + && request.client_id !== undefined + && request.client_id !== params.client_id + ) { throw new InvalidRequestObject('request client_id must equal the one in request parameters'); } @@ -118,7 +128,7 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { if (conf('features.fapiRW.enabled')) { if (!('exp' in payload)) { - throw new InvalidRequestObject('request object is missing the "exp" claim'); + throw new InvalidRequestObject('Request Object is missing the "exp" claim'); } const missing = Object.entries(ctx.oidc.params) @@ -137,7 +147,7 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { }).filter(Boolean); if (missing.length) { - throw new InvalidRequestObject(`all parameters shall be present inside the signed request object (missing: ${missing.join(', ')})`); + throw new InvalidRequestObject(`all parameters shall be present inside the signed Request Object (missing: ${missing.join(', ')})`); } } @@ -152,7 +162,7 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { await JWT.verify(params.request, client.keystore, opts); trusted = true; } catch (err) { - throw new InvalidRequestObject(`could not validate request object (${err.message})`); + throw new InvalidRequestObject(`could not validate Request Object (${err.message})`); } if (payload.jti && payload.exp && payload.iss) { @@ -169,7 +179,7 @@ module.exports = async function processRequestObject(PARAM_LIST, ctx, next) { if (trusted) { ctx.oidc.signed = Object.keys(request); } else if (ctx.oidc.insecureRequestUri) { - throw new InvalidRequestObject('request object from unsecure request_uri must be signed and/or symmetrically encrypted'); + throw new InvalidRequestObject('Request Object from unsecure request_uri must be signed and/or symmetrically encrypted'); } Object.assign(params, request); diff --git a/lib/actions/authorization/reject_unsupported.js b/lib/actions/authorization/reject_unsupported.js new file mode 100644 index 000000000..99cfa6e7e --- /dev/null +++ b/lib/actions/authorization/reject_unsupported.js @@ -0,0 +1,22 @@ +const { RequestNotSupported, RequestUriNotSupported } = require('../../helpers/errors'); +const instance = require('../../helpers/weak_cache'); + +/* + * Rejects registration parameter as not supported. + * + * @throws: registration_not_supported + */ +module.exports = function rejectUnsupported(ctx, next) { + const { request, requestUri } = instance(ctx.oidc.provider).configuration('features'); + const { params } = ctx.oidc; + + if (!request.enabled && params.request !== undefined) { + throw new RequestNotSupported(); + } + + if (!requestUri.enabled && params.request_uri !== undefined) { + throw new RequestUriNotSupported(); + } + + return next(); +}; diff --git a/lib/helpers/defaults.js b/lib/helpers/defaults.js index dea88188a..7bbf9f7ed 100644 --- a/lib/helpers/defaults.js +++ b/lib/helpers/defaults.js @@ -176,7 +176,7 @@ const DEFAULTS = { * clockTolerance * * description: A `Number` value (in seconds) describing the allowed system clock skew for - * validating client-provided JWTs, e.g. request objects, DPoP Proofs and otherwise comparing + * validating client-provided JWTs, e.g. Request Objects, DPoP Proofs and otherwise comparing * timestamps * recommendation: Only set this to a reasonable value when needed to cover server-side client and * oidc-provider server clock skew. More than 5 minutes (if needed) is probably a sign something @@ -727,8 +727,8 @@ const DEFAULTS = { * description: Enables extra behaviours defined in FAPI Part 1 & 2 that cannot be achieved by * other configuration options, namely: * - * - request object "exp" claim is required - * - request object must contain all parameters that are also sent as regular parameters + * - Request Object "exp" claim is required + * - Request Object must contain all parameters that are also sent as regular parameters * - userinfo endpoint becomes a FAPI resource, echoing back the x-fapi-interaction-id header * and disabling query string as a mechanism for providing access tokens */ diff --git a/lib/shared/authorization_error_handler.js b/lib/shared/authorization_error_handler.js index 734487566..f24453271 100644 --- a/lib/shared/authorization_error_handler.js +++ b/lib/shared/authorization_error_handler.js @@ -5,6 +5,7 @@ const instance = require('../helpers/weak_cache'); const errOut = require('../helpers/err_out'); const processSessionState = require('../helpers/process_session_state'); const resolveResponseMode = require('../helpers/resolve_response_mode'); +const oneRedirectUriClients = require('../actions/authorization/one_redirect_uri_clients'); const debug = new Debug('oidc-provider:authentication:error'); const serverError = new Debug('oidc-provider:server_error'); @@ -17,6 +18,7 @@ module.exports = (provider) => { Err: RedirectUriMismatch, method: 'redirectUriAllowed', check: 'redirectUriCheckPerformed', + recovery: oneRedirectUriClients, }, web_message_uri: { Err: WebMessageUriMismatch, @@ -65,14 +67,18 @@ module.exports = (provider) => { } for (const [param, { // eslint-disable-line no-restricted-syntax - Err, check, flag, method, + Err, check, flag, method, recovery, }] of AD_ACTA_CHECKS) { if ( (!flag || instance(provider).configuration(flag)) && !(err instanceof Err) && oidc.client - && safe(params[param]) && !oidc[check] + && !oidc[check] ) { - if (!oidc.client[method](safe(params[param]))) { + if (recovery && !safe(params[param])) { + recovery(ctx, () => {}); + } + + if (safe(params[param]) && !oidc.client[method](params[param])) { getOutAndEmit(ctx, caught, safe(params.state)); err = new Err(); ctx.status = err.statusCode; diff --git a/lib/shared/reject_dupes.js b/lib/shared/reject_dupes.js index 9dd9d9a3d..2d7fe6c33 100644 --- a/lib/shared/reject_dupes.js +++ b/lib/shared/reject_dupes.js @@ -18,7 +18,7 @@ function defaultMap([key, value]) { return Array.isArray(value) ? key : undefined; } -async function rejectDupes({ except, only }, ctx, next) { +module.exports = function rejectDupes({ except, only } = {}, ctx, next) { let mapFn; if (except) { @@ -39,7 +39,5 @@ async function rejectDupes({ except, only }, ctx, next) { throw new InvalidRequest(`parameters must not be provided twice. (${params.join(',')})`); } - await next(); -} - -module.exports = rejectDupes; + return next(); +}; diff --git a/test/encryption/encryption.test.js b/test/encryption/encryption.test.js index 88d432330..df171ab35 100644 --- a/test/encryption/encryption.test.js +++ b/test/encryption/encryption.test.js @@ -3,6 +3,7 @@ const url = require('url'); const { expect } = require('chai'); const base64url = require('base64url'); +const sinon = require('sinon'); const jose = require('@panva/jose'); const bootstrap = require('../test_helper'); @@ -121,7 +122,7 @@ describe('encryption', () => { }); }); - describe('authorization request object encryption', () => { + describe('authorization Request Object encryption', () => { it('works with signed by none', function () { return JWT.sign({ client_id: 'client', @@ -148,6 +149,62 @@ describe('encryption', () => { })); }); + describe('JAR only request', () => { + it('works without any other params if client_id is replicated in the header', function () { + return JWT.sign({ + client_id: 'client', + response_type: 'code', + redirect_uri: 'https://client.example.com/cb', + scope: 'openid', + }, null, 'none', { issuer: 'client', audience: this.provider.issuer }).then((signed) => jose.JWE.encrypt(signed, i(this.provider).keystore.get({ kty: 'RSA' }), { enc: 'A128CBC-HS256', alg: 'RSA1_5', client_id: 'client' })).then((encrypted) => this.wrap({ + route, + verb, + auth: { + request: encrypted, + }, + }) + .expect(302) + .expect((response) => { + const expected = url.parse('https://client.example.com/cb', true); + const actual = url.parse(response.headers.location, true); + ['protocol', 'host', 'pathname'].forEach((attr) => { + expect(actual[attr]).to.equal(expected[attr]); + }); + expect(actual.query).to.have.property('code'); + })); + }); + + it('handles invalid JWE', function () { + const spy = sinon.spy(); + this.provider.once('authorization.error', spy); + + return JWT.sign({ + client_id: 'client', + response_type: 'code', + redirect_uri: 'https://client.example.com/cb', + scope: 'openid', + }, null, 'none', { issuer: 'client', audience: this.provider.issuer }).then((signed) => jose.JWE.encrypt(signed, i(this.provider).keystore.get({ kty: 'RSA' }), { enc: 'A128CBC-HS256', alg: 'RSA1_5', client_id: 'client' })).then((encrypted) => this.wrap({ + route, + verb, + auth: { + request: encrypted.split('.').map((part, i) => { + if (i === 0) { + return 'foo'; + } + + return part; + }).join('.'), + }, + }) + .expect(400) + .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', 'Request Object is not a valid JWE'); + })); + }); + }); + it('handles enc unsupported algs', function () { return JWT.sign({ client_id: 'client', @@ -231,7 +288,7 @@ describe('encryption', () => { }); }); - it('accepts symmetric encrypted request objects', async function () { + it('accepts symmetric encrypted Request Objects', async function () { const client = await this.provider.Client.find('clientSymmetric'); return JWT.sign({ client_id: 'clientSymmetric', @@ -282,7 +339,7 @@ describe('encryption', () => { }); }); - it('accepts symmetric (dir) encrypted request objects', async function () { + it('accepts symmetric (dir) encrypted Request Objects', async function () { const client = await this.provider.Client.find('clientSymmetric'); return JWT.sign({ client_id: 'clientSymmetric-dir', diff --git a/test/fapirw/fapirw.test.js b/test/fapirw/fapirw.test.js index 2182a3ef4..dc575ea53 100644 --- a/test/fapirw/fapirw.test.js +++ b/test/fapirw/fapirw.test.js @@ -68,7 +68,7 @@ describe('Financial-grade API - Part 2: Read and Write API Security Profile beha .expect(auth.validateClientLocation); }); - it('requires exp to be provided in the request object', function () { + it('requires exp to be provided in the Request Object', function () { const request = `${base64url.encode(JSON.stringify({ alg: 'none' }))}.${base64url.encode(JSON.stringify({ client_id: 'client', scope: 'openid', @@ -96,10 +96,10 @@ describe('Financial-grade API - Part 2: Read and Write API Security Profile beha .expect(auth.validateState) .expect(auth.validateClientLocation) .expect(auth.validateError('invalid_request_object')) - .expect(auth.validateErrorDescription('request object is missing the "exp" claim')); + .expect(auth.validateErrorDescription('Request Object is missing the "exp" claim')); }); - it('requires all parameters to be present inside the signed request object', function () { + it('requires all parameters to be present inside the signed Request Object', function () { const request = `${base64url.encode(JSON.stringify({ alg: 'none' }))}.${base64url.encode(JSON.stringify({ client_id: 'client', scope: 'openid', @@ -127,7 +127,7 @@ describe('Financial-grade API - Part 2: Read and Write API Security Profile beha .expect(auth.validateState) .expect(auth.validateClientLocation) .expect(auth.validateError('invalid_request_object')) - .expect(auth.validateErrorDescription('all parameters shall be present inside the signed request object (missing: nonce, redirect_uri, state)')); + .expect(auth.validateErrorDescription('all parameters shall be present inside the signed Request Object (missing: nonce, redirect_uri, state)')); }); }); }); diff --git a/test/request/jwt_request.test.js b/test/request/jwt_request.test.js index ef84dff22..874798006 100644 --- a/test/request/jwt_request.test.js +++ b/test/request/jwt_request.test.js @@ -70,6 +70,139 @@ describe('request parameter features', () => { .expect(successFnCheck)); }); + describe('JAR only request', () => { + it('works without any other params', function () { + return JWT.sign({ + client_id: 'client', + response_type: 'code', + redirect_uri: 'https://client.example.com/cb', + scope: 'openid', + }, null, 'none', { issuer: 'client', audience: this.provider.issuer }).then((request) => this.wrap({ + agent: this.agent, + route, + verb, + auth: { + request, + ...(successCode === 200 ? { + client_id: 'client', + } : undefined), + }, + }) + .expect(successCode) + .expect(successFnCheck)); + }); + + it('when invalid Request Object', function () { + const spy = sinon.spy(); + this.provider.once(errorEvt, spy); + + return this.wrap({ + agent: this.agent, + route, + verb, + auth: { + request: 'foo', + ...(successCode === 200 ? { + client_id: 'client', + } : undefined), + }, + }) + .expect(400) + .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', + route !== '/device/auth' ? 'Request Object is not a valid JWT' : 'could not parse Request Object (invalid JWT.decode input)', + ); + }); + }); + + it('if without client_id', function () { + const spy = sinon.spy(); + this.provider.once(errorEvt, spy); + + return JWT.sign({ + // client_id: 'client', + response_type: 'code', + redirect_uri: 'https://client.example.com/cb', + scope: 'openid', + }, null, 'none', { /* issuer: 'client', */ audience: this.provider.issuer }).then((request) => this.wrap({ + agent: this.agent, + route, + verb, + auth: { + request, + }, + }) + .expect(400) + .expect(() => { + expect(spy.calledOnce).to.be.true; + expect(spy.args[0][1]).to.have.property('message', 'invalid_request'); + expect(spy.args[0][1]).to.have.property( + 'error_description', + route !== '/device/auth' ? 'missing required parameter(s) (client_id)' : 'no client authentication mechanism provided', + ); + })); + }); + + it('if with empty client_id', function () { + const spy = sinon.spy(); + this.provider.once(errorEvt, spy); + + return JWT.sign({ + client_id: '', + response_type: 'code', + redirect_uri: 'https://client.example.com/cb', + scope: 'openid', + }, null, 'none', { issuer: '', audience: this.provider.issuer }).then((request) => this.wrap({ + agent: this.agent, + route, + verb, + auth: { + request, + }, + }) + .expect(400) + .expect(() => { + expect(spy.calledOnce).to.be.true; + expect(spy.args[0][1]).to.have.property('message', 'invalid_request'); + expect(spy.args[0][1]).to.have.property( + 'error_description', + route !== '/device/auth' ? 'missing required parameter(s) (client_id)' : 'no client authentication mechanism provided', + ); + })); + }); + + it('if with invalid type client_id', function () { + const spy = sinon.spy(); + this.provider.once(errorEvt, spy); + + return JWT.sign({ + client_id: 123678, + response_type: 'code', + redirect_uri: 'https://client.example.com/cb', + scope: 'openid', + }, null, 'none', { issuer: 'client', audience: this.provider.issuer }).then((request) => this.wrap({ + agent: this.agent, + route, + verb, + auth: { + request, + }, + }) + .expect(400) + .expect(() => { + expect(spy.calledOnce).to.be.true; + expect(spy.args[0][1]).to.have.property('message', 'invalid_request'); + expect(spy.args[0][1]).to.have.property( + 'error_description', + route !== '/device/auth' ? 'missing required parameter(s) (client_id)' : 'no client authentication mechanism provided', + ); + })); + }); + }); + it('fails to process a request without request', function () { const spy = sinon.spy(); this.provider.once(errorEvt, spy); @@ -91,7 +224,7 @@ describe('request parameter features', () => { expect(spy.args[0][1]).to.have.property('message', 'invalid_request'); expect(spy.args[0][1]).to.have.property( 'error_description', - 'request object must be used by this client', + 'Request Object must be used by this client', ); }); }); @@ -182,7 +315,7 @@ describe('request parameter features', () => { ).to.be.true; }); - it('can accept request objects issued within acceptable system clock skew', async function () { + it('can accept Request Objects issued within acceptable system clock skew', async function () { const key = (await this.provider.Client.find('client-with-HS-sig')).keystore.get({ alg: 'HS256', }); @@ -303,7 +436,7 @@ describe('request parameter features', () => { expect(spy.args[0][1]).to.have.property('message', 'invalid_request_object'); expect(spy.args[0][1]).to.have.property( 'error_description', - 'request object must not contain request or request_uri properties', + 'Request Object must not contain request or request_uri properties', ); })); }); @@ -334,7 +467,7 @@ describe('request parameter features', () => { expect(spy.args[0][1]).to.have.property('message', 'invalid_request_object'); expect(spy.args[0][1]).to.have.property( 'error_description', - 'request object must not contain request or request_uri properties', + 'Request Object must not contain request or request_uri properties', ); })); }); @@ -564,7 +697,7 @@ describe('request parameter features', () => { .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').and.matches(/could not parse request object/); + expect(spy.args[0][1]).to.have.property('error_description').and.matches(/could not parse Request Object/); }); }); @@ -653,11 +786,11 @@ describe('request parameter features', () => { .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(/could not validate request object/); + expect(spy.args[0][1]).to.have.property('error_description').that.matches(/could not validate Request Object/); })); }); - it('rejects "registration" parameter part of the request object', function () { + it('rejects "registration" parameter part of the Request Object', function () { const spy = sinon.spy(); this.provider.once(errorEvt, spy); diff --git a/test/request/uri_request.test.js b/test/request/uri_request.test.js index bbd638814..6c253ecf2 100644 --- a/test/request/uri_request.test.js +++ b/test/request/uri_request.test.js @@ -174,7 +174,7 @@ describe('request Uri features', () => { expect(spy.args[0][1]).to.have.property('message', 'invalid_request_object'); expect(spy.args[0][1]).to.have.property( 'error_description', - 'request object from unsecure request_uri must be signed and/or symmetrically encrypted', + 'Request Object from unsecure request_uri must be signed and/or symmetrically encrypted', ); }); }); @@ -513,7 +513,7 @@ describe('request Uri features', () => { expect(spy.args[0][1]).to.have.property('message', 'invalid_request_object'); expect(spy.args[0][1]).to.have.property( 'error_description', - 'request object must not contain request or request_uri properties', + 'Request Object must not contain request or request_uri properties', ); }); }); @@ -550,7 +550,7 @@ describe('request Uri features', () => { expect(spy.args[0][1]).to.have.property('message', 'invalid_request_object'); expect(spy.args[0][1]).to.have.property( 'error_description', - 'request object must not contain request or request_uri properties', + 'Request Object must not contain request or request_uri properties', ); }); }); @@ -614,7 +614,7 @@ describe('request Uri features', () => { .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').and.matches(/could not parse request object/); + expect(spy.args[0][1]).to.have.property('error_description').and.matches(/could not parse Request Object/); }); }); @@ -719,7 +719,7 @@ describe('request Uri features', () => { .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(/could not validate request object/); + expect(spy.args[0][1]).to.have.property('error_description').that.matches(/could not validate Request Object/); }); }); });