Skip to content

Commit

Permalink
feat: update Web Message Response Mode and remove its Relay Mode
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Jan 12, 2024
1 parent 8230b2c commit a91add8
Show file tree
Hide file tree
Showing 18 changed files with 60 additions and 350 deletions.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ _**default value**_:
<a id="clients-available-metadata"></a><details><summary>(Click to expand) Available Metadata</summary><br>


application_type, client_id, client_name, client_secret, client_uri, contacts, default_acr_values, default_max_age, grant_types, id_token_signed_response_alg, initiate_login_uri, jwks, jwks_uri, logo_uri, policy_uri, post_logout_redirect_uris, redirect_uris, require_auth_time, response_types, response_modes, scope, sector_identifier_uri, subject_type, token_endpoint_auth_method, tos_uri, userinfo_signed_response_alg <br/><br/>The following metadata is available but may not be recognized depending on your provider's configuration.<br/><br/> authorization_encrypted_response_alg, authorization_encrypted_response_enc, authorization_signed_response_alg, backchannel_logout_session_required, backchannel_logout_uri, id_token_encrypted_response_alg, id_token_encrypted_response_enc, introspection_encrypted_response_alg, introspection_encrypted_response_enc, introspection_signed_response_alg, request_object_encryption_alg, request_object_encryption_enc, request_object_signing_alg, request_uris, tls_client_auth_san_dns, tls_client_auth_san_email, tls_client_auth_san_ip, tls_client_auth_san_uri, tls_client_auth_subject_dn, tls_client_certificate_bound_access_tokens, token_endpoint_auth_signing_alg, userinfo_encrypted_response_alg, userinfo_encrypted_response_enc, web_message_uris
application_type, client_id, client_name, client_secret, client_uri, contacts, default_acr_values, default_max_age, grant_types, id_token_signed_response_alg, initiate_login_uri, jwks, jwks_uri, logo_uri, policy_uri, post_logout_redirect_uris, redirect_uris, require_auth_time, response_types, response_modes, scope, sector_identifier_uri, subject_type, token_endpoint_auth_method, tos_uri, userinfo_signed_response_alg <br/><br/>The following metadata is available but may not be recognized depending on your provider's configuration.<br/><br/> authorization_encrypted_response_alg, authorization_encrypted_response_enc, authorization_signed_response_alg, backchannel_logout_session_required, backchannel_logout_uri, id_token_encrypted_response_alg, id_token_encrypted_response_enc, introspection_encrypted_response_alg, introspection_encrypted_response_enc, introspection_signed_response_alg, request_object_encryption_alg, request_object_encryption_enc, request_object_signing_alg, request_uris, tls_client_auth_san_dns, tls_client_auth_san_email, tls_client_auth_san_ip, tls_client_auth_san_uri, tls_client_auth_subject_dn, tls_client_certificate_bound_access_tokens, token_endpoint_auth_signing_alg, userinfo_encrypted_response_alg, userinfo_encrypted_response_enc


</details>
Expand Down
20 changes: 0 additions & 20 deletions lib/actions/authorization/check_web_message_uri.js

This file was deleted.

