mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-23 14:55:42 +00:00
.
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Fiber
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
---
|
||||
id: jwt
|
||||
---
|
||||
|
||||
# JWT
|
||||
|
||||

|
||||
[](https://gofiber.io/discord)
|
||||

|
||||

|
||||

|
||||
|
||||
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)
|
||||
|
||||
**Note: Requires Go 1.19 and above**
|
||||
|
||||
## 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 `<source>:<name>` 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
|
||||
}
|
||||
}
|
||||
```
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
package jwtware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/MicahParks/keyfunc/v2"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrJWTAlg is returned when the JWT header did not contain the expected algorithm.
|
||||
ErrJWTAlg = errors.New("the JWT header did not contain the expected algorithm")
|
||||
)
|
||||
|
||||
// Config defines the config for JWT middleware
|
||||
type Config struct {
|
||||
// Filter defines a function to skip middleware.
|
||||
// Optional. Default: nil
|
||||
Filter func(*fiber.Ctx) bool
|
||||
|
||||
// SuccessHandler defines a function which is executed for a valid token.
|
||||
// Optional. Default: nil
|
||||
SuccessHandler fiber.Handler
|
||||
|
||||
// ErrorHandler defines a function which is executed for an invalid token.
|
||||
// It may be used to define a custom JWT error.
|
||||
// Optional. Default: 401 Invalid or expired JWT
|
||||
ErrorHandler fiber.ErrorHandler
|
||||
|
||||
// Signing key to validate token. Used as fallback if SigningKeys has length 0.
|
||||
// At least one of the following is required: KeyFunc, JWKSetURLs, SigningKeys, or SigningKey.
|
||||
// The order of precedence is: KeyFunc, JWKSetURLs, SigningKeys, SigningKey.
|
||||
SigningKey SigningKey
|
||||
|
||||
// Map of signing keys to validate token with kid field usage.
|
||||
// At least one of the following is required: KeyFunc, JWKSetURLs, SigningKeys, or SigningKey.
|
||||
// The order of precedence is: KeyFunc, JWKSetURLs, SigningKeys, SigningKey.
|
||||
SigningKeys map[string]SigningKey
|
||||
|
||||
// Context key to store user information from the token into context.
|
||||
// Optional. Default: "user".
|
||||
ContextKey string
|
||||
|
||||
// Claims are extendable claims data defining token content.
|
||||
// Optional. Default value jwt.MapClaims
|
||||
Claims jwt.Claims
|
||||
|
||||
// TokenLookup is a string in the form of "<source>:<name>" that is used
|
||||
// to extract token from the request.
|
||||
// Optional. Default value "header:Authorization".
|
||||
// Possible values:
|
||||
// - "header:<name>"
|
||||
// - "query:<name>"
|
||||
// - "param:<name>"
|
||||
// - "cookie:<name>"
|
||||
TokenLookup string
|
||||
|
||||
// AuthScheme to be used in the Authorization header.
|
||||
// Optional. Default: "Bearer".
|
||||
AuthScheme string
|
||||
|
||||
// KeyFunc is a function that supplies the public key for JWT cryptographic verification.
|
||||
// The function shall take care of verifying the signing algorithm and selecting the proper key.
|
||||
// Internally, github.com/MicahParks/keyfunc/v2 package is used project defaults. If you need more customization,
|
||||
// you can provide a jwt.Keyfunc using that package or make your own implementation.
|
||||
//
|
||||
// At least one of the following is required: KeyFunc, JWKSetURLs, SigningKeys, or SigningKey.
|
||||
// The order of precedence is: KeyFunc, JWKSetURLs, SigningKeys, SigningKey.
|
||||
KeyFunc jwt.Keyfunc
|
||||
|
||||
// JWKSetURLs is a slice of HTTP URLs that contain the JSON Web Key Set (JWKS) used to verify the signatures of
|
||||
// JWTs. Use of HTTPS is recommended. The presence of the "kid" field in the JWT header and JWKs is mandatory for
|
||||
// this feature.
|
||||
//
|
||||
// By default, all JWK Sets in this slice will:
|
||||
// * Refresh every hour.
|
||||
// * Refresh automatically if a new "kid" is seen in a JWT being verified.
|
||||
// * Rate limit refreshes to once every 5 minutes.
|
||||
// * Timeout refreshes after 10 seconds.
|
||||
//
|
||||
// At least one of the following is required: KeyFunc, JWKSetURLs, SigningKeys, or SigningKey.
|
||||
// The order of precedence is: KeyFunc, JWKSetURLs, SigningKeys, SigningKey.
|
||||
JWKSetURLs []string
|
||||
}
|
||||
|
||||
// SigningKey holds information about the recognized cryptographic keys used to sign JWTs by this program.
|
||||
type SigningKey struct {
|
||||
// JWTAlg is the algorithm used to sign JWTs. If this value is a non-empty string, this will be checked against the
|
||||
// "alg" value in the JWT header.
|
||||
//
|
||||
// https://www.rfc-editor.org/rfc/rfc7518#section-3.1
|
||||
JWTAlg string
|
||||
// Key is the cryptographic key used to sign JWTs. For supported types, please see
|
||||
// https://github.com/golang-jwt/jwt.
|
||||
Key interface{}
|
||||
}
|
||||
|
||||
// makeCfg function will check correctness of supplied configuration
|
||||
// and will complement it with default values instead of missing ones
|
||||
func makeCfg(config []Config) (cfg Config) {
|
||||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
if cfg.SuccessHandler == nil {
|
||||
cfg.SuccessHandler = func(c *fiber.Ctx) error {
|
||||
return c.Next()
|
||||
}
|
||||
}
|
||||
if cfg.ErrorHandler == nil {
|
||||
cfg.ErrorHandler = func(c *fiber.Ctx, err error) error {
|
||||
if err.Error() == ErrJWTMissingOrMalformed.Error() {
|
||||
return c.Status(fiber.StatusBadRequest).SendString(ErrJWTMissingOrMalformed.Error())
|
||||
}
|
||||
return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired JWT")
|
||||
}
|
||||
}
|
||||
if cfg.SigningKey.Key == nil && len(cfg.SigningKeys) == 0 && len(cfg.JWKSetURLs) == 0 && cfg.KeyFunc == nil {
|
||||
panic("Fiber: JWT middleware configuration: At least one of the following is required: KeyFunc, JWKSetURLs, SigningKeys, or SigningKey.")
|
||||
}
|
||||
if cfg.ContextKey == "" {
|
||||
cfg.ContextKey = "user"
|
||||
}
|
||||
if cfg.Claims == nil {
|
||||
cfg.Claims = jwt.MapClaims{}
|
||||
}
|
||||
if cfg.TokenLookup == "" {
|
||||
cfg.TokenLookup = defaultTokenLookup
|
||||
// set AuthScheme as "Bearer" only if TokenLookup is set to default.
|
||||
if cfg.AuthScheme == "" {
|
||||
cfg.AuthScheme = "Bearer"
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.KeyFunc == nil {
|
||||
if len(cfg.SigningKeys) > 0 || len(cfg.JWKSetURLs) > 0 {
|
||||
var givenKeys map[string]keyfunc.GivenKey
|
||||
if cfg.SigningKeys != nil {
|
||||
givenKeys = make(map[string]keyfunc.GivenKey, len(cfg.SigningKeys))
|
||||
for kid, key := range cfg.SigningKeys {
|
||||
givenKeys[kid] = keyfunc.NewGivenCustom(key.Key, keyfunc.GivenKeyOptions{
|
||||
Algorithm: key.JWTAlg,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(cfg.JWKSetURLs) > 0 {
|
||||
var err error
|
||||
cfg.KeyFunc, err = multiKeyfunc(givenKeys, cfg.JWKSetURLs)
|
||||
if err != nil {
|
||||
panic("Failed to create keyfunc from JWK Set URL: " + err.Error())
|
||||
}
|
||||
} else {
|
||||
cfg.KeyFunc = keyfunc.NewGiven(givenKeys).Keyfunc
|
||||
}
|
||||
} else {
|
||||
cfg.KeyFunc = signingKeyFunc(cfg.SigningKey)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func multiKeyfunc(givenKeys map[string]keyfunc.GivenKey, jwkSetURLs []string) (jwt.Keyfunc, error) {
|
||||
opts := keyfuncOptions(givenKeys)
|
||||
multiple := make(map[string]keyfunc.Options, len(jwkSetURLs))
|
||||
for _, url := range jwkSetURLs {
|
||||
multiple[url] = opts
|
||||
}
|
||||
multiOpts := keyfunc.MultipleOptions{
|
||||
KeySelector: keyfunc.KeySelectorFirst,
|
||||
}
|
||||
multi, err := keyfunc.GetMultiple(multiple, multiOpts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get multiple JWK Set URLs: %w", err)
|
||||
}
|
||||
return multi.Keyfunc, nil
|
||||
}
|
||||
|
||||
func keyfuncOptions(givenKeys map[string]keyfunc.GivenKey) keyfunc.Options {
|
||||
return keyfunc.Options{
|
||||
GivenKeys: givenKeys,
|
||||
RefreshErrorHandler: func(err error) {
|
||||
log.Printf("Failed to perform background refresh of JWK Set: %s.", err)
|
||||
},
|
||||
RefreshInterval: time.Hour,
|
||||
RefreshRateLimit: time.Minute * 5,
|
||||
RefreshTimeout: time.Second * 10,
|
||||
RefreshUnknownKID: true,
|
||||
}
|
||||
}
|
||||
|
||||
// getExtractors function will create a slice of functions which will be used
|
||||
// for token sarch and will perform extraction of the value
|
||||
func (cfg *Config) getExtractors() []jwtExtractor {
|
||||
// Initialize
|
||||
extractors := make([]jwtExtractor, 0)
|
||||
rootParts := strings.Split(cfg.TokenLookup, ",")
|
||||
for _, rootPart := range rootParts {
|
||||
parts := strings.Split(strings.TrimSpace(rootPart), ":")
|
||||
|
||||
switch parts[0] {
|
||||
case "header":
|
||||
extractors = append(extractors, jwtFromHeader(parts[1], cfg.AuthScheme))
|
||||
case "query":
|
||||
extractors = append(extractors, jwtFromQuery(parts[1]))
|
||||
case "param":
|
||||
extractors = append(extractors, jwtFromParam(parts[1]))
|
||||
case "cookie":
|
||||
extractors = append(extractors, jwtFromCookie(parts[1]))
|
||||
}
|
||||
}
|
||||
return extractors
|
||||
}
|
||||
|
||||
func signingKeyFunc(key SigningKey) jwt.Keyfunc {
|
||||
return func(token *jwt.Token) (interface{}, error) {
|
||||
if key.JWTAlg != "" {
|
||||
alg, ok := token.Header["alg"].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected jwt signing method: expected: %q: got: missing or unexpected JSON type", key.JWTAlg)
|
||||
}
|
||||
if alg != key.JWTAlg {
|
||||
return nil, fmt.Errorf("unexpected jwt signing method: expected: %q: got: %q", key.JWTAlg, alg)
|
||||
}
|
||||
}
|
||||
return key.Key, nil
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package jwtware
|
||||
|
||||
const (
|
||||
// HS256 represents a public cryptography key generated by a 256 bit HMAC algorithm.
|
||||
HS256 = "HS256"
|
||||
|
||||
// HS384 represents a public cryptography key generated by a 384 bit HMAC algorithm.
|
||||
HS384 = "HS384"
|
||||
|
||||
// HS512 represents a public cryptography key generated by a 512 bit HMAC algorithm.
|
||||
HS512 = "HS512"
|
||||
|
||||
// ES256 represents a public cryptography key generated by a 256 bit ECDSA algorithm.
|
||||
ES256 = "ES256"
|
||||
|
||||
// ES384 represents a public cryptography key generated by a 384 bit ECDSA algorithm.
|
||||
ES384 = "ES384"
|
||||
|
||||
// ES512 represents a public cryptography key generated by a 512 bit ECDSA algorithm.
|
||||
ES512 = "ES512"
|
||||
|
||||
// P256 represents a cryptographic elliptical curve type.
|
||||
P256 = "P-256"
|
||||
|
||||
// P384 represents a cryptographic elliptical curve type.
|
||||
P384 = "P-384"
|
||||
|
||||
// P521 represents a cryptographic elliptical curve type.
|
||||
P521 = "P-521"
|
||||
|
||||
// RS256 represents a public cryptography key generated by a 256 bit RSA algorithm.
|
||||
RS256 = "RS256"
|
||||
|
||||
// RS384 represents a public cryptography key generated by a 384 bit RSA algorithm.
|
||||
RS384 = "RS384"
|
||||
|
||||
// RS512 represents a public cryptography key generated by a 512 bit RSA algorithm.
|
||||
RS512 = "RS512"
|
||||
|
||||
// PS256 represents a public cryptography key generated by a 256 bit RSA algorithm.
|
||||
PS256 = "PS256"
|
||||
|
||||
// PS384 represents a public cryptography key generated by a 384 bit RSA algorithm.
|
||||
PS384 = "PS384"
|
||||
|
||||
// PS512 represents a public cryptography key generated by a 512 bit RSA algorithm.
|
||||
PS512 = "PS512"
|
||||
)
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// 🚀 Fiber is an Express inspired web framework written in Go with 💖
|
||||
// 📌 API Documentation: https://fiber.wiki
|
||||
// 📝 Github Repository: https://github.com/gofiber/fiber
|
||||
// Special thanks to Echo: https://github.com/labstack/echo/blob/master/middleware/jwt.go
|
||||
|
||||
package jwtware
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultTokenLookup = "header:" + fiber.HeaderAuthorization
|
||||
)
|
||||
|
||||
// New ...
|
||||
func New(config ...Config) fiber.Handler {
|
||||
cfg := makeCfg(config)
|
||||
|
||||
extractors := cfg.getExtractors()
|
||||
|
||||
// Return middleware handler
|
||||
return func(c *fiber.Ctx) error {
|
||||
// Filter request to skip middleware
|
||||
if cfg.Filter != nil && cfg.Filter(c) {
|
||||
return c.Next()
|
||||
}
|
||||
var auth string
|
||||
var err error
|
||||
|
||||
for _, extractor := range extractors {
|
||||
auth, err = extractor(c)
|
||||
if auth != "" && err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return cfg.ErrorHandler(c, err)
|
||||
}
|
||||
var token *jwt.Token
|
||||
|
||||
if _, ok := cfg.Claims.(jwt.MapClaims); ok {
|
||||
token, err = jwt.Parse(auth, cfg.KeyFunc)
|
||||
} else {
|
||||
t := reflect.ValueOf(cfg.Claims).Type().Elem()
|
||||
claims := reflect.New(t).Interface().(jwt.Claims)
|
||||
token, err = jwt.ParseWithClaims(auth, claims, cfg.KeyFunc)
|
||||
}
|
||||
if err == nil && token.Valid {
|
||||
// Store user information from token into context.
|
||||
c.Locals(cfg.ContextKey, token)
|
||||
return cfg.SuccessHandler(c)
|
||||
}
|
||||
return cfg.ErrorHandler(c, err)
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package jwtware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrJWTMissingOrMalformed is returned when the JWT is missing or malformed.
|
||||
ErrJWTMissingOrMalformed = errors.New("missing or malformed JWT")
|
||||
)
|
||||
|
||||
type jwtExtractor func(c *fiber.Ctx) (string, error)
|
||||
|
||||
// jwtFromHeader returns a function that extracts token from the request header.
|
||||
func jwtFromHeader(header string, authScheme string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
auth := c.Get(header)
|
||||
l := len(authScheme)
|
||||
if len(auth) > l+1 && strings.EqualFold(auth[:l], authScheme) {
|
||||
return strings.TrimSpace(auth[l:]), nil
|
||||
}
|
||||
return "", ErrJWTMissingOrMalformed
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromQuery returns a function that extracts token from the query string.
|
||||
func jwtFromQuery(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Query(param)
|
||||
if token == "" {
|
||||
return "", ErrJWTMissingOrMalformed
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromParam returns a function that extracts token from the url param string.
|
||||
func jwtFromParam(param string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Params(param)
|
||||
if token == "" {
|
||||
return "", ErrJWTMissingOrMalformed
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
// jwtFromCookie returns a function that extracts token from the named cookie.
|
||||
func jwtFromCookie(name string) func(c *fiber.Ctx) (string, error) {
|
||||
return func(c *fiber.Ctx) (string, error) {
|
||||
token := c.Cookies(name)
|
||||
if token == "" {
|
||||
return "", ErrJWTMissingOrMalformed
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user