mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-19 08:01:16 +00:00
76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/warmbly/warmbly/internal/config"
|
|
"github.com/warmbly/warmbly/internal/errx"
|
|
)
|
|
|
|
// OIDCBegin starts a generic OpenID Connect authorization.
|
|
//
|
|
// It returns the URL rather than issuing a 302 so the dashboard, which is a
|
|
// single-page app on a different origin from the API, can navigate to it
|
|
// itself. The state, nonce and PKCE verifier are stored server-side and never
|
|
// leave the backend.
|
|
func (h *Handler) OIDCBegin(c *gin.Context) {
|
|
redirect, err := h.AuthService.OIDCBegin(c.Request.Context())
|
|
if err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, redirect)
|
|
}
|
|
|
|
// OIDCCallback is where the provider sends the browser back.
|
|
//
|
|
// It answers with a redirect, not JSON: a person is looking at this response.
|
|
// The session itself is held server-side behind a single-use handoff code that
|
|
// the dashboard immediately exchanges, so no token ever appears in a URL,
|
|
// browser history, Referer header or proxy log.
|
|
func (h *Handler) OIDCCallback(c *gin.Context) {
|
|
base := config.AppBaseURL()
|
|
|
|
// A provider-side refusal arrives as ?error=, not as a failed exchange.
|
|
if e := c.Query("error"); e != "" {
|
|
c.Redirect(http.StatusFound, base+"/auth/login?sso_error="+url.QueryEscape(e))
|
|
return
|
|
}
|
|
|
|
handoff, err := h.AuthService.OIDCCallback(
|
|
c.Request.Context(),
|
|
c.Query("code"),
|
|
c.Query("state"),
|
|
c.ClientIP(),
|
|
c.Request.UserAgent(),
|
|
)
|
|
if err != nil {
|
|
c.Redirect(http.StatusFound, base+"/auth/login?sso_error="+url.QueryEscape(err.Message))
|
|
return
|
|
}
|
|
|
|
c.Redirect(http.StatusFound, base+"/auth/sso?code="+url.QueryEscape(handoff))
|
|
}
|
|
|
|
type oidcExchangeRequest struct {
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
// OIDCExchange swaps the handoff code for the session. Single use.
|
|
func (h *Handler) OIDCExchange(c *gin.Context) {
|
|
var req oidcExchangeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
errx.Handle(c, errx.ErrInvalid)
|
|
return
|
|
}
|
|
|
|
result, err := h.AuthService.OIDCExchange(c.Request.Context(), req.Code)
|
|
if err != nil {
|
|
errx.Handle(c, err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, result)
|
|
}
|