aboutsummaryrefslogtreecommitdiff
path: root/backend/internal/api/auth.go
diff options
context:
space:
mode:
authorzwlucas <lucas.fariamo08@gmail.com>2026-05-29 18:57:37 +0000
committerzwlucas <lucas.fariamo08@gmail.com>2026-05-29 18:57:37 +0000
commit6cc0bfd1d79074df790272b8091b1f0226d14283 (patch)
treecab18b0f708cf3b0eaeeeb98e2cf81459365b33e /backend/internal/api/auth.go
downloadyaum-6cc0bfd1d79074df790272b8091b1f0226d14283.tar.gz
yaum-6cc0bfd1d79074df790272b8091b1f0226d14283.zip
feat: upload project
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'backend/internal/api/auth.go')
-rw-r--r--backend/internal/api/auth.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/backend/internal/api/auth.go b/backend/internal/api/auth.go
new file mode 100644
index 0000000..5095194
--- /dev/null
+++ b/backend/internal/api/auth.go
@@ -0,0 +1,144 @@
+package api
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "encoding/json"
+ "log"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/golang-jwt/jwt/v5"
+ "golang.org/x/crypto/bcrypt"
+)
+
+type AuthHandler struct {
+ username string
+ passwordHash string
+ jwtSecret []byte
+}
+
+type loginRequest struct {
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
+type Claims struct {
+ Username string `json:"username"`
+ jwt.RegisteredClaims
+}
+
+func NewAuthHandler(username, passwordHash, jwtSecret string) *AuthHandler {
+ if username == "" {
+ username = "admin"
+ }
+ secret := []byte(jwtSecret)
+ if len(secret) == 0 {
+ key := make([]byte, 32)
+ if _, err := rand.Read(key); err != nil {
+ secret = []byte("dev-secret-do-not-use-in-production")
+ } else {
+ secret = []byte(base64.RawURLEncoding.EncodeToString(key))
+ }
+ log.Println("[auth] jwt_secret nao configurado — usando chave gerada aleatoriamente")
+ }
+ if passwordHash == "" {
+ log.Fatal("[auth] ADMIN_PASSWORD_HASH ou ADMIN_PASSWORD deve ser definido")
+ }
+ return &AuthHandler{
+ username: username,
+ passwordHash: passwordHash,
+ jwtSecret: secret,
+ }
+}
+
+func (a *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
+ var req loginRequest
+ if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "json invalido"})
+ return
+ }
+ if req.Username != a.username {
+ writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "credenciais invalidas"})
+ return
+ }
+ if err := bcrypt.CompareHashAndPassword([]byte(a.passwordHash), []byte(req.Password)); err != nil {
+ writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "credenciais invalidas"})
+ return
+ }
+
+ now := time.Now()
+ claims := Claims{
+ Username: req.Username,
+ RegisteredClaims: jwt.RegisteredClaims{
+ IssuedAt: jwt.NewNumericDate(now),
+ ExpiresAt: jwt.NewNumericDate(now.Add(7 * 24 * time.Hour)),
+ Subject: req.Username,
+ },
+ }
+
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+ signed, err := token.SignedString(a.jwtSecret)
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "erro ao gerar token"})
+ return
+ }
+
+ writeJSON(w, http.StatusOK, map[string]string{"token": signed})
+}
+
+func (a *AuthHandler) Verify(w http.ResponseWriter, r *http.Request) {
+ tokenStr := extractToken(r)
+ if tokenStr == "" {
+ writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "token ausente"})
+ return
+ }
+ if !a.validateToken(tokenStr) {
+ writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "token invalido ou expirado"})
+ return
+ }
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func (a *AuthHandler) Middleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if strings.HasPrefix(r.URL.Path, "/api/auth/") {
+ next.ServeHTTP(w, r)
+ return
+ }
+ if r.Method == http.MethodGet || r.Method == http.MethodOptions {
+ next.ServeHTTP(w, r)
+ return
+ }
+ tokenStr := extractToken(r)
+ if tokenStr == "" || !a.validateToken(tokenStr) {
+ writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "nao autorizado"})
+ return
+ }
+ next.ServeHTTP(w, r)
+ })
+}
+
+func (a *AuthHandler) validateToken(tokenStr string) bool {
+ claims := &Claims{}
+ token, err := jwt.ParseWithClaims(tokenStr, claims, func(t *jwt.Token) (any, error) {
+ return a.jwtSecret, nil
+ })
+ if err != nil || !token.Valid {
+ return false
+ }
+ return claims.Username == a.username
+}
+
+func extractToken(r *http.Request) string {
+ auth := r.Header.Get("Authorization")
+ if strings.HasPrefix(auth, "Bearer ") {
+ return strings.TrimPrefix(auth, "Bearer ")
+ }
+ cookie, err := r.Cookie("auth_token")
+ if err == nil && cookie.Value != "" {
+ return cookie.Value
+ }
+ return ""
+}