nidus-sync/sync/oauth.go
Eli Ribble 44c4f17f32
Massive rework of platform layer user/organization
The goal of this rework is to make it so I can pass around platform.User
instead of a pair of models.Organization and models.User. This is useful
for reason I kind of forget now, but it started with working on
notifications and ballooned massively from there into refactoring a
number of things that were bugging me.

This also includes a tiny amount of work on server-side events (SSE).

 * background stuff lives inside the platform now, which I need for
   having it push updates through SSE
 * userfile now lives in the platform, under file, so other platform
   functions can safely use it
 * oauth is broken into pieces and inside platform because other stuff
   was calling it already, but badly.
 * notifications go into the platform as well
2026-03-12 23:49:16 +00:00

72 lines
2.2 KiB
Go

package sync
import (
"context"
"net/http"
"net/url"
"strconv"
"github.com/Gleipnir-Technology/nidus-sync/auth"
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/html"
nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
"github.com/Gleipnir-Technology/nidus-sync/platform"
"github.com/rs/zerolog/log"
)
type contentOauthPrompt struct{}
// Build the ArcGIS authorization URL with PKCE
func buildArcGISAuthURL(clientID string) string {
baseURL := "https://www.arcgis.com/sharing/rest/oauth2/authorize/"
params := url.Values{}
params.Add("client_id", clientID)
params.Add("redirect_uri", config.ArcGISOauthRedirectURL())
params.Add("response_type", "code")
//params.Add("code_challenge", generateCodeChallenge(codeVerifier))
//params.Add("code_challenge_method", "S256")
// See https://developers.arcgis.com/rest/users-groups-and-items/token/
// expiration is defined in minutes
var expiration int
if config.IsProductionEnvironment() {
// 2 weeks is the maximum allowed
expiration = 20160
} else {
expiration = 20
}
params.Add("expiration", strconv.Itoa(expiration))
return baseURL + "?" + params.Encode()
}
func getArcgisOauthBegin(w http.ResponseWriter, r *http.Request) {
authURL := buildArcGISAuthURL(config.ClientID)
http.Redirect(w, r, authURL, http.StatusFound)
}
func getArcgisOauthCallback(w http.ResponseWriter, r *http.Request) {
code := r.URL.Query().Get("code")
log.Info().Str("code", code).Msg("Handling oauth callback")
if code == "" {
respondError(w, "Access code is empty", nil, http.StatusBadRequest)
return
}
user, err := auth.GetAuthenticatedUser(r)
if err != nil {
respondError(w, "You're not currently authenticated, which really shouldn't happen.", err, http.StatusUnauthorized)
return
}
err = platform.HandleOauthAccessCode(r.Context(), *user, code)
if err != nil {
respondError(w, "Failed to handle access code", err, http.StatusInternalServerError)
return
}
http.Redirect(w, r, config.MakeURLNidus("/"), http.StatusFound)
}
func getOAuthRefresh(ctx context.Context, r *http.Request, user platform.User) (*html.Response[contentOauthPrompt], *nhttp.ErrorWithStatus) {
data := contentOauthPrompt{}
return html.NewResponse("sync/oauth-prompt.html", data), nil
}