diff --git a/docs/content/docs/api/reference/mailboxes.mdx b/docs/content/docs/api/reference/mailboxes.mdx index 9e7dd1ba..f39453d8 100644 --- a/docs/content/docs/api/reference/mailboxes.mdx +++ b/docs/content/docs/api/reference/mailboxes.mdx @@ -503,6 +503,8 @@ Response: } ``` +After the user approves, the provider redirects to the API's callback page, which hands `code` and `state` back to the client: a web opener receives them via `postMessage`, and when there is no opener (a native in-app browser session) the page redirects to `warmbly://email-oauth?provider=...&code=...&state=...&error=...` instead. Either way, the client then calls Finish OAuth. + ### Finish OAuth `POST /emails/onboarding/oauth/finish` diff --git a/internal/api/handler/email_oauth_callback.go b/internal/api/handler/email_oauth_callback.go index 1cc2914b..e9f43921 100644 --- a/internal/api/handler/email_oauth_callback.go +++ b/internal/api/handler/email_oauth_callback.go @@ -13,6 +13,10 @@ import ( // The opener (the SPA) is expected to POST the code/state to // /emails/onboarding/oauth/finish with the user's bearer token. // +// Without an opener (the native app's ASWebAuthenticationSession, which has +// no popup parent) it instead redirects to the app's warmbly:// scheme; the +// session intercepts that navigation and the app calls oauth/finish itself. +// // We keep this on the API rather than the SPA so that the provider's // registered redirect_uri stays under our control and survives front-end // reshuffles. @@ -39,11 +43,20 @@ var callbackPage = template.Must(template.New("oauth-cb").Parse(` error: {{.Error}} }; var origin = {{.AppOrigin}}; + var delivered = false; try { if (window.opener) { window.opener.postMessage(payload, origin || "*"); + delivered = true; } } catch (e) { /* ignore */ } + if (!delivered) { + var q = "provider=" + encodeURIComponent(payload.provider || "") + + "&code=" + encodeURIComponent(payload.code || "") + + "&state=" + encodeURIComponent(payload.state || "") + + "&error=" + encodeURIComponent(payload.error || ""); + try { window.location.replace("warmbly://email-oauth?" + q); } catch (e) { /* ignore */ } + } setTimeout(function(){ try { window.close(); } catch(e){} }, 400); })();