123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- /*
- This script is meant to to be used in an `iframe` with the sole purpose of doing webfinger queries
- client-side without being restricted by a strict `connect-src` Content-Security-Policy directive.
- It communicates with the parent window through message events that are authenticated by origin,
- and performs no other task.
- */
- import './public-path';
- import axios from 'axios';
- interface JRDLink {
- rel: string;
- template?: string;
- href?: string;
- }
- const isJRDLink = (link: unknown): link is JRDLink =>
- typeof link === 'object' &&
- link !== null &&
- 'rel' in link &&
- typeof link.rel === 'string' &&
- (!('template' in link) || typeof link.template === 'string') &&
- (!('href' in link) || typeof link.href === 'string');
- const findLink = (rel: string, data: unknown): JRDLink | undefined => {
- if (
- typeof data === 'object' &&
- data !== null &&
- 'links' in data &&
- data.links instanceof Array
- ) {
- return data.links.find(
- (link): link is JRDLink => isJRDLink(link) && link.rel === rel,
- );
- } else {
- return undefined;
- }
- };
- const findTemplateLink = (data: unknown) =>
- findLink('http://ostatus.org/schema/1.0/subscribe', data)?.template;
- const fetchInteractionURLSuccess = (
- uri_or_domain: string,
- template: string,
- ) => {
- window.parent.postMessage(
- {
- type: 'fetchInteractionURL-success',
- uri_or_domain,
- template,
- },
- window.origin,
- );
- };
- const fetchInteractionURLFailure = () => {
- window.parent.postMessage(
- {
- type: 'fetchInteractionURL-failure',
- },
- window.origin,
- );
- };
- const isValidDomain = (value: string) => {
- const url = new URL('https:///path');
- url.hostname = value;
- return url.hostname === value;
- };
- // Attempt to find a remote interaction URL from a domain
- const fromDomain = (domain: string) => {
- const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
- axios
- .get(`https://${domain}/.well-known/webfinger`, {
- params: { resource: `https://${domain}` },
- })
- .then(({ data }) => {
- const template = findTemplateLink(data);
- fetchInteractionURLSuccess(domain, template ?? fallbackTemplate);
- return;
- })
- .catch(() => {
- fetchInteractionURLSuccess(domain, fallbackTemplate);
- });
- };
- // Attempt to find a remote interaction URL from an arbitrary URL
- const fromURL = (url: string) => {
- const domain = new URL(url).host;
- const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
- axios
- .get(`https://${domain}/.well-known/webfinger`, {
- params: { resource: url },
- })
- .then(({ data }) => {
- const template = findTemplateLink(data);
- fetchInteractionURLSuccess(url, template ?? fallbackTemplate);
- return;
- })
- .catch(() => {
- fromDomain(domain);
- });
- };
- // Attempt to find a remote interaction URL from a `user@domain` string
- const fromAcct = (acct: string) => {
- acct = acct.replace(/^@/, '');
- const segments = acct.split('@');
- if (segments.length !== 2 || !segments[0] || !isValidDomain(segments[1])) {
- fetchInteractionURLFailure();
- return;
- }
- const domain = segments[1];
- const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
- axios
- .get(`https://${domain}/.well-known/webfinger`, {
- params: { resource: `acct:${acct}` },
- })
- .then(({ data }) => {
- const template = findTemplateLink(data);
- fetchInteractionURLSuccess(acct, template ?? fallbackTemplate);
- return;
- })
- .catch(() => {
- // TODO: handle host-meta?
- fromDomain(domain);
- });
- };
- const fetchInteractionURL = (uri_or_domain: string) => {
- if (uri_or_domain === '') {
- fetchInteractionURLFailure();
- } else if (/^https?:\/\//.test(uri_or_domain)) {
- fromURL(uri_or_domain);
- } else if (uri_or_domain.includes('@')) {
- fromAcct(uri_or_domain);
- } else {
- fromDomain(uri_or_domain);
- }
- };
- window.addEventListener('message', (event: MessageEvent<unknown>) => {
- // Check message origin
- if (
- !window.origin ||
- window.parent !== event.source ||
- event.origin !== window.origin
- ) {
- return;
- }
- if (
- event.data &&
- typeof event.data === 'object' &&
- 'type' in event.data &&
- event.data.type === 'fetchInteractionURL' &&
- 'uri_or_domain' in event.data &&
- typeof event.data.uri_or_domain === 'string'
- ) {
- fetchInteractionURL(event.data.uri_or_domain);
- }
- });
|