mirror of
https://github.com/marcusDenslow/terminalShop.git
synced 2026-10-25 18:08:45 +01:00
previously main.go handled auth directly, it connected to the database, ran migrations, created a JWT manager and SSHAuthService, and authenticated the SSH key before the TUI even started. this commit rips all of that out Auth now happens inside the TUI during the splash screen via splashAuthCmd, which calls the /auth/token endpoint. if no user exists or the fingerprint it auto-creates one using the SSH public ke, so there is no longer a separate registartion flow. The token and user com back in the response. once authed splashViewInitCmd fires a single /view/init request that loads everything needed to start the app, products, cart, addresses, cards, and orders, before switching to the shop page on the model side, User/*models.user and gossh.PublicKey were replaced with plain Fingerprint and SSHPublicKeyStr strings, and NewModelWithAuth was updated too
183 lines
5.4 KiB
Go
183 lines
5.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"terminalShop/pkg/auth"
|
|
"terminalShop/pkg/database"
|
|
"terminalShop/pkg/models"
|
|
"terminalShop/pkg/utils"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// AuthHandler handles authentication-related requests
|
|
type AuthHandler struct {
|
|
jwtManager *auth.JWTManager
|
|
authFingerprintKey string
|
|
}
|
|
|
|
// NewAuthHandler creates a new auth handler
|
|
func NewAuthHandler(jwtManager *auth.JWTManager, authFingerprintKey string) *AuthHandler {
|
|
return &AuthHandler{
|
|
jwtManager: jwtManager,
|
|
authFingerprintKey: authFingerprintKey,
|
|
}
|
|
}
|
|
|
|
// Type to represent a request to exchange SSH fingerprint for JWT
|
|
type TokenRequest struct {
|
|
Fingerprint string `json:"fingerprint"`
|
|
SSHPublicKey string `json:"ssh_public_key"`
|
|
ClientSecret string `json:"client_secret"`
|
|
}
|
|
|
|
// Type to represent the JWT token response
|
|
type TokenResponse struct {
|
|
AccessToken string `json:"access_token"`
|
|
TokenType string `json:"token_type"`
|
|
ExpiresIn int `json:"expires_in"`
|
|
User models.PublicUser `json:"user"`
|
|
}
|
|
|
|
func (h *AuthHandler) GetToken(w http.ResponseWriter, r *http.Request) {
|
|
db := database.GetDB()
|
|
|
|
var req TokenRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
utils.RespondError(w, http.StatusBadRequest, "INVALID_JSON", "invalid request body", nil)
|
|
return
|
|
}
|
|
|
|
// validate shared secret
|
|
if req.ClientSecret != h.authFingerprintKey {
|
|
utils.RespondError(w, http.StatusUnauthorized, "INVALID_SECRET", "invalid client secret", nil)
|
|
return
|
|
}
|
|
|
|
// Return if no fingerprint is found
|
|
if req.Fingerprint == "" {
|
|
utils.RespondError(w, http.StatusBadRequest, "MISSING_FINGERPRINT", "fingerprint is required", nil)
|
|
return
|
|
}
|
|
|
|
// look up user by his fingies
|
|
var user models.User
|
|
err := db.Where("ssh_key_fingerprint = ?", req.Fingerprint).First(&user).Error
|
|
if err == gorm.ErrRecordNotFound {
|
|
if req.SSHPublicKey == "" {
|
|
utils.RespondError(w, http.StatusUnauthorized, "USER_NOT_FOUND", "no user found for this fingerprint", nil)
|
|
return
|
|
}
|
|
user = models.User{
|
|
SSHKeyFingerprint: req.Fingerprint,
|
|
SSHPublicKey: req.SSHPublicKey,
|
|
}
|
|
if err := db.Create(&user).Error; err != nil {
|
|
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to create user", nil)
|
|
return
|
|
}
|
|
} else if err != nil {
|
|
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "database error", nil)
|
|
return
|
|
}
|
|
|
|
token, err := h.jwtManager.GenerateToken(user.ID, user.Email, user.Name)
|
|
if err != nil {
|
|
utils.RespondError(w, http.StatusInternalServerError, "TOKEN_ERROR", "failed to generate token", nil)
|
|
return
|
|
}
|
|
|
|
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
|
|
"access_token": token,
|
|
"token_type": "Bearer",
|
|
"expires_in": 1800, // 30 minutes in seconds
|
|
"user": user.ToPublic(),
|
|
})
|
|
}
|
|
|
|
// RegisterRequest represents a user registration request
|
|
type RegisterRequest struct {
|
|
SSHPublicKey string `json:"ssh_public_key"`
|
|
SSHKeyFingerprint string `json:"ssh_key_fingerprint"`
|
|
Name string `json:"name,omitempty"` // Optional
|
|
Email string `json:"email,omitempty"` // Optional
|
|
}
|
|
|
|
// RegisterWithSSHKey registers a new user with their SSH public key
|
|
// No username required - following terminal.shop pattern
|
|
func (h *AuthHandler) RegisterWithSSHKey(w http.ResponseWriter, r *http.Request) {
|
|
db := database.GetDB()
|
|
|
|
var req RegisterRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
utils.RespondError(w, http.StatusBadRequest, "INVALID_JSON", "invalid request body", nil)
|
|
return
|
|
}
|
|
|
|
// Validate required fields
|
|
errors := map[string]string{}
|
|
if req.SSHPublicKey == "" {
|
|
errors["ssh_public_key"] = "SSH public key is required"
|
|
}
|
|
|
|
if req.SSHKeyFingerprint == "" {
|
|
errors["ssh_key_fingerprint"] = "SSH key fingerprint is required"
|
|
}
|
|
|
|
if len(errors) > 0 {
|
|
utils.RespondError(w, http.StatusBadRequest, "VALIDATION_ERROR", "validation failed", map[string]interface{}{
|
|
"errors": errors,
|
|
})
|
|
return
|
|
}
|
|
|
|
// Check if SSH key is already registered
|
|
var existing models.User
|
|
err := db.Where("ssh_key_fingerprint = ?", req.SSHKeyFingerprint).First(&existing).Error
|
|
if err == nil {
|
|
utils.RespondError(w, http.StatusConflict, "SSH_KEY_REGISTERED", "SSH key already registered", nil)
|
|
return
|
|
}
|
|
|
|
// Create new user (no username required!)
|
|
user := models.User{
|
|
SSHPublicKey: req.SSHPublicKey,
|
|
SSHKeyFingerprint: req.SSHKeyFingerprint,
|
|
Name: req.Name,
|
|
Email: req.Email,
|
|
}
|
|
|
|
if err := db.Create(&user).Error; err != nil {
|
|
utils.RespondError(w, http.StatusInternalServerError, "DATABASE_ERROR", "failed to create user", nil)
|
|
return
|
|
}
|
|
|
|
utils.RespondSuccess(w, http.StatusCreated, map[string]interface{}{
|
|
"user": user.ToPublic(),
|
|
})
|
|
}
|
|
|
|
// GetUserBySSHKey retrieves a user by their SSH key fingerprint
|
|
func (h *AuthHandler) GetUserBySSHKey(w http.ResponseWriter, r *http.Request) {
|
|
db := database.GetDB()
|
|
|
|
fingerprint := r.URL.Query().Get("fingerprint")
|
|
if fingerprint == "" {
|
|
utils.RespondError(w, http.StatusBadRequest, "MISSING_FINGERPRINT", "fingerprint query parameter is required", nil)
|
|
return
|
|
}
|
|
|
|
var user models.User
|
|
err := db.Where("ssh_key_fingerprint = ?", fingerprint).First(&user).Error
|
|
if err != nil {
|
|
utils.RespondError(w, http.StatusNotFound, "USER_NOT_FOUND", "user not found", nil)
|
|
return
|
|
}
|
|
|
|
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
|
|
"user": user.ToPublic(),
|
|
})
|
|
}
|