Passing a server-side value to custom JavaScript after an OIDC login

Setup — Baserow Cloud, Application Builder app published on a custom domain.
User source = Local Baserow table, authentication = OIDC provider (Outseta).
Login itself works fine.

Goal — Outseta ships a JS SDK that can open the user’s profile/billing widget
as a modal on my own page, instead of sending them to Outseta’s full-page hosted
profile. It needs an Outseta JWT in the browser: Outseta.setAccessToken(jwt)
then Outseta.profile.open(). I’ve confirmed the modal works perfectly once the
token is set manually in the console.

The problem: the OIDC code exchange happens server-side between Baserow and
Outseta, so the browser never receives an Outseta token. Outseta.getAccessToken()
returns null.

Outseta exposes a server-side endpoint to mint a JWT for a given user
(POST /api/v1/tokens, API key in the header, email in the body), which is
exactly what the workflow actions should be able to call.

What I tried — Three actions on the login form’s After login event:
Send HTTP requestExecute code (parse the JSON) → Open page (redirect
back with the token appended to the URL, so my custom JS can read it).

What I ran into

  1. The After login event does not fire when the login goes through the OIDC
    provider. With all three actions fully configured and no validation warnings,
    the network tab shows only GET /api/builder/domains/published/page/{id}/workflow_actions/
    and no dispatch POST at all. Is this expected for SSO, or a bug? The event is
    offered on the Login element regardless of which auth method it uses.

  2. There is no on-page-load trigger, so once the user is in the app there is no
    other hook to run those actions automatically. Every action needs a click.

  3. There seems to be no supported way to hand a workflow action’s result to the
    app’s own custom JavaScript. Actions chain to each other, but the Custom
    CSS/JS pane is a separate world. The only two routes I found are both
    workarounds: append the value to a URL via Open page, or write it to a row
    and read it back out of the DOM. Putting a bearer token in a URL is not great —
    it lands in history and in Referer headers.

  4. No raw HTML element, so I can’t render a server-side value into the page
    either.

Questions

  • Is point 1 a known limitation or a bug worth reporting?
  • Is there any trigger that fires after an SSO/OIDC login?
  • Is there a supported way to pass a workflow action result to custom JS?
  • Failing that, which workaround would you consider least bad here?

Outseta side, for reference:

hi @bastien

Is point 1 a known limitation or a bug worth reporting?

Is there any trigger that fires after an SSO/OIDC login?

You’re right that “After login” doesn’t fire for OIDC (same is true for SAML as well); it only fires for Email/password auth. Part of solving your problem is enabling “After login” to fire for SSO providers.

I’m not sure if omitting this for SSO was an oversight or an intentional choice - let me discuss this with the team. The end result may be that we’ll either implement “After login” to fill this gap, or remove it for SSO.

There is also no page load trigger today, so once the user is in the app nothing runs without a click.

Is there a supported way to pass a workflow action result to custom JS?

We don’t currently have a way to fire custom JS code in the browser, with dynamic data, such as the response of your HTTP Request payload.

I think what we need to do here, is to create a new Workflow Action type that either emits a browser event with your data (which custom JS can then receive and call your SDK), or a Workflow Action type that allows running browser code (call your SDK directly). Again, let me check with the team on which direction we want to go.

Failing that, which workaround would you consider least bad here?

As for a workaround, you could:

  • Create a Button element with an HTTP Request workflow action
    • The main point of using a Button + HTTP Request workflow action here is to avoid setting your token in Custom JS, which is public.
    • Set its visibility to Logged-in users and hide it visually with custom CSS.
  • Dispatch the HTTP Request using Custom JS, then call your SDK.

Your custom JS might look something like this:

(async () => {
  // Fetch the Baserow user source refresh token.
  const m = document.cookie.match(/(?:^|;\s*)user_source_token=([^;]*)/);
  const refreshToken = m && decodeURIComponent(m[1]);

  // Return early if you're not logged in.
  if (!refreshToken) return;

  // Exchange the refresh token for an access token.
  const backend = 'https://api.baserow.io';
  const r1 = await fetch(`${backend}/api/user-source-auth-refresh/`, {
    method: 'POST', headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({ refresh_token: refreshToken })
  });
  const { access_token } = await r1.json();

  // Dispatch the workflow action. You'll need to find the
  // workflow action ID by inspecting the network request in
  // your published app.
  const workflowActionId = '';

  const data = new FormData();
  data.append('metadata', JSON.stringify({}));
  const r2 = await fetch(`${backend}/api/builder/workflow_action/${workflowActionId}/dispatch/`, {
    method: 'POST', headers: { Authorization: `JWT ${access_token}` }, body: data
  });
  const { body } = await r2.json();

  // Then call your Outseta SDK
  Outseta.setAccessToken(body.<your field>);
})();

Thanks a lot for the clear answer, and for taking the “After login” case back to the team.

I had actually thought of the hidden-button approach myself, but I find it inelegant and a bit too convoluted to rely on — especially since it depends on internal endpoints that could change. For now I’ve gone around the problem by using an external page hosted by my OAuth provider. It works, but it forces my users to leave the application for part of their account management, which isn’t the experience I want.

Three things would make a real difference for me:

  1. Workflow action triggers that aren’t limited to Baserow’s native authentication. Right now OIDC feels a little like second-class auth: you log in fine, but you lose the hooks that come with email/password. Having “After login” fire for SSO too would close that gap.

  2. A workflow action that can talk to the browser — emitting an event with the action’s data, or running browser code with it. That would be a considerable step forward, and combined with the first point it would cover, I think, most of what OIDC users need.

  3. The cherry on top: being able to inject HTML into the header and into Baserow pages. That’s what would let me integrate third-party SDKs and widgets properly instead of working around them.

With those three solved, I wouldn’t feel limited by Baserow at all, and I’d genuinely call it the perfect solution for building a client portal or a scalable internal tool.