metric f ton
This commit is contained in:
@@ -8,7 +8,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
fbauth "github.com/kjannette/koin-ping/backend/internal/firebase"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
"github.com/kjannette/koin-ping/backend/internal/models"
|
||||
)
|
||||
|
||||
@@ -31,10 +32,9 @@ func writeJSON(w http.ResponseWriter, status int, v interface{}) {
|
||||
json.NewEncoder(w).Encode(v) //nolint:errcheck
|
||||
}
|
||||
|
||||
// Authenticate verifies the Firebase ID token and auto-provisions a local user
|
||||
// record. The local user UUID (not the Firebase UID) is placed into context so
|
||||
// all downstream handlers use it as the canonical user identifier.
|
||||
func Authenticate(userModel *models.UserModel) func(http.Handler) http.Handler {
|
||||
// Authenticate verifies the JWT from the Authorization header, loads the local
|
||||
// user from Postgres, and injects the user UUID + email + tier into context.
|
||||
func Authenticate(userModel *models.UserModel, jwtSecret string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
@@ -47,9 +47,8 @@ func Authenticate(userModel *models.UserModel) func(http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
token := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
if token == "" {
|
||||
log.Println("Empty token")
|
||||
rawToken := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
if rawToken == "" {
|
||||
writeJSON(w, http.StatusUnauthorized, errorResponse{
|
||||
Error: "UNAUTHORIZED",
|
||||
Message: "Invalid token format",
|
||||
@@ -57,12 +56,19 @@ func Authenticate(userModel *models.UserModel) func(http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("Verifying Firebase token...")
|
||||
decoded, err := fbauth.Auth().VerifyIDToken(r.Context(), token)
|
||||
if err != nil {
|
||||
log.Printf("Token verification failed: %v", err)
|
||||
parsed, err := jwt.Parse(rawToken, func(t *jwt.Token) (interface{}, error) {
|
||||
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, jwt.ErrSignatureInvalid
|
||||
}
|
||||
return []byte(jwtSecret), nil
|
||||
})
|
||||
if err != nil || !parsed.Valid {
|
||||
log.Printf("JWT verification failed: %v", err)
|
||||
|
||||
errMsg := err.Error()
|
||||
errMsg := ""
|
||||
if err != nil {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
if strings.Contains(errMsg, "expired") {
|
||||
writeJSON(w, http.StatusUnauthorized, errorResponse{
|
||||
Error: "TOKEN_EXPIRED",
|
||||
@@ -78,20 +84,37 @@ func Authenticate(userModel *models.UserModel) func(http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
firebaseUID := decoded.UID
|
||||
email, _ := decoded.Claims["email"].(string)
|
||||
|
||||
user, err := userModel.FindOrCreateByFirebaseUID(r.Context(), firebaseUID, email)
|
||||
if err != nil {
|
||||
log.Printf("Failed to provision local user for Firebase UID %s: %v", firebaseUID, err)
|
||||
writeJSON(w, http.StatusInternalServerError, errorResponse{
|
||||
Error: "INTERNAL_ERROR",
|
||||
Message: "Failed to initialize user account",
|
||||
claims, ok := parsed.Claims.(jwt.MapClaims)
|
||||
if !ok {
|
||||
writeJSON(w, http.StatusUnauthorized, errorResponse{
|
||||
Error: "UNAUTHORIZED",
|
||||
Message: "Invalid token claims",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Token verified! User UUID: %s, Email: %s", user.ID, email)
|
||||
userID, _ := claims["sub"].(string)
|
||||
email, _ := claims["email"].(string)
|
||||
|
||||
if userID == "" {
|
||||
writeJSON(w, http.StatusUnauthorized, errorResponse{
|
||||
Error: "UNAUTHORIZED",
|
||||
Message: "Invalid token: missing user ID",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := userModel.GetByID(r.Context(), userID)
|
||||
if err != nil || user == nil {
|
||||
log.Printf("JWT auth: user %s not found in DB: %v", userID, err)
|
||||
writeJSON(w, http.StatusUnauthorized, errorResponse{
|
||||
Error: "UNAUTHORIZED",
|
||||
Message: "User account not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("JWT verified — User UUID: %s, Email: %s", user.ID, email)
|
||||
|
||||
ctx := context.WithValue(r.Context(), UserIDKey, user.ID)
|
||||
ctx = context.WithValue(ctx, UserEmailKey, email)
|
||||
|
||||
Reference in New Issue
Block a user