6 changes: 1 addition & 5 deletions lib/actions/authorization/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import checkIdTokenHint from './check_id_token_hint.js';
import checkScope from './check_scope.js';
import checkResponseType from './check_response_type.js';
import checkRedirectUri from './check_redirect_uri.js';
import checkWebMessageUri from './check_web_message_uri.js';
import assignDefaults from './assign_defaults.js';
import checkClaims from './check_claims.js';
import assignClaims from './assign_claims.js';
Expand Down Expand Up @@ -79,8 +78,7 @@ export default function authorizationAction(provider, endpoint) {
const allowList = new Set(PARAM_LIST);

if (webMessageResponseMode.enabled) {
allowList.add('web_message_uri');
allowList.add('web_message_target');
allowList.add('web_message_uri'); // adding it just so that it can be rejected when detected
}

if (claimsParameter.enabled) {
Expand All @@ -96,7 +94,6 @@ export default function authorizationAction(provider, endpoint) {
extraParams.forEach(Set.prototype.add.bind(allowList));
if ([DA, CV, DR, BA].includes(endpoint)) {
allowList.delete('web_message_uri');
allowList.delete('web_message_target');
allowList.delete('response_type');
allowList.delete('response_mode');
allowList.delete('code_challenge_method');
Expand Down Expand Up @@ -168,7 +165,6 @@ export default function authorizationAction(provider, endpoint) {
use(() => checkScope.bind(undefined, allowList), A, DA, PAR, BA);
use(() => checkOpenidScope.bind(undefined, allowList), A, DA, PAR, BA);
use(() => checkRedirectUri, A, PAR );
use(() => checkWebMessageUri, A, PAR );
use(() => checkPKCE, A, PAR );
use(() => checkClaims, A, DA, PAR, BA);
use(() => checkMaxAge, A, DA, PAR, BA);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { InvalidRedirectUri, WebMessageUriMismatch } from '../../helpers/errors.js';
import { InvalidRedirectUri } from '../../helpers/errors.js';

/*
* Remaps the Pushed Authorization Request Endpoint errors thrown in downstream middlewares.
*/
export default async function requestObjectRemapErrors(ctx, next) {
return next().catch((err) => {
if (err instanceof InvalidRedirectUri || err instanceof WebMessageUriMismatch) {
if (err instanceof InvalidRedirectUri) {
Object.assign(err, {
message: 'invalid_request',
error: 'invalid_request',
Expand Down
12 changes: 9 additions & 3 deletions lib/actions/authorization/reject_unsupported.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { RequestNotSupported, RequestUriNotSupported } from '../../helpers/errors.js';
import { InvalidRequest, RequestNotSupported, RequestUriNotSupported } from '../../helpers/errors.js';
import instance from '../../helpers/weak_cache.js';

/*
* Rejects request and request_uri parameters when not supported.
* Rejects request and request_uri parameters when not supported. Also rejects wmrm's relay mode.
*/
export default function rejectUnsupported(ctx, next) {
const { requestObjects, pushedAuthorizationRequests } = instance(ctx.oidc.provider).configuration('features');
const { requestObjects, pushedAuthorizationRequests, webMessageResponseMode } = instance(ctx.oidc.provider).configuration('features');
const { params } = ctx.oidc;

if (params.request !== undefined && !requestObjects.request) {
Expand All @@ -19,5 +19,11 @@ export default function rejectUnsupported(ctx, next) {
throw new RequestUriNotSupported();
}

if (webMessageResponseMode.enabled && params.response_mode?.includes('web_message') && params.web_message_uri) {
const error = new InvalidRequest('Web Message Response Mode Relay Mode is not supported');
error.allow_redirect = false;
throw error;
}

return next();
}
3 changes: 0 additions & 3 deletions lib/consts/client_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ const DEFAULT = {
subject_type: 'public',
tls_client_certificate_bound_access_tokens: false,
token_endpoint_auth_method: 'client_secret_basic',
web_message_uris: [],
};

const REQUIRED = [
Expand Down Expand Up @@ -72,7 +71,6 @@ const ARYS = [
'request_uris',
'response_types',
'response_modes',
'web_message_uris',
];

const STRING = [
Expand Down Expand Up @@ -124,7 +122,6 @@ const STRING = [
'request_uris',
'response_types',
'response_modes',
'web_message_uris',
];

const WHEN = {
Expand Down
2 changes: 0 additions & 2 deletions lib/consts/param_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,4 @@ export default [
'scope',
'state',
'ui_locales',
// 'web_message_uri', // added conditionally depending on feature flag
// 'web_message_target', // added conditionally depending on feature flag
];
25 changes: 0 additions & 25 deletions lib/helpers/client_schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,6 @@ export default function getSchema(provider) {
}
}

if (features.webMessageResponseMode.enabled) {
RECOGNIZED_METADATA.push('web_message_uris');
}

if (features.mTLS.enabled && features.mTLS.certificateBoundAccessTokens) {
RECOGNIZED_METADATA.push('tls_client_certificate_bound_access_tokens');
}
Expand Down Expand Up @@ -228,7 +224,6 @@ export default function getSchema(provider) {
this.scopes();
this.postLogoutRedirectUris();
this.redirectUris();
this.webMessageUris();
this.checkContacts();
this.jarPolicy();
this.parPolicy();
Expand Down Expand Up @@ -502,26 +497,6 @@ export default function getSchema(provider) {
}
}

webMessageUris() {
if (!this.web_message_uris) return;
this.web_message_uris.forEach((uri) => {
let origin;
let protocol;

try {
({ origin, protocol } = new URL(uri));
} catch (err) {
this.invalidate('web_message_uris must only contain valid uris');
}
if (!['https:', 'http:'].includes(protocol)) {
this.invalidate('web_message_uris must only contain web uris');
}
if (origin !== uri) {
this.invalidate('web_message_uris must only contain origins');
}
});
}

redirectUris(uris = this.redirect_uris, label = 'redirect_uris') {
uris.forEach((redirectUri) => {
let hostname;
Expand Down
13 changes: 8 additions & 5 deletions lib/helpers/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ function makeDefaults() {
* tls_client_auth_san_dns, tls_client_auth_san_email, tls_client_auth_san_ip,
* tls_client_auth_san_uri, tls_client_auth_subject_dn,
* tls_client_certificate_bound_access_tokens, token_endpoint_auth_signing_alg,
* userinfo_encrypted_response_alg, userinfo_encrypted_response_enc, web_message_uris
* userinfo_encrypted_response_alg, userinfo_encrypted_response_enc
*
*/
clients: [],
Expand Down Expand Up @@ -1771,13 +1771,16 @@ function makeDefaults() {
/*
* features.webMessageResponseMode
*
* title: [draft-sakimura-oauth-wmrm-00](https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-00) - OAuth 2.0 Web Message Response Mode
* title: [draft-sakimura-oauth-wmrm-01](https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-01) - OAuth 2.0 Web Message Response Mode
*
* description: Enables `web_message` response mode.
* description: Enables `web_message` response mode. Only Simple Mode is supported. Requests containing
* the Relay Mode parameters will be rejected.
*
* recommendation: Although a general advise to use a `helmet` ([express](https://www.npmjs.com/package/helmet),
* recommendation: Although a general advise to use a `helmet` (e.g. for [express](https://www.npmjs.com/package/helmet),
* [koa](https://www.npmjs.com/package/koa-helmet)) it is especially advised for your interaction
* views routes if Web Message Response Mode is enabled in your deployment.
* views routes if Web Message Response Mode is enabled in your deployment. You will have to experiment
* with removal of the Cross-Origin-Embedder-Policy and Cross-Origin-Opener-Policy headers at various
* endpoints throughout the authorization request end-user journey to finalize this feature.
*
* recommendation: Updates to draft specification versions are released as MINOR library versions,
* if you utilize these specification implementations consider using the tilde `~` operator
Expand Down
11 changes: 0 additions & 11 deletions lib/helpers/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,6 @@ export class InvalidRedirectUri extends OIDCProviderError {
}
}

export class WebMessageUriMismatch extends OIDCProviderError {
error_description = 'web_message_uri did not match any client\'s registered web_message_uris';

allow_redirect = false;

constructor() {
super(400, 'web_message_uri_mismatch');
Error.captureStackTrace(this, this.constructor);
}
}

function E(message, errorDescription) {
const klassName = upperFirst(camelCase(message));
const klass = class extends OIDCProviderError {
Expand Down
6 changes: 3 additions & 3 deletions lib/helpers/features.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export const DRAFTS = new Map(Object.entries({
version: ['draft-09', 'draft-10'],
},
webMessageResponseMode: {
name: 'OAuth 2.0 Web Message Response Mode - draft 00',
name: 'OAuth 2.0 Web Message Response Mode - draft 01',
type: 'Individual draft',
url: 'https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-00',
version: [0, 'id-00', 'individual-draft-00'],
url: 'https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-01',
version: ['individual-draft-01'],
},
}));
4 changes: 0 additions & 4 deletions lib/models/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -530,10 +530,6 @@ export default function getClient(provider) {
});
}

webMessageUriAllowed(webMessageUri) {
return this.webMessageUris && this.webMessageUris.includes(webMessageUri);
}

requestUriAllowed(uri) {
const requested = stripFragment(uri);
return !!(this.requestUris || []).find((enabled) => requested === stripFragment(enabled));
Expand Down
21 changes: 1 addition & 20 deletions lib/response_modes/web_message.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ export default function webMessage(ctx, redirectUri, response) {
const data = jsesc({
response,
redirect_uri: redirectUri,
web_message_uri: ctx.oidc.params.web_message_uri,
web_message_target: ctx.oidc.params.web_message_target,
}, { json: true, isScriptContext: true });

ctx.body = `<!DOCTYPE html>
Expand All @@ -37,8 +35,6 @@ export default function webMessage(ctx, redirectUri, response) {
var response = data.response;
var redirect_uri = data.redirect_uri;
var web_message_uri = data.web_message_uri;
var web_message_target = data.web_message_target;
var authorization_response = { type: 'authorization_response', response: response };
Expand All @@ -49,22 +45,7 @@ export default function webMessage(ctx, redirectUri, response) {
};
var mainWin = win.opener || win.parent;
if (web_message_uri && web_message_target) {
var onRelayResponse = function(event) {
if (event.origin !== redirect_uri) return;
if (event.data.type === 'relay_response') {
win.removeEventListener('message', onRelayResponse);
messageTargetWindow = event.source.frames[web_message_target];
if (messageTargetWindow) {
respond(messageTargetWindow, web_message_uri);
}
}
}
win.addEventListener('message', onRelayResponse);
mainWin.postMessage({ type: 'relay_request' }, redirect_uri);
} else {
respond(mainWin, redirect_uri);
}
respond(mainWin, redirect_uri);
})(this, this.document);
`)}</script>
</body>
Expand Down
10 changes: 2 additions & 8 deletions lib/shared/authorization_error_handler.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Debug from 'debug';

import { InvalidRedirectUri, WebMessageUriMismatch } from '../helpers/errors.js';
import { InvalidRedirectUri } from '../helpers/errors.js';
import instance from '../helpers/weak_cache.js';
import errOut from '../helpers/err_out.js';
import resolveResponseMode from '../helpers/resolve_response_mode.js';
Expand All @@ -18,12 +18,6 @@ export default (provider) => {
check: 'redirectUriCheckPerformed',
recovery: oneRedirectUriClients,
},
web_message_uri: {
Err: WebMessageUriMismatch,
method: 'webMessageUriAllowed',
check: 'webMessageUriCheckPerformed',
flag: 'features.webMessageResponseMode.enabled',
},
});

function getOutAndEmit(ctx, err, state) {
Expand Down Expand Up @@ -87,7 +81,7 @@ export default (provider) => {

const out = getOutAndEmit(ctx, err, safe(params.state));

// in case redirect_uri, client or web_message_uri could not be verified no successful
// in case redirect_uri or client could not be verified no successful
// response should happen, render instead
if (
!safe(params.client_id)
Expand Down
56 changes: 0 additions & 56 deletions test/configuration/client_metadata.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -638,62 +638,6 @@ describe('Client metadata validation', () => {
);
});

context('web_message_uris', function () {
const configuration = { features: { webMessageResponseMode: { enabled: true } } };
defaultsTo(this.title, [], undefined, configuration);
mustBeArray(this.title, undefined, configuration);

allows(this.title, [], undefined, configuration);
allows(this.title, ['https://example.com'], undefined, configuration);
allows(this.title, ['https://example.com:3000'], undefined, configuration);
rejects(this.title, [123], /must only contain strings$/, undefined, configuration);
rejects(this.title, [true], /must only contain strings$/, undefined, configuration);
rejects(this.title, [null], /must only contain strings$/, undefined, configuration);
rejects(this.title, ['not a uri'], /must only contain valid uris$/, undefined, configuration);
rejects(
this.title,
['custom-scheme://not-a-web-uri'],
/must only contain web uris$/,
undefined,
configuration,
);
rejects(
this.title,
['https://example.com/'],
/must only contain origins$/,
undefined,
configuration,
);
rejects(
this.title,
['https://example.com?'],
/must only contain origins$/,
undefined,
configuration,
);
rejects(
this.title,
['https://example.com#'],
/must only contain origins$/,
undefined,
configuration,
);
rejects(
this.title,
['https://foo:[email protected]'],
/must only contain origins$/,
undefined,
configuration,
);
rejects(
this.title,
['https://[email protected]'],
/must only contain origins$/,
undefined,
configuration,
);
});

context('require_auth_time', function () {
defaultsTo(this.title, false);
mustBeBoolean(this.title);
Expand Down
Loading

0 comments on commit a91add8

Please sign in to comment.