Full Fiber v3 example
Complete server combining credentials, OTP, bearer tokens, roles, and admin routes — based on the official fiberauth example.
main.go
package main
import (
"context"
"crypto/subtle"
"log"
"net/http"
"os"
"time"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/logger"
"github.com/izetmolla/fiberauth"
"github.com/izetmolla/goauth"
"github.com/izetmolla/goauth/adapters/memory"
"github.com/izetmolla/goauth/providers/credentials"
)
type account struct {
ID, Name, Email, Password string
Roles []string
}
var users = map[string]*account{
"alice@example.com": {ID: "u_alice", Name: "Alice", Email: "alice@example.com", Password: "password", Roles: []string{"user"}},
"admin@example.com": {ID: "u_admin", Name: "Admin", Email: "admin@example.com", Password: "password", Roles: []string{"user", "admin"}},
}
func main() {
auth, err := goauth.New(goauth.Config{
Secret: []string{os.Getenv("AUTH_SECRET")},
TrustHost: true,
Adapter: memory.New(),
Session: goauth.SessionConfig{Strategy: goauth.StrategyJWT},
Tokens: goauth.TokensConfig{
Enabled: true,
AlwaysReturn: true,
},
Providers: []goauth.Provider{
credentials.New(credentials.Options{
Authorize: authorize,
}),
otpProviderDemo(),
},
Callbacks: goauth.Callbacks{
JWT: jwtRoles,
},
Pages: goauth.Pages{VerifyRequest: "/check-email"},
})
if err != nil {
log.Fatal(err)
}
app := fiber.New()
app.Use(logger.New())
app.All("/auth/*", fiberauth.Handler(auth))
app.Get("/api/public", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "public"})
})
app.Get("/api/me", fiberauth.Protect(auth), func(c fiber.Ctx) error {
s := fiberauth.SessionFrom(c)
return c.JSON(fiber.Map{"user": s.User, "roles": s.Roles()})
})
admin := app.Group("/api/admin", fiberauth.Guard(auth, fiberauth.HasRole("admin")))
admin.Get("/users", func(c fiber.Ctx) error {
out := []fiber.Map{}
for _, u := range users {
out = append(out, fiber.Map{"email": u.Email, "roles": u.Roles})
}
return c.JSON(out)
})
log.Fatal(app.Listen(":3009"))
}
func authorize(_ context.Context, c map[string]string, _ *http.Request) (*goauth.User, error) {
u := users[c["email"]]
if u == nil || subtle.ConstantTimeCompare([]byte(u.Password), []byte(c["password"])) != 1 {
return nil, nil
}
return &goauth.User{ID: u.ID, Name: u.Name, Email: u.Email}, nil
}
func jwtRoles(_ context.Context, p goauth.JWTCallbackParams) (goauth.JWT, error) {
for _, u := range users {
if u.ID == p.User.ID || u.Email == p.User.Email {
p.Token["roles"] = u.Roles
}
}
return p.Token, nil
}
func otpProviderDemo() goauth.Provider {
return &goauth.EmailProvider{
ProviderID: "otp",
DisplayName: "Email code",
MaxAge: 600,
GenerateVerificationToken: func() string { return "123456" },
SendVerificationRequest: func(_ context.Context, p goauth.SendVerificationRequestParams) error {
log.Printf("[otp] %s -> %s", p.Identifier, p.Token)
return nil
},
}
}
Try it
# Terminal 1 — backend
cd fiberauth/example && go run .
# Sign in (token)
curl -s -X POST http://localhost:3009/auth/callback/credentials \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'X-Auth-Flow: token' \
-d 'email=admin@example.com&password=password' | jq .
# Call protected API
curl -s http://localhost:3009/api/me \
-H "Authorization: Bearer ACCESS_TOKEN" | jq .
# Admin only
curl -s http://localhost:3009/api/admin/users \
-H "Authorization: Bearer ACCESS_TOKEN" | jq .
# OTP: POST /auth/signin/otp then POST /auth/callback/otp with code 123456
Clone the official repo
git clone https://github.com/izetmolla/fiberauth.git
cd fiberauth/example
export AUTH_SECRET="your-secret-at-least-32-chars-long!!"
go run .
Pair with a React SPA using the Vite proxy pattern from the example README in the repo.