Oauth2 and how to determine redirect url (for worker on static observable user content)?

Hey folks,

Looking to setup an oauth2 interaction in a notebook, but this requires registering the redirect URI with the external identity solution. Some insecure identity solutions allow for wildcard redirect URIs, but most should block that and will require the URLs to be registered explicitly.

For example, for Microsoft AAD I need to register the URL explicitly and the URL looks to have some version or hash code / blob ID in the URL. Is this static or coupled to notebook version or anything like that? Can I register this exact URL in my oauth2 application, and will it change over time? Or how can this work?

The redirect URI ‘https://keystroke.static.observableusercontent.com/next/worker-ea1f1578.html’ specified in the request does not match the redirect URIs configured for the application


I’m not sure there’s a way to use the redirect method without a proxy, since you cannot redirect to the notebook iframe. But have you seen @azure/msal-browser - npm?

Edit: Looking at the library’s docs and prerequisites I don’t think this is possible. You could take a look at Oauth 2.0 Client Examples / Tom Larkworthy | Observable to find some potential workarounds.

Yes I’m using msal-browser client. The issue is AADSTS doesn’t allow wildcard redirect URIs so you can’t do something like register “https://keystroke.static.observableusercontent.com/*” as a redirect URI. Some other identity solutions allow this (or at least used to). One can use the msal package to login with pop-up or redirect if you temporarily register the dynamic worker URL as the redirect URI in the oauth2 app configuration. But if that changes over time it’s not reliable approach.

Using a proxy is undesirable in what would otherwise be purely client-side SPA.

Since an actual location change back to the worker doesn’t give you a functioning notebook, I suspect that this is more about the cookie domain and path?

In that case you could try to register your worker domain and root path (https://keystroke.static.observableusercontent.com/) instead of the full path, and then poll until you have access to the window’s location again.

Here’s a very basic example:

viewof authenticated = {
  let win;
  const button = htl.html`<div><button onclick=${open}>Authenticate`;
  return button;

  function open() {
    win = window.open("about:blank", "_blank", "popup=1");
    invalidation.then(() => win.close());

    win.addEventListener("unload", async e => {
      const href = await poll(win);
      button.value = href ? href !== "about:blank" : false;
      button.dispatchEvent(new Event("input", {bubbles: true}));
    }, {once: true});

    win.document.documentElement.innerHTML = `<body>
      <p><a href="https://bit.ly/3WoSMpE">redirect</a></p>
      <p><a href="https://bit.ly/">no redirect</a></p>

  async function poll(win, interval = 2000) {
    while(!win.closed) {
      await Promises.delay(interval);
      try { return win.location.href }
      catch(e) {}