package handler import ( "html/template" "net/http" "os" "github.com/gin-gonic/gin" ) // callbackPage renders a tiny HTML page that hands the OAuth code + state // back to the opening window via postMessage and then closes itself. // The opener (the SPA) is expected to POST the code/state to // /emails/onboarding/oauth/finish with the user's bearer token. // // 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. var callbackPage = template.Must(template.New("oauth-cb").Parse(` Connecting…
{{.Status}}
{{if .Error}}
{{.Error}}
{{end}}
`)) type callbackData struct { Provider string Code string State string Error string Status string AppOrigin string } func (h *Handler) EmailOAuthCallbackGmail(c *gin.Context) { renderOAuthCallback(c, "gmail") } func (h *Handler) EmailOAuthCallbackOutlook(c *gin.Context) { renderOAuthCallback(c, "outlook") } func renderOAuthCallback(c *gin.Context, provider string) { code := c.Query("code") state := c.Query("state") providerErr := c.Query("error") data := callbackData{ Provider: provider, Code: code, State: state, Error: providerErr, Status: "Connecting your mailbox… this window will close.", AppOrigin: os.Getenv("APP_ORIGIN"), } if providerErr != "" { data.Status = "Connection cancelled." } else if code == "" || state == "" { data.Error = "missing_code_or_state" data.Status = "Connection cancelled." } c.Header("Content-Type", "text/html; charset=utf-8") c.Status(http.StatusOK) _ = callbackPage.Execute(c.Writer, data) }