Files
golib/hwsauth/middleware.go

65 lines
1.7 KiB
Go

package hwsauth
import (
"context"
"net/http"
"slices"
"time"
"git.haelnorr.com/h/golib/hws"
"github.com/pkg/errors"
)
// Authenticate returns the main authentication middleware.
// This middleware validates JWT tokens, refreshes expired tokens, and adds
// the authenticated user to the request context.
//
// Example:
//
// server.AddMiddleware(auth.Authenticate())
func (auth *Authenticator[T, TX]) Authenticate() hws.Middleware {
return auth.server.NewMiddleware(auth.authenticate())
}
func (auth *Authenticator[T, TX]) authenticate() hws.MiddlewareFunc {
return func(w http.ResponseWriter, r *http.Request) (*http.Request, *hws.HWSError) {
if slices.Contains(auth.ignoredPaths, r.URL.Path) {
return r, nil
}
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
// Start the transaction
tx, err := auth.beginTx(ctx)
if err != nil {
return nil, &hws.HWSError{
Message: "Unable to start transaction",
StatusCode: http.StatusServiceUnavailable,
Error: errors.Wrap(err, "auth.beginTx"),
}
}
// Type assert to TX - safe because user's beginTx should return their TX type
txTyped, ok := tx.(TX)
if !ok {
return nil, &hws.HWSError{
Message: "Transaction type mismatch",
StatusCode: http.StatusInternalServerError,
Error: errors.Wrap(err, "TX type not ok"),
}
}
model, err := auth.getAuthenticatedUser(txTyped, w, r)
if err != nil {
tx.Rollback()
auth.logger.Debug().
Str("remote_addr", r.RemoteAddr).
Err(err).
Msg("Failed to authenticate user")
return r, nil
}
tx.Commit()
authContext := setAuthenticatedModel(r.Context(), model)
newReq := r.WithContext(authContext)
return newReq, nil
}
}