diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/README.md new file mode 100644 index 00000000000..af1fae0a824 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/README.md @@ -0,0 +1,34 @@ +--- +title: 👋 Welcome +sidebar_position: 1 +--- + +
+ Fiber + Fiber +
+ + +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Repository for third party middlewares with dependencies. + +
+ +## 📑 Middleware Implementations + +* [Casbin](./casbin/README.md) +* [Fiberi18n](./fiberi18n/README.md) +* [Fibersentry](./fibersentry/README.md) +* [Fiberzap](./fiberzap/README.md) +* [Fiberzerolog](./fiberzerolog/README.md) +* [JWT](./jwt/README.md) +* [NewRelic](./fibernewrelic/README.md) +* [Open Policy Agent](./opafiber/README.md) +* [Otelfiber (OpenTelemetry)](./otelfiber/README.md) +* [Paseto](./paseto/README.md) +* [Swagger](./swagger/README.md) +* [Websocket](./websocket/README.md) diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/casbin/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/casbin/README.md new file mode 100644 index 00000000000..2010a129bc6 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/casbin/README.md @@ -0,0 +1,151 @@ +--- +id: casbin +title: Casbin +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=casbin*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Casbin middleware for Fiber. + +### Install +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/casbin +``` +choose an adapter from [here](https://casbin.org/docs/en/adapters) +``` +go get -u github.com/casbin/xorm-adapter +``` + +### Signature +```go +casbin.New(config ...casbin.Config) *casbin.Middleware +``` + +### Config +| Property | Type | Description | Default | +| :--- | :--- | :--- | :--- | +| ModelFilePath | `string` | Model file path | `"./model.conf"` | +| PolicyAdapter | `persist.Adapter` | Database adapter for policies | `./policy.csv` | +| Enforcer | `*casbin.Enforcer` | Custom casbin enforcer | `Middleware generated enforcer using ModelFilePath & PolicyAdapter` | +| Lookup | `func(*fiber.Ctx) string` | Look up for current subject | `""` | +| Unauthorized | `func(*fiber.Ctx) error` | Response body for unauthorized responses | `Unauthorized` | +| Forbidden | `func(*fiber.Ctx) error` | Response body for forbidden responses | `Forbidden` | + +### Examples +- [Gorm Adapter](https://github.com/svcg/-fiber_casbin_demo) +- [File Adapter](https://github.com/gofiber/contrib/casbin/tree/master/example) + +### CustomPermission + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/casbin" + _ "github.com/go-sql-driver/mysql" + "github.com/casbin/xorm-adapter/v2" +) + +func main() { + app := fiber.New() + + authz := casbin.New(casbin.Config{ + ModelFilePath: "path/to/rbac_model.conf", + PolicyAdapter: xormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/"), + Lookup: func(c *fiber.Ctx) string { + // fetch authenticated user subject + }, + }) + + app.Post("/blog", + authz.RequiresPermissions([]string{"blog:create"}, casbin.WithValidationRule(casbin.MatchAllRule)), + func(c *fiber.Ctx) error { + // your handler + }, + ) + + app.Delete("/blog/:id", + authz.RequiresPermissions([]string{"blog:create", "blog:delete"}, casbin.WithValidationRule(casbin.AtLeastOneRule)), + func(c *fiber.Ctx) error { + // your handler + }, + ) + + app.Listen(":8080") +} +``` + +### RoutePermission + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/casbin" + _ "github.com/go-sql-driver/mysql" + "github.com/casbin/xorm-adapter/v2" +) + +func main() { + app := fiber.New() + + authz := casbin.New(casbin.Config{ + ModelFilePath: "path/to/rbac_model.conf", + PolicyAdapter: xormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/"), + Lookup: func(c *fiber.Ctx) string { + // fetch authenticated user subject + }, + }) + + // check permission with Method and Path + app.Post("/blog", + authz.RoutePermission(), + func(c *fiber.Ctx) error { + // your handler + }, + ) + + app.Listen(":8080") +} +``` + +### RoleAuthorization + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/casbin" + _ "github.com/go-sql-driver/mysql" + "github.com/casbin/xorm-adapter/v2" +) + +func main() { + app := fiber.New() + + authz := casbin.New(casbin.Config{ + ModelFilePath: "path/to/rbac_model.conf", + PolicyAdapter: xormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/"), + Lookup: func(c *fiber.Ctx) string { + // fetch authenticated user subject + }, + }) + + app.Put("/blog/:id", + authz.RequiresRoles([]string{"admin"}), + func(c *fiber.Ctx) error { + // your handler + }, + ) + + app.Listen(":8080") +} +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/fiberi18n/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/fiberi18n/README.md new file mode 100644 index 00000000000..9a38e85d821 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/fiberi18n/README.md @@ -0,0 +1,77 @@ +--- +id: fiberi18n +title: Fiberi18n +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fiberi18n*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[go-i18n](https://github.com/nicksnyder/go-i18n) support for Fiber. + +### Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fiberi18n +``` + +### Signature + +``` +fiberi18n.New(config ...*Config) fiber.Handler +``` + +### Config + +| Property | Type | Description | Default | +| ---------------- | ------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| Next | `func(c *fiber.Ctx) bool` | A function to skip this middleware when returned `true`. | `nil` | +| RootPath | `string` | The i18n template folder path. | `"./example/localize"` | +| AcceptLanguages | `[]language.Tag` | A collection of languages that can be processed. | `[]language.Tag{language.Chinese, language.English}` | +| FormatBundleFile | `string` | The type of the template file. | `"yaml"` | +| DefaultLanguage | `language.Tag` | The default returned language type. | `language.English` | +| Loader | `Loader` | The implementation of the Loader interface, which defines how to read the file. We provide both os.ReadFile and embed.FS.ReadFile. | `LoaderFunc(os.ReadFile)` | +| UnmarshalFunc | `i18n.UnmarshalFunc` | The function used for decoding template files. | `yaml.Unmarshal` | +| LangHandler | `func(ctx *fiber.Ctx, defaultLang string) string` | Used to get the kind of language handled by *fiber.Ctx and defaultLang. | Retrieved from the request header `Accept-Language` or query parameter `lang`. | + +### Example + +```go +package main + +import ( + "github.com/gofiber/contrib/fiberi18n" + "github.com/gofiber/fiber/v2" + "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" +) + +func main() { + app := fiber.New() + app.Use( + fiberi18n.New(&fiberi18n.Config{ + RootPath: "./example/localize", + AcceptLanguages: []language.Tag{language.Chinese, language.English}, + DefaultLanguage: language.Chinese, + }), + ) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString(fiberi18n.MustGetMessage("welcome")) + }) + app.Get("/:name", func(ctx *fiber.Ctx) error { + return ctx.SendString(fiberi18n.MustGetMessage(&i18n.LocalizeConfig{ + MessageID: "welcomeWithName", + TemplateData: map[string]string{ + "name": ctx.Params("name"), + }, + })) + }) + app.Listen("127.0.0.1:3000") +} +``` + diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/fibernewrelic/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/fibernewrelic/README.md new file mode 100644 index 00000000000..398560f267d --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/fibernewrelic/README.md @@ -0,0 +1,99 @@ +--- +id: fibernewrelic +title: Fibernewrelic +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fibernewrelic*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[NewRelic](https://github.com/newrelic/go-agent) support for Fiber. + +### Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fibernewrelic +``` + +### Signature + +```go +fibernewrelic.New(config fibernewrelic.Config) fiber.Handler +``` + +### Config + +| Property | Type | Description | Default | +|:------------------|:-----------------|:---------------------------------------|:---------------| +| License | `string` | Required - New Relic License Key | `""` | +| AppName | `string` | New Relic Application Name | `fiber-api` | +| Enabled | `bool` | Enable/Disable New Relic | `false` | +| ~~TransportType~~ | ~~`string`~~ | ~~Can be HTTP or HTTPS~~ (Deprecated) | ~~`"HTTP"`~~ | +| Application | `Application` | Existing New Relic App | `nil` | +| ErrorStatusCodeHandler | `func(c *fiber.Ctx, err error) int` | If you want to change newrelic status code, you can use it. | `DefaultErrorStatusCodeHandler` | + +### Usage + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fibernewrelic" +) + +func main() { + app := fiber.New() + + app.Get("/", func(ctx *fiber.Ctx) error { + return ctx.SendStatus(200) + }) + + cfg := fibernewrelic.Config{ + License: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + AppName: "MyCustomApi", + Enabled: true, + } + + app.Use(fibernewrelic.New(cfg)) + + app.Listen(":8080") +} +``` + +### Usage with existing New Relic application + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fibernewrelic" + "github.com/newrelic/go-agent/v3/newrelic" +) + +func main() { + newrelicApp, err := newrelic.NewApplication( + newrelic.ConfigAppName("MyCustomApi"), + newrelic.ConfigLicense("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + newrelic.ConfigEnabled(true), + ) + + app := fiber.New() + + app.Get("/", func(ctx *fiber.Ctx) error { + return ctx.SendStatus(200) + }) + + cfg := fibernewrelic.Config{ + Application: newrelicApp + } + + app.Use(fibernewrelic.New(cfg)) + + app.Listen(":8080") +} +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/fibersentry/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/fibersentry/README.md new file mode 100644 index 00000000000..245a082cc0e --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/fibersentry/README.md @@ -0,0 +1,124 @@ +--- +id: fibersentry +title: Fibersentry +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fibersentry*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Sentry](https://sentry.io/) support for Fiber. + +### Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fibersentry +go get -u github.com/getsentry/sentry-go +``` + +### Signature + +``` +fibersentry.New(config ...Config) fiber.Handler +``` + +### Config + +| Property | Type | Description | Default | +| :------------- | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------ | +| Repanic| `bool` | Repanic configures whether Sentry should repanic after recovery. Set to true, if [Recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) middleware is used. | `false` | +| WaitForDelivery| `bool` | WaitForDelivery configures whether you want to block the request before moving forward with the response. If [Recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) middleware is used, it's safe to either skip this option or set it to false. | `false` | +| Timeout | `time.Duration` | Timeout for the event delivery requests. | `time.Second * 2` | + + +### Usage + +`fibersentry` attaches an instance of `*sentry.Hub` (https://godoc.org/github.com/getsentry/sentry-go#Hub) to the request's context, which makes it available throughout the rest of the request's lifetime. +You can access it by using the `fibersentry.GetHubFromContext()` method on the context itself in any of your proceeding middleware and routes. +And it should be used instead of the global `sentry.CaptureMessage`, `sentry.CaptureException`, or any other calls, as it keeps the separation of data between the requests. + +**Keep in mind that `*sentry.Hub` won't be available in middleware attached before to `fibersentry`!** + +```go +package main + +import ( + "fmt" + "log" + + "github.com/getsentry/sentry-go" + "github.com/gofiber/contrib/fibersentry" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +func main() { + _ = sentry.Init(sentry.ClientOptions{ + Dsn: "", + BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + if hint.Context != nil { + if c, ok := hint.Context.Value(sentry.RequestContextKey).(*fiber.Ctx); ok { + // You have access to the original Context if it panicked + fmt.Println(utils.ImmutableString(c.Hostname())) + } + } + fmt.Println(event) + return event + }, + Debug: true, + AttachStacktrace: true, + }) + + app := fiber.New() + + app.Use(fibersentry.New(fibersentry.Config{ + Repanic: true, + WaitForDelivery: true, + })) + + enhanceSentryEvent := func(c *fiber.Ctx) error { + if hub := fibersentry.GetHubFromContext(c); hub != nil { + hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt") + } + return c.Next() + } + + app.All("/foo", enhanceSentryEvent, func(c *fiber.Ctx) error { + panic("y tho") + }) + + app.All("/", func(c *fiber.Ctx) error { + if hub := fibersentry.GetHubFromContext(c); hub != nil { + hub.WithScope(func(scope *sentry.Scope) { + scope.SetExtra("unwantedQuery", "someQueryDataMaybe") + hub.CaptureMessage("User provided unwanted query string, but we recovered just fine") + }) + } + return c.SendStatus(fiber.StatusOK) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Accessing Context in `BeforeSend` callback + +```go +sentry.Init(sentry.ClientOptions{ + Dsn: "your-public-dsn", + BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + if hint.Context != nil { + if c, ok := hint.Context.Value(sentry.RequestContextKey).(*fiber.Ctx); ok { + // You have access to the original Context if it panicked + fmt.Println(c.Hostname()) + } + } + return event + }, +}) +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/fiberzap/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/fiberzap/README.md new file mode 100644 index 00000000000..03e3e6e59ad --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/fiberzap/README.md @@ -0,0 +1,116 @@ +--- +id: fiberzap +title: Fiberzap +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fiberzap*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Zap](https://github.com/uber-go/zap) logging support for Fiber. + +## Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fiberzap +go get -u go.uber.org/zap +``` + +### Signature + +```go +fiberzap.New(config ...Config) fiber.Handler +``` + +### Config + +| Property | Type | Description | Default | +|:--------------|:-------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------| +| Next | `func(*Ctx) bool` | Define a function to skip this middleware when returned true | `nil` | +| Logger | `*zap.Logger` | Add custom zap logger. | `zap.NewDevelopment()` | +| Fields | `[]string` | Add fields what you want see. | `[]string{"latency", "status", "method", "url"}` | +| Messages | `[]string` | Custom response messages. | `[]string{"Server error", "Client error", "Success"}` | +| Levels | `[]zapcore.Level` | Custom response levels. | `[]zapcore.Level{zapcore.ErrorLevel, zapcore.WarnLevel, zapcore.InfoLevel}` | +| SkipURIs | `[]string` | Skip logging these URI. | `[]string{}` | +| GetResBody | func(c *fiber.Ctx) []byte | Define a function to get response body when return non-nil.
eg: When use compress middleware, resBody is unreadable. you can set GetResBody func to get readable resBody. | `nil` | +### Example +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fiberzap" + "go.uber.org/zap" +) + +func main() { + app := fiber.New() + logger, _ := zap.NewProduction() + + app.Use(fiberzap.New(fiberzap.Config{ + Logger: logger, + })) + + app.Get("/", func (c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## NewLogger + +### Signature + +```go +fiberzap.NewLogger(config ...LoggerConfig) *LoggerConfig +``` + +### LoggerConfig + + +| Property | Type | Description | Default | +|:------------|:---------------|:---------------------------------------------------------------------------------------------------------|:-------------------------------| +| CoreConfigs | `[]CoreConfig` | Define Config for zapcore | `fiberzap.LoggerConfigDefault` | +| SetLogger | `*zap.Logger` | Add custom zap logger. if not nil, `ZapOptions`, `CoreConfigs`, `SetLevel`, `SetOutput` will be ignored. | `nil` | +| ExtraKeys | `[]string` | Allow users log extra values from context. | `[]string{}` | +| ZapOptions | `[]zap.Option` | Allow users to configure the zap.Option supplied by zap. | `[]zap.Option{}` | + +### Example + +```go +package main + +import ( + "context" + "github.com/gofiber/contrib/fiberzap" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" +) + +func main() { + app := fiber.New() + log.SetLogger(fiberzap.NewLogger(fiberzap.LoggerConfig{ + ExtraKeys: []string{"request_id"}, + })) + app.Use(func(c *fiber.Ctx) error { + ctx := context.WithValue(c.UserContext(), "request_id", "123") + c.SetUserContext(ctx) + return c.Next() + }) + app.Get("/", func(c *fiber.Ctx) error { + log.WithContext(c.UserContext()).Info("Hello, World!") + return c.SendString("Hello, World!") + }) + log.Fatal(app.Listen(":3000")) +} +``` + diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/fiberzerolog/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/fiberzerolog/README.md new file mode 100644 index 00000000000..987934988b2 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/fiberzerolog/README.md @@ -0,0 +1,70 @@ +--- +id: fiberzerolog +title: Fiberzerolog +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fiberzerolog*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Zerolog](https://zerolog.io/) logging support for Fiber. + +## Install + +This middleware supports Fiber v2. + +```sh +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fiberzerolog +go get -u github.com/rs/zerolog/log +``` + +## Signature + +```go +fiberzerolog.New(config ...Config) fiber.Handler +``` + +## Config + +| Property | Type | Description | Default | +|:--------------|:-------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------| +| Next | `func(*Ctx) bool` | Define a function to skip this middleware when returned true | `nil` | +| Logger | `*zerolog.Logger` | Add custom zerolog logger. | `zerolog.New(os.Stderr).With().Timestamp().Logger()` | +| GetLogger | `func(*fiber.Ctx) zerolog.Logger` | Get custom zerolog logger, if it's defined the returned logger will replace the `Logger` value. | `nil` | +| Fields | `[]string` | Add fields what you want see. | `[]string{"latency", "status", "method", "url", "error"}` | +| Messages | `[]string` | Custom response messages. | `[]string{"Server error", "Client error", "Success"}` | +| Levels | `[]zerolog.Level` | Custom response levels. | `[]zerolog.Level{zerolog.ErrorLevel, zerolog.WarnLevel, zerolog.InfoLevel}` | +| SkipURIs | `[]string` | Skip logging these URI. | `[]string{}` | +| GetResBody | func(c *fiber.Ctx) []byte | Define a function to get response body when return non-nil.
eg: When use compress middleware, resBody is unreadable. you can set GetResBody func to get readable resBody. | `nil` | + +## Example + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fiberzerolog" + "github.com/rs/zerolog" +) + +func main() { + app := fiber.New() + logger := zerolog.New(os.Stderr).With().Timestamp().Logger() + + app.Use(fiberzerolog.New(fiberzerolog.Config{ + Logger: &logger, + })) + + app.Get("/", func (c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + if err := app.Listen(":3000"); err != nil { + logger.Fatal().Err(err).Msg("Fiber app error") + } +} +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/jwt/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/jwt/README.md new file mode 100644 index 00000000000..5f852b28f88 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/jwt/README.md @@ -0,0 +1,296 @@ +--- +id: jwt +title: JWT +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=jwt*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +JWT returns a JSON Web Token (JWT) auth middleware. +For valid token, it sets the user in Ctx.Locals and calls next handler. +For invalid token, it returns "401 - Unauthorized" error. +For missing token, it returns "400 - Bad Request" error. + +Special thanks and credits to [Echo](https://echo.labstack.com/middleware/jwt) + +### Install + +This middleware supports Fiber v1 & v2, install accordingly. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/jwt +go get -u github.com/golang-jwt/jwt/v5 +``` + +### Signature +```go +jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error +``` + +### Config +| Property | Type | Description | Default | +|:---------------|:--------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| +| Filter | `func(*fiber.Ctx) bool` | Defines a function to skip middleware | `nil` | +| SuccessHandler | `func(*fiber.Ctx) error` | SuccessHandler defines a function which is executed for a valid token. | `nil` | +| ErrorHandler | `func(*fiber.Ctx, error) error` | ErrorHandler defines a function which is executed for an invalid token. | `401 Invalid or expired JWT` | +| SigningKey | `interface{}` | Signing key to validate token. Used as fallback if SigningKeys has length 0. | `nil` | +| SigningKeys | `map[string]interface{}` | Map of signing keys to validate token with kid field usage. | `nil` | +| ContextKey | `string` | Context key to store user information from the token into context. | `"user"` | +| Claims | `jwt.Claim` | Claims are extendable claims data defining token content. | `jwt.MapClaims{}` | +| TokenLookup | `string` | TokenLookup is a string in the form of `:` that is used | `"header:Authorization"` | +| AuthScheme | `string` | AuthScheme to be used in the Authorization header. The default value (`"Bearer"`) will only be used in conjuction with the default `TokenLookup` value. | `"Bearer"` | +| KeyFunc | `func() jwt.Keyfunc` | KeyFunc defines a user-defined function that supplies the public key for a token validation. | `jwtKeyFunc` | +| JWKSetURLs | `[]string` | A slice of unique JSON Web Key (JWK) Set URLs to used to parse JWTs. | `nil` | + + +### HS256 Example +```go +package main + +import ( + "time" + + "github.com/gofiber/fiber/v2" + + jwtware "github.com/gofiber/contrib/jwt" + "github.com/golang-jwt/jwt/v5" +) + +func main() { + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // JWT Middleware + app.Use(jwtware.New(jwtware.Config{ + SigningKey: jwtware.SigningKey{Key: []byte("secret")}, + })) + + // Restricted Routes + app.Get("/restricted", restricted) + + app.Listen(":3000") +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create the Claims + claims := jwt.MapClaims{ + "name": "John Doe", + "admin": true, + "exp": time.Now().Add(time.Hour * 72).Unix(), + } + + // Create token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // Generate encoded token and send it as response. + t, err := token.SignedString([]byte("secret")) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": t}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + name := claims["name"].(string) + return c.SendString("Welcome " + name) +} + +``` + +### HS256 Test +_Login using username and password to retrieve a token._ +``` +curl --data "user=john&pass=doe" http://localhost:3000/login +``` +_Response_ +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY" +} +``` + + +_Request a restricted resource using the token in Authorization request header._ +``` +curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY" +``` +_Response_ +``` +Welcome John Doe +``` + +### RS256 Example +```go +package main + +import ( + "crypto/rand" + "crypto/rsa" + "log" + "time" + + "github.com/gofiber/fiber/v2" + + "github.com/golang-jwt/jwt/v5" + + jwtware "github.com/gofiber/contrib/jwt" +) + +var ( + // Obviously, this is just a test example. Do not do this in production. + // In production, you would have the private key and public key pair generated + // in advance. NEVER add a private key to any GitHub repo. + privateKey *rsa.PrivateKey +) + +func main() { + app := fiber.New() + + // Just as a demo, generate a new private/public key pair on each run. See note above. + rng := rand.Reader + var err error + privateKey, err = rsa.GenerateKey(rng, 2048) + if err != nil { + log.Fatalf("rsa.GenerateKey: %v", err) + } + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // JWT Middleware + app.Use(jwtware.New(jwtware.Config{ + SigningKey: jwtware.SigningKey{ + JWTAlg: jwtware.RS256, + Key: privateKey.Public(), + }, + })) + + // Restricted Routes + app.Get("/restricted", restricted) + + app.Listen(":3000") +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create the Claims + claims := jwt.MapClaims{ + "name": "John Doe", + "admin": true, + "exp": time.Now().Add(time.Hour * 72).Unix(), + } + + // Create token + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + + // Generate encoded token and send it as response. + t, err := token.SignedString(privateKey) + if err != nil { + log.Printf("token.SignedString: %v", err) + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": t}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + name := claims["name"].(string) + return c.SendString("Welcome " + name) +} +``` + +### RS256 Test +The RS256 is actually identical to the HS256 test above. + +### JWK Set Test +The tests are identical to basic `JWT` tests above, with exception that `JWKSetURLs` to valid public keys collection in JSON Web Key (JWK) Set format should be supplied. See [RFC 7517](https://www.rfc-editor.org/rfc/rfc7517). + +### Custom KeyFunc example + +KeyFunc defines a user-defined function that supplies the public key for a token validation. +The function shall take care of verifying the signing algorithm and selecting the proper key. +A user-defined KeyFunc can be useful if tokens are issued by an external party. + +When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored. +This is one of the three options to provide a token validation key. +The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. +Required if neither SigningKeys nor SigningKey is provided. +Default to an internal implementation verifying the signing algorithm and selecting the proper key. + +```go +package main + +import ( + "fmt" + "github.com/gofiber/fiber/v2" + + jwtware "github.com/gofiber/contrib/jwt" + "github.com/golang-jwt/jwt/v5" +) + +func main() { + app := fiber.New() + + app.Use(jwtware.New(jwtware.Config{ + KeyFunc: customKeyFunc(), + })) + + app.Get("/ok", func(c *fiber.Ctx) error { + return c.SendString("OK") + }) +} + +func customKeyFunc() jwt.Keyfunc { + return func(t *jwt.Token) (interface{}, error) { + // Always check the signing method + if t.Method.Alg() != jwtware.HS256 { + return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"]) + } + + // TODO custom implementation of loading signing key like from a database + signingKey := "secret" + + return []byte(signingKey), nil + } +} +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/opafiber/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/opafiber/README.md new file mode 100644 index 00000000000..affe100a3f9 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/opafiber/README.md @@ -0,0 +1,108 @@ +--- +id: opafiber +title: Opafiber +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=opafiber*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Open Policy Agent](https://github.com/open-policy-agent/opa) support for Fiber. + +**Note: Requires Go 1.18 and above** + +### Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/opafiber +``` + +### Signature + +```go +opafiber.New(config opafiber.Config) fiber.Handler + +``` + +### Config + +| Property | Type | Description | Default | +|:----------------------|:--------------------|:-------------------------------------------------------------|:--------------------------------------------------------------------| +| RegoQuery | `string` | Required - Rego query | - | +| RegoPolicy | `io.Reader` | Required - Rego policy | - | +| IncludeQueryString | `bool` | Include query string as input to rego policy | `false` | +| DeniedStatusCode | `int` | Http status code to return when policy denies request | `400` | +| DeniedResponseMessage | `string` | Http response body text to return when policy denies request | `""` | +| IncludeHeaders | `[]string` | Include headers as input to rego policy | - | +| InputCreationMethod | `InputCreationFunc` | Use your own function to provide input for OPA | `func defaultInput(ctx *fiber.Ctx) (map[string]interface{}, error)` | + +### Types + +```go +type InputCreationFunc func(c *fiber.Ctx) (map[string]interface{}, error) +``` + +### Usage + +OPA Fiber middleware sends the following example data to the policy engine as input: + +```json +{ + "method": "GET", + "path": "/somePath", + "query": { + "name": ["John Doe"] + }, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json" + } +} +``` + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/opafiber" +) + +func main() { + app := fiber.New() + module := ` +package example.authz + +default allow := false + +allow { + input.method == "GET" +} +` + + cfg := opafiber.Config{ + RegoQuery: "data.example.authz.allow", + RegoPolicy: bytes.NewBufferString(module), + IncludeQueryString: true, + DeniedStatusCode: fiber.StatusForbidden, + DeniedResponseMessage: "status forbidden", + IncludeHeaders: []string{"Authorization"}, + InputCreationMethod: func (ctx *fiber.Ctx) (map[string]interface{}, error) { + return map[string]interface{}{ + "method": ctx.Method(), + "path": ctx.Path(), + }, nil + }, + } + app.Use(opafiber.New(cfg)) + + app.Get("/", func(ctx *fiber.Ctx) error { + return ctx.SendStatus(200) + }) + + app.Listen(":8080") +} +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/otelfiber/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/otelfiber/README.md new file mode 100644 index 00000000000..d4889385616 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/otelfiber/README.md @@ -0,0 +1,128 @@ +--- +id: otelfiber +title: Otelfiber +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=otelfiber*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[OpenTelemetry](https://opentelemetry.io/) support for Fiber. + +Can be found on [OpenTelemetry Registry](https://opentelemetry.io/registry/instrumentation-go-fiber/). + +### Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/contrib/otelfiber +``` + +### Signature + +``` +otelfiber.Middleware(opts ...Option) fiber.Handler +``` + +### Config + + +| Property | Type | Description | Default | +| :------------------ | :-------------------------------- | :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| Next | `func(*fiber.Ctx) bool` | Define a function to skip this middleware when returned trueRequired - Rego quer | nil | +| TracerProvider | `oteltrace.TracerProvider` | Specifies a tracer provider to use for creating a tracer | nil - the global tracer provider is used | +| MeterProvider | `otelmetric.MeterProvider` | Specifies a meter provider to use for reporting | nil - the global meter provider is used | +| Port | `*int` | Specifies the value to use when setting the `net.host.port` attribute on metrics/spans | Required: If not default (`80` for `http`, `443` for `https`) | +| Propagators | `propagation.TextMapPropagator` | Specifies propagators to use for extracting information from the HTTP requests | If none are specified, global ones will be used | +| ServerName | `*string` | specifies the value to use when setting the `http.server_name` attribute on metrics/spans | - | +| SpanNameFormatter | `func(*fiber.Ctx) string` | Takes a function that will be called on every request and the returned string will become the Span Name | default formatter returns the route pathRaw | + +### Usage + +Please refer to [example](./example) + +### Example + + +```go +package main + +import ( + "context" + "errors" + "log" + + "go.opentelemetry.io/otel/sdk/resource" + + "github.com/gofiber/fiber/v2" + + "github.com/gofiber/contrib/otelfiber" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + + //"go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/propagation" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + oteltrace "go.opentelemetry.io/otel/trace" +) + +var tracer = otel.Tracer("fiber-server") + +func main() { + tp := initTracer() + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Printf("Error shutting down tracer provider: %v", err) + } + }() + + app := fiber.New() + + app.Use(otelfiber.Middleware()) + + app.Get("/error", func(ctx *fiber.Ctx) error { + return errors.New("abc") + }) + + app.Get("/users/:id", func(c *fiber.Ctx) error { + id := c.Params("id") + name := getUser(c.UserContext(), id) + return c.JSON(fiber.Map{"id": id, name: name}) + }) + + log.Fatal(app.Listen(":3000")) +} + +func initTracer() *sdktrace.TracerProvider { + exporter, err := stdout.New(stdout.WithPrettyPrint()) + if err != nil { + log.Fatal(err) + } + tp := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource( + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("my-service"), + )), + ) + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) + return tp +} + +func getUser(ctx context.Context, id string) string { + _, span := tracer.Start(ctx, "getUser", oteltrace.WithAttributes(attribute.String("id", id))) + defer span.End() + if id == "123" { + return "otelfiber tester" + } + return "unknown" +} +``` \ No newline at end of file diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/otelfiber/example/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/otelfiber/example/README.md new file mode 100644 index 00000000000..a354c87d48d --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/otelfiber/example/README.md @@ -0,0 +1,31 @@ +--- +id: otelfiber-example +title: Example +--- + +An HTTP server using gofiber fiber and instrumentation. The server has a +`/users/:id` endpoint. The server generates span information to +`stdout`. + +These instructions expect you have +[docker-compose](https://docs.docker.com/compose/) installed. + +Bring up the `fiber-server` and `fiber-client` services to run the +example: + +```sh +docker-compose up --detach fiber-server fiber-client +``` + +The `fiber-client` service sends just one HTTP request to `fiber-server` +and then exits. View the span generated by `fiber-server` in the logs: + +```sh +docker-compose logs fiber-server +``` + +Shut down the services when you are finished with the example: + +```sh +docker-compose down +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/paseto/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/paseto/README.md new file mode 100644 index 00000000000..370b7d89f35 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/paseto/README.md @@ -0,0 +1,382 @@ +--- +id: paseto +title: Paseto +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=paseto*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +PASETO returns a Web Token (PASETO) auth middleware. + +- For valid token, it sets the payload data in Ctx.Locals and calls next handler. +- For invalid token, it returns "401 - Unauthorized" error. +- For missing token, it returns "400 - BadRequest" error. + +### Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/paseto +go get -u github.com/o1egl/paseto +``` + +### Signature + +```go +pasetoware.New(config ...pasetoware.Config) func(*fiber.Ctx) error +``` + +### Config + +| Property | Type | Description | Default | +|:---------------|:--------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------| +| Next | `func(*Ctx) bool` | Defines a function to skip middleware | `nil` | +| SuccessHandler | `func(*fiber.Ctx) error` | SuccessHandler defines a function which is executed for a valid token. | `c.Next()` | +| ErrorHandler | `func(*fiber.Ctx, error) error` | ErrorHandler defines a function which is executed for an invalid token. | `401 Invalid or expired PASETO` | +| Validate | `PayloadValidator` | Defines a function to validate if payload is valid. Optional. In case payload used is created using `CreateToken` function. If token is created using another function, this function must be provided. | `nil` | +| SymmetricKey | `[]byte` | Secret key to encrypt token. If present the middleware will generate local tokens. | `nil` | +| PrivateKey | `ed25519.PrivateKey` | Secret key to sign the tokens. If present (along with its `PublicKey`) the middleware will generate public tokens. | `nil` +| PublicKey | `crypto.PublicKey` | Public key to verify the tokens. If present (along with `PrivateKey`) the middleware will generate public tokens. | `nil` +| ContextKey | `string` | Context key to store user information from the token into context. | `"auth-token"` | +| TokenLookup | `[2]string` | TokenLookup is a string slice with size 2, that is used to extract token from the request | `["header","Authorization"]` | + +### Instructions + +When using this middleware, and creating a token for authentication, you can use the function pasetoware.CreateToken, +that will create a token, encrypt or sign it and returns the PASETO token. + +Passing a `SymmetricKey` in the Config results in a local (encrypted) token, while passing a `PublicKey` +and `PrivateKey` results in a public (signed) token. + +In case you want to use your own data structure, is needed to provide the `Validate` function in `paseware.Config`, that +will return the data stored in the token, and a error. + +### Examples + +Below have a list of some examples that can help you start to use this middleware. In case of any additional example +that doesn't show here, please take a look at the test file. + +#### SymmetricKey + +```go +package main + +import ( + "time" + + "github.com/gofiber/fiber/v2" + "github.com/o1egl/paseto" + + pasetoware "github.com/gofiber/contrib/paseto" +) + +const secretSymmetricKey = "symmetric-secret-key (size = 32)" + +func main() { + + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // Paseto Middleware with local (encrypted) token + apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{ + SymmetricKey: []byte(secretSymmetricKey), + TokenPrefix: "Bearer", + })) + + // Restricted Routes + apiGroup.Get("/restricted", restricted) + + err := app.Listen(":8088") + if err != nil { + return + } +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create token and encrypt it + encryptedToken, err := pasetoware.CreateToken([]byte(secretSymmetricKey), user, 12*time.Hour, pasetoware.PurposeLocal) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": encryptedToken}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + payload := c.Locals(pasetoware.DefaultContextKey).(string) + return c.SendString("Welcome " + payload) +} + +``` + +##### Test it + +_Login using username and password to retrieve a token._ + +``` +curl --data "user=john&pass=doe" http://localhost:8088/login +``` + +_Response_ + +```json +{ + "token": "v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbAY" +} +``` + +_Request a restricted resource using the token in Authorization request header._ + +``` +curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbA" +``` + +_Response_ + +``` +Welcome john +``` + +#### SymmetricKey + Custom Validator callback + +```go +package main + +import ( + "encoding/json" + "time" + + "github.com/o1egl/paseto" + + pasetoware "github.com/gofiber/contrib/paseto" +) + +const secretSymmetricKey = "symmetric-secret-key (size = 32)" + +type customPayloadStruct struct { + Name string `json:"name"` + ExpiresAt time.Time `json:"expiresAt"` +} + +func main() { + + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // Paseto Middleware with local (encrypted) token + apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{ + SymmetricKey: []byte(secretSymmetricKey), + TokenPrefix: "Bearer", + Validate: func(decrypted []byte) (any, error) { + var payload customPayloadStruct + err := json.Unmarshal(decrypted, &payload) + return payload, err + }, + })) + + // Restricted Routes + apiGroup.Get("/restricted", restricted) + + err := app.Listen(":8088") + if err != nil { + return + } +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create the payload + payload := customPayloadStruct{ + Name: "John Doe", + ExpiresAt: time.Now().Add(12 * time.Hour), + } + + // Create token and encrypt it + encryptedToken, err := paseto.NewV2().Encrypt([]byte(secretSymmetricKey), payload, nil) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": encryptedToken}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + payload := c.Locals(pasetoware.DefaultContextKey).(customPayloadStruct) + return c.SendString("Welcome " + payload.Name) +} + +``` + +##### Test it + +_Login using username and password to retrieve a token._ + +``` +curl --data "user=john&pass=doe" http://localhost:8088/login +``` + +_Response_ + +```json +{ + "token": "v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA" +} +``` + +_Request a restricted resource using the token in Authorization request header._ + +``` +curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA" +``` + +_Response_ + +``` +Welcome John Doe +``` + +#### PublicPrivate Key + +```go +package main + +import ( + "crypto/ed25519" + "encoding/hex" + "time" + + "github.com/gofiber/fiber/v2" + + pasetoware "github.com/gofiber/contrib/paseto" +) + +const privateKeySeed = "e9c67fe2433aa4110caf029eba70df2c822cad226b6300ead3dcae443ac3810f" + +var seed, _ = hex.DecodeString(privateKeySeed) +var privateKey = ed25519.NewKeyFromSeed(seed) + +type customPayloadStruct struct { + Name string `json:"name"` + ExpiresAt time.Time `json:"expiresAt"` +} + +func main() { + + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // Paseto Middleware with local (encrypted) token + apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{ + TokenPrefix: "Bearer", + PrivateKey: privateKey, + PublicKey: privateKey.Public(), + })) + + // Restricted Routes + apiGroup.Get("/restricted", restricted) + + err := app.Listen(":8088") + if err != nil { + return + } +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create token and encrypt it + encryptedToken, err := pasetoware.CreateToken(privateKey, user, 12*time.Hour, pasetoware.PurposePublic) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": encryptedToken}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + payload := c.Locals(pasetoware.DefaultContextKey).(string) + return c.SendString("Welcome " + payload) +} + +``` + +##### Test it + +_Login using username and password to retrieve a token._ + +``` +curl --data "user=john&pass=doe" http://localhost:8088/login +``` + +_Response_ + +```json +{ + "token": "v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA" +} +``` + +_Request a restricted resource using the token in Authorization request header._ + +``` +curl localhost:8088/api/restricted -H "Authorization: Bearer v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA" +``` + +_Response_ + +``` +Welcome John Doe +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/swagger/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/swagger/README.md new file mode 100644 index 00000000000..aaab8bbed57 --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/swagger/README.md @@ -0,0 +1,52 @@ +--- +id: swagger +title: Swagger +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=swagger*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Swagger middleware for [Fiber](https://github.com/gofiber/fiber). The middleware handles Swagger UI. + +### Table of Contents +- [Signatures](#signatures) +- [Examples](#examples) + + +### Signatures +```go +func New(config ...Config) fiber.Handler +``` + +### Examples +Import the middleware package that is part of the Fiber web framework +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/swagger" +) +``` + +Then create a Fiber app with app := fiber.New(). + +After you initiate your Fiber app, you can use the following possibilities: + +### Default Config + +```go +app.Use(swagger.New(cfg)) +``` + +### Custom Config + +```go +cfg := swagger.Config{ + BasePath: "/", //swagger ui base path + FilePath: "./docs/swagger.json", +} + +app.Use(swagger.New(cfg)) +``` diff --git a/contrib_versioned_docs/version-jwt_v1.x.x/websocket/README.md b/contrib_versioned_docs/version-jwt_v1.x.x/websocket/README.md new file mode 100644 index 00000000000..dc85c2630fe --- /dev/null +++ b/contrib_versioned_docs/version-jwt_v1.x.x/websocket/README.md @@ -0,0 +1,94 @@ +--- +id: websocket +title: Websocket +--- + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=websocket*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Based on [Fasthttp WebSocket](https://github.com/fasthttp/websocket) for [Fiber](https://github.com/gofiber/fiber) with available `*fiber.Ctx` methods like [Locals](http://docs.gofiber.io/ctx#locals), [Params](http://docs.gofiber.io/ctx#params), [Query](http://docs.gofiber.io/ctx#query) and [Cookies](http://docs.gofiber.io/ctx#cookies). + +### Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/websocket +``` + +### Example + +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/websocket" +) + +func main() { + app := fiber.New() + + app.Use("/ws", func(c *fiber.Ctx) error { + // IsWebSocketUpgrade returns true if the client + // requested upgrade to the WebSocket protocol. + if websocket.IsWebSocketUpgrade(c) { + c.Locals("allowed", true) + return c.Next() + } + return fiber.ErrUpgradeRequired + }) + + app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) { + // c.Locals is added to the *websocket.Conn + log.Println(c.Locals("allowed")) // true + log.Println(c.Params("id")) // 123 + log.Println(c.Query("v")) // 1.0 + log.Println(c.Cookies("session")) // "" + + // websocket.Conn bindings https://pkg.go.dev/github.com/fasthttp/websocket?tab=doc#pkg-index + var ( + mt int + msg []byte + err error + ) + for { + if mt, msg, err = c.ReadMessage(); err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", msg) + + if err = c.WriteMessage(mt, msg); err != nil { + log.Println("write:", err) + break + } + } + + })) + + log.Fatal(app.Listen(":3000")) + // Access the websocket server: ws://localhost:3000/ws/123?v=1.0 + // https://www.websocket.org/echo.html +} + +``` + +### Note with cache middleware + +If you get the error `websocket: bad handshake` when using the [cache middleware](https://github.com/gofiber/fiber/tree/master/middleware/cache), please use `config.Next` to skip websocket path. + +```go +app := fiber.New() +app.Use(cache.New(cache.Config{ + Next: func(c *fiber.Ctx) bool { + return strings.Contains(c.Route().Path, "/ws") + }, +})) + +app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {})) +``` diff --git a/contrib_versioned_sidebars/version-jwt_v1.x.x-sidebars.json b/contrib_versioned_sidebars/version-jwt_v1.x.x-sidebars.json new file mode 100644 index 00000000000..caea0c03ba6 --- /dev/null +++ b/contrib_versioned_sidebars/version-jwt_v1.x.x-sidebars.json @@ -0,0 +1,8 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/contrib_versions.json b/contrib_versions.json index d744ce7a299..1b77df5b491 100644 --- a/contrib_versions.json +++ b/contrib_versions.json @@ -1,4 +1,5 @@ [ + "jwt_v1.x.x", "fiberzerolog_v0.x.x", "fibersentry_v1.x.x", "swagger_v1.x.x"