2021-12-07 13:18:08 -05:00

72 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Credit: https://github.com/rohe/pyoidc/blob/master/src/oic/utils/webfinger.py
// -- Normalization --
// A string of any other type is interpreted as a URI either the form of scheme
// "://" authority path-abempty [ "?" query ] [ "#" fragment ] or authority
// path-abempty [ "?" query ] [ "#" fragment ] per RFC 3986 [RFC3986] and is
// normalized according to the following rules:
//
// If the user input Identifier does not have an RFC 3986 [RFC3986] scheme
// portion, the string is interpreted as [userinfo "@"] host [":" port]
// path-abempty [ "?" query ] [ "#" fragment ] per RFC 3986 [RFC3986].
// If the userinfo component is present and all of the path component, query
// component, and port component are empty, the acct scheme is assumed. In this
// case, the normalized URI is formed by prefixing acct: to the string as the
// scheme. Per the 'acct' URI Scheme [ID.ietfappsawgaccturi], if there is an
// at-sign character ('@') in the userinfo component, it needs to be
// percent-encoded as described in RFC 3986 [RFC3986].
// For all other inputs without a scheme portion, the https scheme is assumed,
// and the normalized URI is formed by prefixing https:// to the string as the
// scheme.
// If the resulting URI contains a fragment portion, it MUST be stripped off
// together with the fragment delimiter character "#".
// The WebFinger [ID.ietfappsawgwebfinger] Resource in this case is the
// resulting URI, and the WebFinger Host is the authority component.
//
// Note: Since the definition of authority in RFC 3986 [RFC3986] is
// [ userinfo "@" ] host [ ":" port ], it is legal to have a user input
// identifier like userinfo@host:port, e.g., alice@example.com:8080.
const PORT = /^\d+$/;
function hasScheme(input) {
if (input.includes('://')) return true;
const authority = input.replace(/(\/|\?)/g, '#').split('#')[0];
if (authority.includes(':')) {
const index = authority.indexOf(':');
const hostOrPort = authority.slice(index + 1);
if (!PORT.test(hostOrPort)) {
return true;
}
}
return false;
}
function acctSchemeAssumed(input) {
if (!input.includes('@')) return false;
const parts = input.split('@');
const host = parts[parts.length - 1];
return !(host.includes(':') || host.includes('/') || host.includes('?'));
}
function normalize(input) {
if (typeof input !== 'string') {
throw new TypeError('input must be a string');
}
let output;
if (hasScheme(input)) {
output = input;
} else if (acctSchemeAssumed(input)) {
output = `acct:${input}`;
} else {
output = `https://${input}`;
}
return output.split('#')[0];
}
module.exports = normalize;