Files
golib/hwsauth/protectpage.go

105 lines
3.2 KiB
Go

package hwsauth
import (
"net/http"
"time"
"git.haelnorr.com/h/golib/hws"
)
// LoginReq returns a middleware that requires the user to be authenticated.
// If the user is not authenticated, it returns a 401 Unauthorized error page.
//
// Example:
//
// protectedHandler := auth.LoginReq(http.HandlerFunc(dashboardHandler))
// server.AddRoute("GET", "/dashboard", protectedHandler)
func (auth *Authenticator[T, TX]) LoginReq(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, ok := getAuthorizedModel[T](r.Context())
if !ok {
page, err := auth.errorPage(http.StatusUnauthorized)
if err != nil {
auth.server.ThrowError(w, r, hws.HWSError{
Error: err,
Message: "Failed to get valid error page",
StatusCode: http.StatusInternalServerError,
RenderErrorPage: true,
})
}
err = page.Render(r.Context(), w)
if err != nil {
auth.server.ThrowError(w, r, hws.HWSError{
Error: err,
Message: "Failed to render error page",
StatusCode: http.StatusInternalServerError,
RenderErrorPage: true,
})
}
return
}
next.ServeHTTP(w, r)
})
}
// LogoutReq returns a middleware that redirects authenticated users to the landing page.
// Use this for login and registration pages to prevent logged-in users from accessing them.
//
// Example:
//
// loginPageHandler := auth.LogoutReq(http.HandlerFunc(showLoginPage))
// server.AddRoute("GET", "/login", loginPageHandler)
func (auth *Authenticator[T, TX]) LogoutReq(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, ok := getAuthorizedModel[T](r.Context())
if ok {
http.Redirect(w, r, auth.LandingPage, http.StatusFound)
return
}
next.ServeHTTP(w, r)
})
}
// FreshReq returns a middleware that requires a fresh authentication token.
// If the token is not fresh (recently issued), it returns a 444 status code.
// Use this for sensitive operations like password changes or account deletions.
//
// Example:
//
// changePasswordHandler := auth.FreshReq(http.HandlerFunc(handlePasswordChange))
// server.AddRoute("POST", "/change-password", changePasswordHandler)
//
// The 444 status code can be used by the client to prompt for re-authentication.
func (auth *Authenticator[T, TX]) FreshReq(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
model, ok := getAuthorizedModel[T](r.Context())
if !ok {
page, err := auth.errorPage(http.StatusUnauthorized)
if err != nil {
auth.server.ThrowError(w, r, hws.HWSError{
Error: err,
Message: "Failed to get valid error page",
StatusCode: http.StatusInternalServerError,
RenderErrorPage: true,
})
}
err = page.Render(r.Context(), w)
if err != nil {
auth.server.ThrowError(w, r, hws.HWSError{
Error: err,
Message: "Failed to render error page",
StatusCode: http.StatusInternalServerError,
RenderErrorPage: true,
})
}
return
}
isFresh := time.Now().Before(time.Unix(model.fresh, 0))
if !isFresh {
w.WriteHeader(444)
return
}
next.ServeHTTP(w, r)
})
}