added comments
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gorm.io/gorm"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"git.schreifuchs.ch/schreifuchs/ng-blog/backend/internal/model"
|
||||
)
|
||||
|
||||
// Signup handles user signup by decoding request body, hashing the password, and saving user data to the database.
|
||||
func (s *Service) Signup(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
var login Login
|
||||
@@ -50,6 +50,7 @@ func (s *Service) Signup(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Login handles user login by decoding request body, verifying credentials, and returning a JWT token.
|
||||
func (s *Service) Login(w http.ResponseWriter, r *http.Request) {
|
||||
var login Login
|
||||
var user model.User
|
||||
@@ -86,6 +87,7 @@ func (s *Service) Login(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(res)
|
||||
}
|
||||
|
||||
// Logout handles user logout by invalidating the JWT and saving it to the database.
|
||||
func (s *Service) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
token, err := extractToken(r)
|
||||
if err != nil {
|
||||
@@ -109,28 +111,3 @@ func (s *Service) Logout(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *Service) Authenticated(next http.HandlerFunc, roles ...model.Role) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Our middleware logic goes here...
|
||||
token, err := extractToken(r)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := s.validateJWT(token)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// if roles specified check if satisfied
|
||||
if len(roles) > 0 && !slices.Contains(roles, claims.Role) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
r = writeToContext(r, &claims)
|
||||
next(w, r)
|
||||
})
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package auth
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -28,6 +27,7 @@ func (s *Service) createJWT(user *model.User) (token string, err error) {
|
||||
return jwt.NewWithClaims(jwt.SigningMethodHS512, claims).SignedString([]byte(s.cfg.Secret))
|
||||
}
|
||||
|
||||
// validateJWT returns the token Claims and if token ist invalid ErrJWTInvalid
|
||||
func (s *Service) validateJWT(tokenString string) (claims Claims, err error) {
|
||||
_, err = jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (any, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
@@ -40,12 +40,13 @@ func (s *Service) validateJWT(tokenString string) (claims Claims, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Println(claims)
|
||||
|
||||
if claims.ExpiresAt.Before(time.Now()) {
|
||||
err = ErrJWTInvalid
|
||||
return
|
||||
}
|
||||
|
||||
// check if user has logged out this token
|
||||
var invalidated bool
|
||||
err = s.db.Model(&model.InvalidJWT{}).
|
||||
Select("count(*) > 0").
|
||||
@@ -60,6 +61,7 @@ func (s *Service) validateJWT(tokenString string) (claims Claims, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// extractToken extracts the Bearer token from the request
|
||||
func extractToken(r *http.Request) (token string, err error) {
|
||||
tokenHeader := r.Header.Get("Authorization") // Grab the token from the header
|
||||
|
||||
|
34
backend/internal/auth/middleware.go
Normal file
34
backend/internal/auth/middleware.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"git.schreifuchs.ch/schreifuchs/ng-blog/backend/internal/model"
|
||||
)
|
||||
|
||||
// Authenticated: This function is a middleware that authenticates incoming HTTP requests using JWT tokens and role-based access control.
|
||||
func (s *Service) Authenticated(next http.HandlerFunc, roles ...model.Role) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Our middleware logic goes here...
|
||||
token, err := extractToken(r)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := s.validateJWT(token)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// if roles specified check if satisfied
|
||||
if len(roles) > 0 && !slices.Contains(roles, claims.Role) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
r = writeToContext(r, &claims)
|
||||
next(w, r)
|
||||
})
|
||||
}
|
@@ -11,6 +11,7 @@ import (
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
// Config defines a struct for configuration settings, often loaded from environment variables.
|
||||
type Config struct {
|
||||
Secret string `env:"SECRET"`
|
||||
ValidDuration time.Duration `env:"VALID_DURATION"`
|
||||
@@ -19,11 +20,13 @@ type Config struct {
|
||||
DefaultRole model.Role `env:"DEFAULT_ROLE"`
|
||||
}
|
||||
|
||||
// Service Represents a service with configuration and database connection.
|
||||
type Service struct {
|
||||
cfg *Config
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// New creates a new Service instance, initializing a default admin user and saving it to the database.
|
||||
func New(cfg *Config, db *gorm.DB) *Service {
|
||||
user := model.NewUser()
|
||||
var err error
|
||||
@@ -42,16 +45,20 @@ func New(cfg *Config, db *gorm.DB) *Service {
|
||||
}
|
||||
}
|
||||
|
||||
// Claims struct represents JWT claims, including role and user ID, extending the standard jwt.RegisteredClaims.
|
||||
type Claims struct {
|
||||
Role model.Role `json:"rl"`
|
||||
UserID uint `json:"uid"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// Login struct represents user login credentials with a name and password.
|
||||
type Login struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"Password"`
|
||||
}
|
||||
|
||||
// LoginResponse Represents the response from a login endpoint, containing a JWT token.
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
Reference in New Issue
Block a user