orama/pkg/gateway/handlers/auth/wallet_handler.go
2026-02-13 12:47:02 +02:00

121 lines
3.5 KiB
Go

package auth
import (
"encoding/json"
"net/http"
"strings"
authsvc "github.com/DeBrosOfficial/network/pkg/gateway/auth"
)
// WhoamiHandler returns the authenticated user's identity and method.
// This endpoint shows whether the request is authenticated via JWT or API key,
// and provides details about the authenticated principal.
//
// GET /v1/auth/whoami
// Response: { "authenticated", "method", "subject", "namespace", ... }
func (h *Handlers) WhoamiHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// Determine namespace (may be overridden by auth layer)
ns := h.defaultNS
if v := ctx.Value(CtxKeyNamespaceOverride); v != nil {
if s, ok := v.(string); ok && s != "" {
ns = s
}
}
// Prefer JWT if present
if v := ctx.Value(CtxKeyJWT); v != nil {
if claims, ok := v.(*authsvc.JWTClaims); ok && claims != nil {
writeJSON(w, http.StatusOK, map[string]any{
"authenticated": true,
"method": "jwt",
"subject": claims.Sub,
"issuer": claims.Iss,
"audience": claims.Aud,
"issued_at": claims.Iat,
"not_before": claims.Nbf,
"expires_at": claims.Exp,
"namespace": ns,
})
return
}
}
// Fallback: API key identity
var key string
if v := ctx.Value(CtxKeyAPIKey); v != nil {
if s, ok := v.(string); ok {
key = s
}
}
writeJSON(w, http.StatusOK, map[string]any{
"authenticated": key != "",
"method": "api_key",
"api_key": key,
"namespace": ns,
})
}
// RegisterHandler registers a new application/client after wallet signature verification.
// This allows wallets to register applications and obtain client credentials.
//
// POST /v1/auth/register
// Request body: RegisterRequest
// Response: { "client_id", "app": { ... }, "signature_verified" }
func (h *Handlers) RegisterHandler(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
}
r.Body = http.MaxBytesReader(w, r.Body, 64*1024) // 64KB
var req RegisterRequest
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)
// In a real app we'd derive the public key from the signature, but for simplicity here
// we just use a placeholder or expect it in the request if needed.
// For Ethereum, we can recover it.
publicKey := "recovered-pk"
appID, err := h.authService.RegisterApp(ctx, req.Wallet, req.Namespace, req.Name, publicKey)
if err != nil {
writeError(w, http.StatusInternalServerError, err.Error())
return
}
writeJSON(w, http.StatusCreated, map[string]any{
"client_id": appID,
"app": map[string]any{
"app_id": appID,
"name": req.Name,
"namespace": req.Namespace,
"wallet": strings.ToLower(req.Wallet),
},
"signature_verified": true,
})
}