updated docs
58
HWS.md
58
HWS.md
@@ -700,18 +700,20 @@ server.LoggerIgnorePaths("/static/", "/assets/", "/favicon.ico")
|
|||||||
### Starting the Server
|
### Starting the Server
|
||||||
|
|
||||||
```go
|
```go
|
||||||
ctx := context.Background()
|
// Start the server (runs in background goroutine)
|
||||||
|
logger.Debug().Msg("Starting up the HTTP server")
|
||||||
err := server.Start(ctx)
|
err := server.Start(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Wrap(err, "server.Start")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `Start` method:
|
The `Start` method:
|
||||||
1. Validates that routes have been added
|
1. Validates that routes have been added
|
||||||
2. Applies middleware if not already done
|
2. Applies middleware if not already done
|
||||||
3. Starts the HTTP server in a goroutine
|
3. Starts the HTTP server in a background goroutine
|
||||||
4. Begins polling for server readiness
|
4. Returns immediately, allowing your code to continue
|
||||||
|
5. Server listens for context cancellation to shutdown
|
||||||
|
|
||||||
### Checking Server Status
|
### Checking Server Status
|
||||||
|
|
||||||
@@ -732,30 +734,44 @@ addr := server.Addr() // e.g., "127.0.0.1:3000"
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func run() error {
|
||||||
// ... server setup ...
|
// Create context that listens for interrupt signals
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
server.Start(ctx)
|
|
||||||
|
|
||||||
// Wait for interrupt signal
|
|
||||||
sigChan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
||||||
<-sigChan
|
|
||||||
|
|
||||||
// Shutdown with timeout
|
|
||||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if err := server.Shutdown(shutdownCtx); err != nil {
|
// ... server setup ...
|
||||||
logger.Fatal("Shutdown failed", err)
|
|
||||||
|
// Start server
|
||||||
|
err := server.Start(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("Server shutdown complete")
|
logger.Info().Msgf("Server started on %s", server.Addr())
|
||||||
|
|
||||||
|
// Handle graceful shutdown
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Go(func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
|
||||||
|
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := server.Shutdown(shutdownCtx); err != nil {
|
||||||
|
logger.Error().Err(err).Msg("Graceful shutdown failed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
logger.Info().Msg("Server shutdown complete")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1160,3 +1176,5 @@ Check that:
|
|||||||
- [Issue Tracker](https://git.haelnorr.com/h/golib/hws/issues)
|
- [Issue Tracker](https://git.haelnorr.com/h/golib/hws/issues)
|
||||||
- [Examples](https://git.haelnorr.com/h/golib/hws/tree/master/examples)
|
- [Examples](https://git.haelnorr.com/h/golib/hws/tree/master/examples)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
285
HWSAuth.md
285
HWSAuth.md
@@ -4,7 +4,9 @@ JWT-based authentication middleware for the hws web framework.
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
`hwsauth` provides a complete authentication solution for hws web applications using JSON Web Tokens (JWT). It handles access tokens, refresh tokens, automatic token rotation, and integrates seamlessly with any database or ORM.
|
`hwsauth` provides a complete authentication solution for HWS web applications using JSON Web Tokens (JWT). It handles access tokens, refresh tokens, automatic token rotation, and integrates seamlessly with any database or ORM.
|
||||||
|
|
||||||
|
**Database Flexibility**: hwsauth works with any database backend - from the standard library's `database/sql` to popular ORMs like GORM, Bun, SQLC, and Ent. The examples below use Bun ORM, but you can easily adapt them to your preferred database solution. See the [ORM Integration](#orm-integration) section for examples with different backends.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -337,116 +339,107 @@ Function to create database transactions.
|
|||||||
|
|
||||||
## Complete Examples
|
## Complete Examples
|
||||||
|
|
||||||
|
The examples below focus on HWSAuth-specific functionality using basic error handling. For complete HWS server setup, middleware configuration, and advanced error handling patterns, see the [HWS documentation](./HWS.md).
|
||||||
|
|
||||||
### Route Setup with LoginReq, LogoutReq, and FreshReq
|
### Route Setup with LoginReq, LogoutReq, and FreshReq
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func setupRoutes(
|
routes := []hws.Route{
|
||||||
server *hws.Server,
|
// Public routes - no authentication required
|
||||||
auth *hwsauth.Authenticator[*User, bun.Tx],
|
{
|
||||||
db *bun.DB,
|
Path: "/",
|
||||||
) error {
|
Method: hws.MethodGET,
|
||||||
routes := []hws.Route{
|
Handler: homeHandler,
|
||||||
// Public routes - no authentication required
|
},
|
||||||
{
|
|
||||||
Path: "/",
|
// LogoutReq - requires user to NOT be logged in
|
||||||
Method: hws.MethodGET,
|
// Redirects authenticated users away
|
||||||
Handler: homeHandler,
|
{
|
||||||
},
|
Path: "/login",
|
||||||
|
Method: hws.MethodGET,
|
||||||
// LogoutReq - requires user to NOT be logged in
|
Handler: auth.LogoutReq(loginPageHandler),
|
||||||
// Redirects authenticated users away
|
},
|
||||||
{
|
{
|
||||||
Path: "/login",
|
Path: "/login",
|
||||||
Method: hws.MethodGET,
|
Method: hws.MethodPOST,
|
||||||
Handler: auth.LogoutReq(loginPageHandler),
|
Handler: auth.LogoutReq(loginSubmitHandler(auth, db)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Path: "/login",
|
Path: "/register",
|
||||||
Method: hws.MethodPOST,
|
Method: hws.MethodGET,
|
||||||
Handler: auth.LogoutReq(loginSubmitHandler(server, auth, db)),
|
Handler: auth.LogoutReq(registerPageHandler),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Path: "/register",
|
Path: "/register",
|
||||||
Method: hws.MethodGET,
|
Method: hws.MethodPOST,
|
||||||
Handler: auth.LogoutReq(registerPageHandler),
|
Handler: auth.LogoutReq(registerSubmitHandler(auth, db)),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Path: "/register",
|
// Logout - accessible to anyone
|
||||||
Method: hws.MethodPOST,
|
{
|
||||||
Handler: auth.LogoutReq(registerSubmitHandler(server, auth, db)),
|
Path: "/logout",
|
||||||
},
|
Method: hws.MethodPOST,
|
||||||
|
Handler: logoutHandler(auth, db),
|
||||||
// Logout - accessible to anyone
|
},
|
||||||
{
|
|
||||||
Path: "/logout",
|
// LoginReq - requires user to be authenticated
|
||||||
Method: hws.MethodPOST,
|
{
|
||||||
Handler: logoutHandler(server, auth, db),
|
Path: "/dashboard",
|
||||||
},
|
Method: hws.MethodGET,
|
||||||
|
Handler: auth.LoginReq(dashboardHandler),
|
||||||
// LoginReq - requires user to be authenticated
|
},
|
||||||
{
|
{
|
||||||
Path: "/dashboard",
|
Path: "/profile",
|
||||||
Method: hws.MethodGET,
|
Method: hws.MethodGET,
|
||||||
Handler: auth.LoginReq(dashboardHandler),
|
Handler: auth.LoginReq(profileHandler),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Path: "/profile",
|
// Reauthentication endpoint for sensitive operations
|
||||||
Method: hws.MethodGET,
|
{
|
||||||
Handler: auth.LoginReq(profileHandler),
|
Path: "/reauthenticate",
|
||||||
},
|
Method: hws.MethodPOST,
|
||||||
|
Handler: auth.LoginReq(reauthenticateHandler(auth, db)),
|
||||||
// Reauthentication endpoint for sensitive operations
|
},
|
||||||
{
|
|
||||||
Path: "/reauthenticate",
|
// FreshReq - requires fresh authentication (recent login)
|
||||||
Method: hws.MethodPOST,
|
// Used for sensitive operations like changing password/username
|
||||||
Handler: auth.LoginReq(reauthenticateHandler(server, auth, db)),
|
{
|
||||||
},
|
Path: "/change-password",
|
||||||
|
Method: hws.MethodPOST,
|
||||||
// FreshReq - requires fresh authentication (recent login)
|
Handler: auth.LoginReq(auth.FreshReq(changePasswordHandler(auth, db))),
|
||||||
// Used for sensitive operations like changing password/username
|
},
|
||||||
{
|
{
|
||||||
Path: "/change-password",
|
Path: "/change-username",
|
||||||
Method: hws.MethodPOST,
|
Method: hws.MethodPOST,
|
||||||
Handler: auth.LoginReq(auth.FreshReq(changePasswordHandler(server, auth, db))),
|
Handler: auth.LoginReq(auth.FreshReq(changeUsernameHandler(auth, db))),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Path: "/change-username",
|
// Regular authenticated routes (no fresh token required)
|
||||||
Method: hws.MethodPOST,
|
{
|
||||||
Handler: auth.LoginReq(auth.FreshReq(changeUsernameHandler(server, auth, db))),
|
Path: "/change-bio",
|
||||||
},
|
Method: hws.MethodPOST,
|
||||||
|
Handler: auth.LoginReq(changeBioHandler(auth, db)),
|
||||||
// Regular authenticated routes (no fresh token required)
|
},
|
||||||
{
|
|
||||||
Path: "/change-bio",
|
|
||||||
Method: hws.MethodPOST,
|
|
||||||
Handler: auth.LoginReq(changeBioHandler(server, auth, db)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return server.AddRoutes(routes...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server.AddRoutes(routes...)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Login Handler with Transaction Management
|
### Login Handler with Transaction Management
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func loginSubmitHandler(
|
func loginSubmitHandler(
|
||||||
server *hws.Server,
|
|
||||||
auth *hwsauth.Authenticator[*User, bun.Tx],
|
auth *hwsauth.Authenticator[*User, bun.Tx],
|
||||||
db *bun.DB,
|
db *bun.DB,
|
||||||
) http.Handler {
|
) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Start database transaction
|
// Start database transaction
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Login failed", http.StatusServiceUnavailable)
|
||||||
StatusCode: http.StatusServiceUnavailable,
|
|
||||||
Message: "Login failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
@@ -475,19 +468,13 @@ func loginSubmitHandler(
|
|||||||
// Login user - sets authentication cookies
|
// Login user - sets authentication cookies
|
||||||
err = auth.Login(w, r, user, rememberMe)
|
err = auth.Login(w, r, user, rememberMe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Login failed", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Login failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
|
|
||||||
// Redirect to dashboard or previous page
|
|
||||||
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -495,21 +482,16 @@ func loginSubmitHandler(
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func registerSubmitHandler(
|
func registerSubmitHandler(
|
||||||
server *hws.Server,
|
|
||||||
auth *hwsauth.Authenticator[*User, bun.Tx],
|
auth *hwsauth.Authenticator[*User, bun.Tx],
|
||||||
db *bun.DB,
|
db *bun.DB,
|
||||||
) http.Handler {
|
) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Registration failed", http.StatusServiceUnavailable)
|
||||||
StatusCode: http.StatusServiceUnavailable,
|
|
||||||
Message: "Registration failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
@@ -528,11 +510,7 @@ func registerSubmitHandler(
|
|||||||
// Check if username is unique
|
// Check if username is unique
|
||||||
exists, err := usernameExists(ctx, tx, username)
|
exists, err := usernameExists(ctx, tx, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Registration failed", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Registration failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if exists {
|
if exists {
|
||||||
@@ -543,11 +521,7 @@ func registerSubmitHandler(
|
|||||||
// Create user
|
// Create user
|
||||||
user, err := createUser(ctx, tx, username, password)
|
user, err := createUser(ctx, tx, username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Registration failed", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Registration failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,17 +529,13 @@ func registerSubmitHandler(
|
|||||||
rememberMe := r.FormValue("remember-me") == "on"
|
rememberMe := r.FormValue("remember-me") == "on"
|
||||||
err = auth.Login(w, r, user, rememberMe)
|
err = auth.Login(w, r, user, rememberMe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Login failed", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Login failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -573,21 +543,16 @@ func registerSubmitHandler(
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func logoutHandler(
|
func logoutHandler(
|
||||||
server *hws.Server,
|
|
||||||
auth *hwsauth.Authenticator[*User, bun.Tx],
|
auth *hwsauth.Authenticator[*User, bun.Tx],
|
||||||
db *bun.DB,
|
db *bun.DB,
|
||||||
) http.Handler {
|
) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Logout failed", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Logout failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
@@ -595,17 +560,13 @@ func logoutHandler(
|
|||||||
// Logout - clears cookies and revokes tokens in database
|
// Logout - clears cookies and revokes tokens in database
|
||||||
err = auth.Logout(tx, w, r)
|
err = auth.Logout(tx, w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Logout failed", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Logout failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -613,21 +574,16 @@ func logoutHandler(
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func reauthenticateHandler(
|
func reauthenticateHandler(
|
||||||
server *hws.Server,
|
|
||||||
auth *hwsauth.Authenticator[*User, bun.Tx],
|
auth *hwsauth.Authenticator[*User, bun.Tx],
|
||||||
db *bun.DB,
|
db *bun.DB,
|
||||||
) http.Handler {
|
) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Reauthentication failed", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Reauthentication failed",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
@@ -648,17 +604,13 @@ func reauthenticateHandler(
|
|||||||
// Refresh tokens to make them "fresh"
|
// Refresh tokens to make them "fresh"
|
||||||
err = auth.RefreshAuthTokens(tx, w, r)
|
err = auth.RefreshAuthTokens(tx, w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Failed to refresh tokens", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Failed to refresh tokens",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -688,21 +640,16 @@ func dashboardHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
func changePasswordHandler(
|
func changePasswordHandler(
|
||||||
server *hws.Server,
|
|
||||||
auth *hwsauth.Authenticator[*User, bun.Tx],
|
auth *hwsauth.Authenticator[*User, bun.Tx],
|
||||||
db *bun.DB,
|
db *bun.DB,
|
||||||
) http.Handler {
|
) http.HandlerFunc {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Failed to change password", http.StatusServiceUnavailable)
|
||||||
StatusCode: http.StatusServiceUnavailable,
|
|
||||||
Message: "Failed to change password",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
@@ -722,17 +669,13 @@ func changePasswordHandler(
|
|||||||
// Update password
|
// Update password
|
||||||
err = user.UpdatePassword(ctx, tx, newPassword)
|
err = user.UpdatePassword(ctx, tx, newPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.ThrowError(w, r, hws.HWSError{
|
http.Error(w, "Failed to change password", http.StatusInternalServerError)
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "Failed to change password",
|
|
||||||
Error: err,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
http.Redirect(w, r, "/profile", http.StatusSeeOther)
|
http.Redirect(w, r, "/profile", http.StatusSeeOther)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -773,3 +716,11 @@ func changePasswordHandler(
|
|||||||
- [Issue Tracker](https://git.haelnorr.com/h/golib/hwsauth/issues)
|
- [Issue Tracker](https://git.haelnorr.com/h/golib/hwsauth/issues)
|
||||||
- [Examples](https://git.haelnorr.com/h/golib/hwsauth/tree/master/examples)
|
- [Examples](https://git.haelnorr.com/h/golib/hwsauth/tree/master/examples)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user