Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow other API to open popups besides window.open #1321

Open
5 tasks done
kcarnold opened this issue Nov 13, 2024 · 1 comment
Open
5 tasks done

Allow other API to open popups besides window.open #1321

kcarnold opened this issue Nov 13, 2024 · 1 comment
Labels
feature request A feature has been asked for or suggested by the community

Comments

@kcarnold
Copy link

Checklist

Describe the problem you'd like to have solved

Microsoft Office add-ins require that any popups be opened using a special API instead of window.open. A few unusual constraints of that API are:

  1. The popup must start in the same domain (it can redirect out of it).
  2. There isn't a reliable, cross-platform way to send information to the dialog after it's opened (i.e., you should package everything you need to send into the query string)
  3. Sending data back from the popup also requires a custom API.

Describe the ideal solution

I've done some gymnastics (see below) to make this API work with loginWithPopup, but it would be much easier if the user of the library could provide a custom dialog API. For example, something like:

loginWithPopup({}, {
  popupController: async (popupURL) => {
     const bounceURL = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '') + '/popup.html?redirect=' + encodeURIComponent(popupURL);
    const dialog = await asyncify(Office.context.ui.displayDialogAsync)(bounceURL, OTHER_OPTIONS);
    const tokenReply = await waitForMessageReceived(dialog);
    return tokenReply;
    }
});

where I've imagined asyncified versions of the Office dialog code.

Alternatives and current workarounds

My workaround code currently looks like:

		let dialog: Office.Dialog;

		const processMessage = async (
			args:
				| { message: string; origin: string | undefined }
				| { error: number }
		) => {
			if ('error' in args) {
				// eslint-disable-next-line no-console
				console.error('Error:', args.error);
				return;
			}
			// eslint-disable-next-line prefer-const
			let messageFromDialog = JSON.parse(args.message);
			dialog.close();

			if (messageFromDialog.status === 'success') {
				// The dialog reported a successful login.
				// eslint-disable-next-line prefer-const
				let token = messageFromDialog.auth0Token;
				// eslint-disable-next-line no-console
				console.log('Login successful.', token);

				// Mock the window message event that auth0-spa-js expects
				// see https://github.com/auth0/auth0-spa-js/blob/f2e566849efa398ca599daf9ebdfbbd62fcb1894/__tests__/utils.test.ts#L234
				let messageEvent = new MessageEvent('message', {
					data: {
						type: 'authorization_response',
						response: {id_token: token}
					}
				})
				window.dispatchEvent(messageEvent);
			}
			else {
				// eslint-disable-next-line no-console
				console.error('Login failed.', messageFromDialog);
			}
		};


		// Make the href of the popup be a setter so that we can actually launch the dialog with the correct url to begin with
		const mockPopup = {
			location: { 
				set href(url: string) {
					console.log("Setting location.href to", url);
					
					// Set up an Office dialog to do the login flow
					// height and width are percentages of the size of the screen.
					// How MS use it: https://github.com/OfficeDev/Office-Add-in-samples/blob/main/Samples/auth/Office-Add-in-Microsoft-Graph-React/utilities/office-apis-helpers.ts#L38

					// Bounce off /popup.html?redirect=... to get the token
					let redirect = encodeURIComponent(url);
					let bounceURL = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '') + '/popup.html?redirect=' + redirect;
					console.log("Bouncing to", bounceURL);
					Office.context.ui.displayDialogAsync(
						bounceURL,
						{ height: 45, width: 55 },
						function (result) {
							dialog = result.value;
							dialog.addEventHandler(
								Office.EventType.DialogMessageReceived,
								processMessage
							);
						}
					);
				}
			},
			closed: false,
			close: () => {mockPopup.closed = true},
		};
...
await loginWithPopup({}, {popup: mockPopup});

Additional context

No response

@kcarnold kcarnold added the feature request A feature has been asked for or suggested by the community label Nov 13, 2024
@kcarnold
Copy link
Author

kcarnold commented Nov 15, 2024

Nevermind, the popup approach doesn't actually work here at all because auth0 tries to use a webmessage response, and Office doesn't allow cross-origin messages from dialogs without its special API too.

But I realized that loginWithRedirect is a more robust flow. Our WIP approach:

	if (!isAuthenticated) {
		let dialog: Office.Dialog;

		// Strategy: the popup will pass its redirect-callback data here, so we can pass it on to handleRedirectCallback
		const processMessage = async (
			args:
				| { message: string; origin: string | undefined }
				| { error: number }
		) => {
			if ('error' in args) {
				console.error('Error:', args.error);
				return;
			}
			let messageFromDialog = JSON.parse(args.message);
			dialog.close();

			if (messageFromDialog.status === 'success') {
				// The dialog reported a successful login.
				handleRedirectCallback(messageFromDialog.urlWithAuthInfo);
			}
			else {
				console.error('Login failed.', messageFromDialog);
			}
		};

	// Actually make a popup using MS dialog API
	// hook the message event from the popup to set close false and get the token
	return (
		<div>
			Login here:
			<button onClick= { async () => {
				// Use this dialog for the Auth0 client library.
				await loginWithRedirect({
					openUrl: async (url: string) => {
						const redirect = encodeURIComponent(url);
						const bounceURL = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '') + '/popup.html?redirect=' + redirect;
						// height and width are percentages of the size of the screen.
						// How MS use it: https://github.com/OfficeDev/Office-Add-in-samples/blob/main/Samples/auth/Office-Add-in-Microsoft-Graph-React/utilities/office-apis-helpers.ts#L38
						Office.context.ui.displayDialogAsync(
							bounceURL,
							{ height: 45, width: 55 },
							function (result) {
								dialog = result.value;
								dialog.addEventHandler(
									Office.EventType.DialogMessageReceived,
									processMessage
								);
							}
						);
					}
				});
		}}
				>Log in
			</button>
			</div>
		);
	}

Handling log-out is a different story... we haven't figured that out yet!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request A feature has been asked for or suggested by the community
Projects
None yet
Development

No branches or pull requests

1 participant