diff --git a/.changeset/modern-snails-judge.md b/.changeset/modern-snails-judge.md new file mode 100644 index 00000000..0992746d --- /dev/null +++ b/.changeset/modern-snails-judge.md @@ -0,0 +1,6 @@ +--- +'@apollo/explorer': minor +'@apollo/sandbox': minor +--- + +Support Explorer preflight OAuth in embeds diff --git a/packages/explorer/src/helpers/constants.ts b/packages/explorer/src/helpers/constants.ts index a4d25c46..424cc075 100644 --- a/packages/explorer/src/helpers/constants.ts +++ b/packages/explorer/src/helpers/constants.ts @@ -33,5 +33,9 @@ export const EXPLORER_LISTENING_FOR_PARTIAL_TOKEN = 'ExplorerListeningForPartialToken'; export const PARTIAL_AUTHENTICATION_TOKEN_RESPONSE = 'PartialAuthenticationTokenResponse'; +export const PREFLIGHT_OAUTH_REQUEST = 'PreflightOAuthRequest'; +export const PREFLIGHT_OAUTH_PROVIDER_RESPONSE = + 'PreflightOAuthProviderResponse'; +export const PREFLIGHT_OAUTH_RESPONSE = 'PreflightOAuthResponse'; export const INTROSPECTION_QUERY_WITH_HEADERS = 'IntrospectionQueryWithHeaders'; export const PARENT_LOGOUT_SUCCESS = 'ParentLogoutSuccess'; diff --git a/packages/explorer/src/helpers/postMessageRelayHelpers.ts b/packages/explorer/src/helpers/postMessageRelayHelpers.ts index 58d25f3f..7ebe52a4 100644 --- a/packages/explorer/src/helpers/postMessageRelayHelpers.ts +++ b/packages/explorer/src/helpers/postMessageRelayHelpers.ts @@ -21,6 +21,9 @@ import { EXPLORER_SUBSCRIPTION_REQUEST, EXPLORER_SUBSCRIPTION_TERMINATION, EXPLORER_LISTENING_FOR_SCHEMA, + PREFLIGHT_OAUTH_REQUEST, + PREFLIGHT_OAUTH_PROVIDER_RESPONSE, + PREFLIGHT_OAUTH_RESPONSE, } from './constants'; import MIMEType from 'whatwg-mimetype'; import { readMultipartWebStream } from './readMultipartWebStream'; @@ -159,9 +162,21 @@ export type OutgoingEmbedMessage = } | { name: typeof PARENT_LOGOUT_SUCCESS; + } + | { + name: typeof PREFLIGHT_OAUTH_RESPONSE; + queryParams: string; }; export type IncomingEmbedMessage = + | MessageEvent<{ + name: typeof PREFLIGHT_OAUTH_REQUEST; + oauthUrl: string; + }> + | MessageEvent<{ + name: typeof PREFLIGHT_OAUTH_PROVIDER_RESPONSE; + queryParams: string; + }> | MessageEvent<{ name: typeof EXPLORER_LISTENING_FOR_HANDSHAKE; }> @@ -504,6 +519,23 @@ export const handleAuthenticationPostMessage = ({ embedUrl: string; }) => { const { data } = event; + if (data.name === PREFLIGHT_OAUTH_REQUEST) { + const handleEmbedPostMessage = (event: IncomingEmbedMessage) => { + if (event.data.name === PREFLIGHT_OAUTH_PROVIDER_RESPONSE) { + window.removeEventListener('message', handleEmbedPostMessage); + sendPostMessageToEmbed({ + message: { + name: PREFLIGHT_OAUTH_RESPONSE, + queryParams: event.data.queryParams, + }, + embeddedIFrameElement, + embedUrl, + }); + } + }; + window.addEventListener('message', handleEmbedPostMessage); + window.open(data.oauthUrl, undefined, '_blank'); + } // When the embed authenticates, save the partial token in local storage if (data.name === SET_PARTIAL_AUTHENTICATION_TOKEN_FOR_PARENT) { const partialEmbedApiKeysString = window.localStorage.getItem( diff --git a/packages/sandbox/src/helpers/constants.ts b/packages/sandbox/src/helpers/constants.ts index 91f069bd..c80876db 100644 --- a/packages/sandbox/src/helpers/constants.ts +++ b/packages/sandbox/src/helpers/constants.ts @@ -34,5 +34,9 @@ export const EXPLORER_LISTENING_FOR_PARTIAL_TOKEN = 'ExplorerListeningForPartialToken'; export const PARTIAL_AUTHENTICATION_TOKEN_RESPONSE = 'PartialAuthenticationTokenResponse'; +export const PREFLIGHT_OAUTH_REQUEST = 'PreflightOAuthRequest'; +export const PREFLIGHT_OAUTH_PROVIDER_RESPONSE = + 'PreflightOAuthProviderResponse'; +export const PREFLIGHT_OAUTH_RESPONSE = 'PreflightOAuthResponse'; export const INTROSPECTION_QUERY_WITH_HEADERS = 'IntrospectionQueryWithHeaders'; export const PARENT_LOGOUT_SUCCESS = 'ParentLogoutSuccess'; diff --git a/packages/sandbox/src/helpers/postMessageRelayHelpers.ts b/packages/sandbox/src/helpers/postMessageRelayHelpers.ts index 8bbba65e..3cac55ba 100644 --- a/packages/sandbox/src/helpers/postMessageRelayHelpers.ts +++ b/packages/sandbox/src/helpers/postMessageRelayHelpers.ts @@ -23,6 +23,9 @@ import { EXPLORER_SUBSCRIPTION_TERMINATION, EXPLORER_LISTENING_FOR_SCHEMA, INTROSPECTION_QUERY_WITH_HEADERS, + PREFLIGHT_OAUTH_REQUEST, + PREFLIGHT_OAUTH_RESPONSE, + PREFLIGHT_OAUTH_PROVIDER_RESPONSE, } from './constants'; import MIMEType from 'whatwg-mimetype'; import { readMultipartWebStream } from './readMultipartWebStream'; @@ -168,9 +171,21 @@ export type OutgoingEmbedMessage = } | { name: typeof PARENT_LOGOUT_SUCCESS; + } + | { + name: typeof PREFLIGHT_OAUTH_RESPONSE; + queryParams: string; }; export type IncomingEmbedMessage = + | MessageEvent<{ + name: typeof PREFLIGHT_OAUTH_REQUEST; + oauthUrl: string; + }> + | MessageEvent<{ + name: typeof PREFLIGHT_OAUTH_PROVIDER_RESPONSE; + queryParams: string; + }> | MessageEvent<{ name: typeof EXPLORER_LISTENING_FOR_HANDSHAKE; }> @@ -588,6 +603,23 @@ export const handleAuthenticationPostMessage = ({ embedUrl: string; }) => { const { data } = event; + if (data.name === PREFLIGHT_OAUTH_REQUEST) { + const handleEmbedPostMessage = (event: IncomingEmbedMessage) => { + if (event.data.name === PREFLIGHT_OAUTH_PROVIDER_RESPONSE) { + window.removeEventListener('message', handleEmbedPostMessage); + sendPostMessageToEmbed({ + message: { + name: PREFLIGHT_OAUTH_RESPONSE, + queryParams: event.data.queryParams, + }, + embeddedIFrameElement, + embedUrl, + }); + } + }; + window.addEventListener('message', handleEmbedPostMessage); + window.open(data.oauthUrl, undefined, '_blank'); + } // When the embed authenticates, save the partial token in local storage if (data.name === SET_PARTIAL_AUTHENTICATION_TOKEN_FOR_PARENT) { const partialEmbedApiKeysString = window.localStorage.getItem(