2026-01-13 20:26:15 +00:00
|
|
|
package sync
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
2026-01-22 03:27:32 +00:00
|
|
|
"net/url"
|
|
|
|
|
"strconv"
|
2026-01-13 20:26:15 +00:00
|
|
|
|
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/auth"
|
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/background"
|
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/config"
|
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
2026-01-30 18:21:27 +00:00
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/html"
|
2026-01-13 20:26:15 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
oauthPromptT = buildTemplate("oauth-prompt", "authenticated")
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-14 20:15:48 +00:00
|
|
|
type ContextOauthPrompt struct {
|
|
|
|
|
User User
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-22 03:27:32 +00:00
|
|
|
// 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()
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 20:26:15 +00:00
|
|
|
func getArcgisOauthBegin(w http.ResponseWriter, r *http.Request) {
|
2026-01-22 03:27:32 +00:00
|
|
|
authURL := buildArcGISAuthURL(config.ClientID)
|
2026-01-13 20:26:15 +00:00
|
|
|
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 = background.HandleOauthAccessCode(r.Context(), user, code)
|
|
|
|
|
if err != nil {
|
|
|
|
|
respondError(w, "Failed to handle access code", err, http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-01-22 03:27:32 +00:00
|
|
|
http.Redirect(w, r, config.MakeURLNidus("/"), http.StatusFound)
|
2026-01-13 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getOAuthRefresh(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
user, err := auth.GetAuthenticatedUser(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Redirect(w, r, "/?next=/oauth/refresh", http.StatusFound)
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-01-14 20:15:48 +00:00
|
|
|
oauthPrompt(w, r, user)
|
2026-01-13 20:26:15 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-14 20:15:48 +00:00
|
|
|
func oauthPrompt(w http.ResponseWriter, r *http.Request, user *models.User) {
|
|
|
|
|
userContent, err := contentForUser(r.Context(), user)
|
|
|
|
|
if err != nil {
|
|
|
|
|
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
data := ContextOauthPrompt{
|
|
|
|
|
User: userContent,
|
2026-01-13 20:26:15 +00:00
|
|
|
}
|
2026-01-30 18:21:27 +00:00
|
|
|
html.RenderOrError(w, oauthPromptT, data)
|
2026-01-13 20:26:15 +00:00
|
|
|
}
|