package auth import ( "encoding/json" "net/http" "strings" "time" ) // VerifyHandler verifies a wallet signature and issues JWT tokens and an API key. // This completes the authentication flow by validating the signed nonce and returning // access credentials. // // POST /v1/auth/verify // Request body: VerifyRequest // Response: { "access_token", "token_type", "expires_in", "refresh_token", "subject", "namespace", "api_key", "nonce", "signature_verified" } func (h *Handlers) VerifyHandler(w http.ResponseWriter, r *http.Request) { if h.authService == nil { writeError(w, http.StatusServiceUnavailable, "auth service not initialized") return } if r.Method != http.MethodPost { writeError(w, http.StatusMethodNotAllowed, "method not allowed") return } var req VerifyRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid json body") return } if strings.TrimSpace(req.Wallet) == "" || strings.TrimSpace(req.Nonce) == "" || strings.TrimSpace(req.Signature) == "" { writeError(w, http.StatusBadRequest, "wallet, nonce and signature are required") return } ctx := r.Context() verified, err := h.authService.VerifySignature(ctx, req.Wallet, req.Nonce, req.Signature, req.ChainType) if err != nil || !verified { writeError(w, http.StatusUnauthorized, "signature verification failed") return } // Mark nonce used nsID, _ := h.resolveNamespace(ctx, req.Namespace) h.markNonceUsed(ctx, nsID, strings.ToLower(req.Wallet), req.Nonce) token, refresh, expUnix, err := h.authService.IssueTokens(ctx, req.Wallet, req.Namespace) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } apiKey, err := h.authService.GetOrCreateAPIKey(ctx, req.Wallet, req.Namespace) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, map[string]any{ "access_token": token, "token_type": "Bearer", "expires_in": int(expUnix - time.Now().Unix()), "refresh_token": refresh, "subject": req.Wallet, "namespace": req.Namespace, "api_key": apiKey, "nonce": req.Nonce, "signature_verified": true, }) }