package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "net/http" ) func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) { payloadHex := r.FormValue("payload") signature := r.FormValue("hmac") payloadBytes, err := hex.DecodeString(payloadHex) if err != nil { fmt.Fprintf(w, "Error: invalid JSON payload hex encoding: %s\n", err.Error()) return } signatureBytes, err := hex.DecodeString(signature) if err != nil { fmt.Fprintf(w, "Error: invalid HMAC signature hex encoding: %s\n", err.Error()) return } payload := ssoPayload{} err = json.Unmarshal(payloadBytes, &payload) if err != nil { fmt.Fprintf(w, "Error: cannot unmarshal JSON payload: %s\n", err.Error()) return } if payload.Domain == "" || payload.Token == "" || payload.Email == "" || payload.Name == "" { fmt.Fprintf(w, "Error: %s\n", errorMissingField.Error()) return } if payload.Link == "" { payload.Link = "undefined" } if payload.Photo == "" { payload.Photo = "undefined" } d, err := domainGet(payload.Domain) if err != nil { if err == errorNoSuchDomain { fmt.Fprintf(w, "Error: %s\n", err.Error()) } else { logger.Errorf("cannot get domain for SSO: %v", err) fmt.Fprintf(w, "Error: %s\n", errorInternal.Error()) } return } if d.SsoSecret == "" || d.SsoUrl == "" { fmt.Fprintf(w, "Error: %s\n", errorMissingConfig.Error()) return } key, err := hex.DecodeString(d.SsoSecret) if err != nil { logger.Errorf("cannot decode SSO secret as hex: %v", err) fmt.Fprintf(w, "Error: %s\n", err.Error()) return } h := hmac.New(sha256.New, key) h.Write(payloadBytes) expectedSignatureBytes := h.Sum(nil) if !hmac.Equal(expectedSignatureBytes, signatureBytes) { fmt.Fprintf(w, "Error: HMAC signature verification failed\n") return } _, err = commenterGetByCommenterToken(payload.Token) if err != nil && err != errorNoSuchToken { fmt.Fprintf(w, "Error: %s\n", err.Error()) return } c, err := commenterGetByEmail("sso:"+d.Domain, payload.Email) if err != nil && err != errorNoSuchCommenter { fmt.Fprintf(w, "Error: %s\n", err.Error()) return } var commenterHex string // TODO: in case of returning users, update the information we have on record? if err == errorNoSuchCommenter { commenterHex, err = commenterNew(payload.Email, payload.Name, payload.Link, payload.Photo, "sso:"+d.Domain, "") if err != nil { fmt.Fprintf(w, "Error: %s", err.Error()) return } } else { commenterHex = c.CommenterHex } if err = commenterSessionUpdate(payload.Token, commenterHex); err != nil { fmt.Fprintf(w, "Error: %s\n", err.Error()) return } fmt.Fprintf(w, "") }