// Package hwsauth provides JWT-based authentication middleware for the hws web framework. // // # Overview // // hwsauth integrates with the hws web server to provide secure, stateless authentication // using JSON Web Tokens (JWT). It supports both access and refresh tokens, automatic // token rotation, and flexible transaction handling compatible with any database or ORM. // // # Key Features // // - JWT-based authentication with access and refresh tokens // - Automatic token rotation and refresh // - Generic over user model and transaction types // - ORM-agnostic transaction handling // - Environment variable configuration // - Middleware for protecting routes // - Context-based user retrieval // - Optional SSL cookie security // // # Quick Start // // First, define your user model: // // type User struct { // UserID int // Username string // Email string // } // // func (u User) ID() int { // return u.UserID // } // // Configure the authenticator using environment variables or programmatically: // // // Option 1: Load from environment variables // cfg, err := hwsauth.ConfigFromEnv() // if err != nil { // log.Fatal(err) // } // // // Option 2: Create config manually // cfg := &hwsauth.Config{ // SSL: true, // TrustedHost: "https://example.com", // SecretKey: "your-secret-key", // AccessTokenExpiry: 5, // 5 minutes // RefreshTokenExpiry: 1440, // 1 day // TokenFreshTime: 5, // 5 minutes // LandingPage: "/dashboard", // } // // Create the authenticator: // // // Define how to begin transactions // beginTx := func(ctx context.Context) (hwsauth.DBTransaction, error) { // return db.BeginTx(ctx, nil) // } // // // Define how to load users from the database // loadUser := func(ctx context.Context, tx *sql.Tx, id int) (User, error) { // var user User // err := tx.QueryRowContext(ctx, "SELECT id, username, email FROM users WHERE id = ?", id). // Scan(&user.UserID, &user.Username, &user.Email) // return user, err // } // // // Create the authenticator // auth, err := hwsauth.NewAuthenticator[User, *sql.Tx]( // cfg, // loadUser, // server, // beginTx, // logger, // errorPage, // ) // if err != nil { // log.Fatal(err) // } // // # Middleware // // Use the Authenticate middleware to protect routes: // // // Apply to all routes // server.AddMiddleware(auth.Authenticate()) // // // Ignore specific paths // auth.IgnorePaths("/login", "/register", "/public") // // Use route guards for specific protection requirements: // // // LoginReq: Requires user to be authenticated // protectedHandler := auth.LoginReq(myHandler) // // // LogoutReq: Redirects authenticated users (for login/register pages) // loginHandler := auth.LogoutReq(loginPageHandler) // // // FreshReq: Requires fresh authentication (for sensitive operations) // changePasswordHandler := auth.FreshReq(changePasswordHandler) // // # Login and Logout // // To log a user in: // // func loginHandler(w http.ResponseWriter, r *http.Request) { // // Validate credentials... // user := getUserFromDatabase(username) // // // Log the user in (sets JWT cookies) // err := auth.Login(w, r, user, rememberMe) // if err != nil { // // Handle error // } // // http.Redirect(w, r, "/dashboard", http.StatusSeeOther) // } // // To log a user out: // // func logoutHandler(w http.ResponseWriter, r *http.Request) { // tx, _ := db.BeginTx(r.Context(), nil) // defer tx.Rollback() // // err := auth.Logout(tx, w, r) // if err != nil { // // Handle error // } // // tx.Commit() // http.Redirect(w, r, "/", http.StatusSeeOther) // } // // # Retrieving the Current User // // Access the authenticated user from the request context: // // func dashboardHandler(w http.ResponseWriter, r *http.Request) { // user := auth.CurrentModel(r.Context()) // if user.ID() == 0 { // // User not authenticated // return // } // // fmt.Fprintf(w, "Welcome, %s!", user.Username) // } // // # ORM Support // // hwsauth works with any ORM that implements the DBTransaction interface. // // GORM Example: // // beginTx := func(ctx context.Context) (hwsauth.DBTransaction, error) { // return gormDB.WithContext(ctx).Begin().Statement.ConnPool.(*sql.Tx), nil // } // // loadUser := func(ctx context.Context, tx *gorm.DB, id int) (User, error) { // var user User // err := tx.First(&user, id).Error // return user, err // } // // auth, err := hwsauth.NewAuthenticator[User, *gorm.DB](...) // // Bun Example: // // beginTx := func(ctx context.Context) (hwsauth.DBTransaction, error) { // return bunDB.BeginTx(ctx, nil) // } // // loadUser := func(ctx context.Context, tx bun.Tx, id int) (User, error) { // var user User // err := tx.NewSelect().Model(&user).Where("id = ?", id).Scan(ctx) // return user, err // } // // auth, err := hwsauth.NewAuthenticator[User, bun.Tx](...) // // # Environment Variables // // The following environment variables are supported: // // - HWSAUTH_SSL: Enable SSL mode (default: false) // - HWSAUTH_TRUSTED_HOST: Trusted host for SSL (required if SSL is true) // - HWSAUTH_SECRET_KEY: Secret key for signing tokens (required) // - HWSAUTH_ACCESS_TOKEN_EXPIRY: Access token expiry in minutes (default: 5) // - HWSAUTH_REFRESH_TOKEN_EXPIRY: Refresh token expiry in minutes (default: 1440) // - HWSAUTH_TOKEN_FRESH_TIME: Token fresh time in minutes (default: 5) // - HWSAUTH_LANDING_PAGE: Landing page for logged in users (default: "/profile") // - HWSAUTH_JWT_TABLE_NAME: Custom JWT table name (optional) // - HWSAUTH_DATABASE_TYPE: Database type (e.g., "postgres", "mysql") // - HWSAUTH_DATABASE_VERSION: Database version (e.g., "15") // // # Security Considerations // // - Always use SSL in production (set HWSAUTH_SSL=true) // - Use strong, randomly generated secret keys // - Set appropriate token expiry times based on your security requirements // - Use FreshReq middleware for sensitive operations (password changes, etc.) // - Store refresh tokens securely in HTTP-only cookies // // # Type Parameters // // hwsauth uses Go generics for type safety: // // - T Model: Your user model type (must implement the Model interface) // - TX DBTransaction: Your transaction type (must implement DBTransaction interface) // // This allows compile-time type checking and eliminates the need for type assertions // when working with your user models. package hwsauth