package hwsauth import ( "context" "git.haelnorr.com/h/golib/hws" "net/http" "slices" "time" ) // 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: err} } // 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: err} } 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 } }