Enable JWT Auth
All checks were successful
Create Release & Upload Assets / Upload Assets To Gitea w/ goreleaser (push) Successful in 2m30s

This commit is contained in:
2025-01-12 20:41:49 -06:00
parent e02d88f266
commit eb1d69465f
16 changed files with 853 additions and 15 deletions

View File

@@ -10,14 +10,13 @@ Just a fun little project to take a REST API input and generate an output to a n
# TO DO # TO DO
- Print Queue for incoming jobs (create background loop that checks queue) - Print Queue for incoming jobs (create background loop that checks queue)
- Fix JWT, I think maybe this version of Golang broke it?
## Example Usage: ## Example Usage:
``` ```
curl --request POST \ curl --request POST \
--url http://127.0.0.1:8080/api/v1/print \ --url http://127.0.0.1:8080/api/v1/print \
--header 'Authorization: Bearer <TOKEN>' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidWJlcmJyaW5nZXIifQ.TWB3oK8fuoad7oP6FHTP7HhAQB7iRhZRtL1N71irVNc' \
--header 'Content-Type: application/json' \ --header 'Content-Type: application/json' \
--header 'User-Agent: Insomnia/2023.5.5' \ --header 'User-Agent: Insomnia/2023.5.5' \
--data '{ --data '{
@@ -49,4 +48,4 @@ curl --request POST \
} }
] ]
}' }'
``` ```

View File

@@ -3,7 +3,7 @@
port = 8080 port = 8080
[api] [api]
secret = "4ce3ea58ff83fd4e77a9b6031d24b103fcbc7e90889dc73846da7801fc75a52f" secret = "xTw8qdxTy7Ng4DiptTqmeVy506xWLht3OhFn4kdawmM="
[printer] [printer]
address = "192.168.1.245" address = "192.168.1.245"

1
go.mod
View File

@@ -9,6 +9,7 @@ require (
github.com/getsentry/sentry-go/echo v0.31.1 github.com/getsentry/sentry-go/echo v0.31.1
github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang-jwt/jwt/v5 v5.2.1
github.com/justinmichaelvieira/escpos v0.1.1 github.com/justinmichaelvieira/escpos v0.1.1
github.com/labstack/echo-jwt/v4 v4.3.0
github.com/labstack/echo/v4 v4.13.3 github.com/labstack/echo/v4 v4.13.3
github.com/liamg/tml v0.7.0 github.com/liamg/tml v0.7.0
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1

2
go.sum
View File

@@ -22,6 +22,8 @@ github.com/justinmichaelvieira/escpos v0.1.1 h1:FzvdSWbuR/6tVt6UKXPAxtS+VM7XGxLW
github.com/justinmichaelvieira/escpos v0.1.1/go.mod h1:BTLZkBKl4F53lYM/S635//HCltbEoUHuVfHHw+tC9os= github.com/justinmichaelvieira/escpos v0.1.1/go.mod h1:BTLZkBKl4F53lYM/S635//HCltbEoUHuVfHHw+tC9os=
github.com/justinmichaelvieira/iconv v0.1.0 h1:nL7d7cHijfoBYHa804+5SvLwaWHO6+OFyJfWHrPnaOw= github.com/justinmichaelvieira/iconv v0.1.0 h1:nL7d7cHijfoBYHa804+5SvLwaWHO6+OFyJfWHrPnaOw=
github.com/justinmichaelvieira/iconv v0.1.0/go.mod h1:Mm+28owXWTkJx2w+ftVX/RUnzWK7hM1hyUYkrgUerts= github.com/justinmichaelvieira/iconv v0.1.0/go.mod h1:Mm+28owXWTkJx2w+ftVX/RUnzWK7hM1hyUYkrgUerts=
github.com/labstack/echo-jwt/v4 v4.3.0 h1:8JcvVCrK9dRkPx/aWY3ZempZLO336Bebh4oAtBcxAv4=
github.com/labstack/echo-jwt/v4 v4.3.0/go.mod h1:OlWm3wqfnq3Ma8DLmmH7GiEAz2S7Bj23im2iPMEAR+Q=
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=

View File

@@ -4,6 +4,7 @@ import (
fifo "github.com/foize/go.fifo" fifo "github.com/foize/go.fifo"
sentryecho "github.com/getsentry/sentry-go/echo" sentryecho "github.com/getsentry/sentry-go/echo"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo-jwt/v4"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware" "github.com/labstack/echo/v4/middleware"
"golang.org/x/time/rate" "golang.org/x/time/rate"
@@ -84,17 +85,15 @@ func StartEcho(server chan bool, webserver_addr string, webserver_port int) {
api_v1 := e.Group("/api/v1") api_v1 := e.Group("/api/v1")
// Echo JWT - Won't accept valid keys? Bug To-Do // Echo JWT - Won't accept valid keys? Bug To-Do
/* api_v1.Use(echojwt.WithConfig(echojwt.Config{
api_v1.Use(echojwt.WithConfig(echojwt.Config{ SigningKey: []byte(config.JWT_Secret),
SigningKey: []byte(config.JWT_Secret), SigningMethod: "HS256",
SigningMethod: "HS256", TokenLookup: "header:Authorization:Bearer ,query:token, param:token",
TokenLookup: "header:Authorization:Bearer ,query:token, param:token", ContextKey: "User",
ContextKey: "User", NewClaimsFunc: func(c echo.Context) jwt.Claims {
NewClaimsFunc: func(c echo.Context) jwt.Claims { return new(jwtCustomClaims)
return new(jwtCustomClaims) },
}, }))
}))
*/
api_v1.POST("/print", func(c echo.Context) error { api_v1.POST("/print", func(c echo.Context) error {

25
vendor/github.com/labstack/echo-jwt/v4/.editorconfig generated vendored Normal file
View File

@@ -0,0 +1,25 @@
# EditorConfig coding styles definitions. For more information about the
# properties used in this file, please see the EditorConfig documentation:
# http://editorconfig.org/
# indicate this is the root of the project
root = true
[*]
charset = utf-8
end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab
[*.md]
trim_trailing_whitespace = false
[*.go]
indent_style = tab

16
vendor/github.com/labstack/echo-jwt/v4/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1,16 @@
# Automatically normalize line endings for all text-based files
# http://git-scm.com/docs/gitattributes#_end_of_line_conversion
* text=auto
# For the following file types, normalize line endings to LF on checking and
# prevent conversion to CRLF when they are checked out (this is required in
# order to prevent newline related issues)
.* text eol=lf
*.go text eol=lf
*.yml text eol=lf
*.html text eol=lf
*.css text eol=lf
*.js text eol=lf
*.json text eol=lf
LICENSE text eol=lf

8
vendor/github.com/labstack/echo-jwt/v4/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
.DS_Store
coverage.txt
_test
vendor
.idea
*.iml
*.out
.vscode

50
vendor/github.com/labstack/echo-jwt/v4/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,50 @@
# Changelog
## v4.3.0 - 2024-12-04
**Enhancements**
* Update Echo dependency to v4.13.0 by @aldas in [#28](https://github.com/labstack/echo-jwt/pull/28)
## v4.2.1 - 2024-12-04
**Enhancements**
* Return HTTP status 400 if missing JWT by @kitloong in [#13](https://github.com/labstack/echo-jwt/pull/13)
* Update dependencies and CI flow by @aldas in [#21](https://github.com/labstack/echo-jwt/pull/21), [#24](https://github.com/labstack/echo-jwt/pull/24), [#27](https://github.com/labstack/echo-jwt/pull/27)
* Improve readme formatting by @aldas in [#25](https://github.com/labstack/echo-jwt/pull/25)
## v4.2.0 - 2023-01-26
**Breaking change:** [JWT](github.com/golang-jwt/jwt) has been upgraded to `v5`. Check/test all your code involved with JWT tokens/claims. Search for `github.com/golang-jwt/jwt/v4`
and replace it with `github.com/golang-jwt/jwt/v5`
**Enhancements**
* Upgrade `golang-jwt/jwt` library to `v5` [#9](https://github.com/labstack/echo-jwt/pull/9)
## v4.1.0 - 2023-01-26
**Enhancements**
* Add TokenExtractionError and TokenParsingError types to help distinguishing error source in ErrorHandler [#6](https://github.com/labstack/echo-jwt/pull/6)
## v4.0.1 - 2023-01-24
**Fixes**
* Fix data race in error path [#4](https://github.com/labstack/echo-jwt/pull/4)
**Enhancements**
* add TokenError as error returned when parsing fails [#3](https://github.com/labstack/echo-jwt/pull/3)
## v4.0.0 - 2022-12-27
* First release

21
vendor/github.com/labstack/echo-jwt/v4/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 LabStack and Echo contributors
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.

36
vendor/github.com/labstack/echo-jwt/v4/Makefile generated vendored Normal file
View File

@@ -0,0 +1,36 @@
PKG := "github.com/labstack/echo-jwt"
PKG_LIST := $(shell go list ${PKG}/...)
.DEFAULT_GOAL := check
check: lint vet race ## Check project
init:
@go install golang.org/x/lint/golint@latest
@go install honnef.co/go/tools/cmd/staticcheck@latest
lint: ## Lint the files
@staticcheck ${PKG_LIST}
@golint -set_exit_status ${PKG_LIST}
vet: ## Vet the files
@go vet ${PKG_LIST}
test: ## Run tests
@go test -short ${PKG_LIST}
race: ## Run tests with data race detector
@go test -race ${PKG_LIST}
benchmark: ## Run benchmarks
@go test -run="-" -bench=".*" ${PKG_LIST}
format: ## Format the source code
@find ./ -type f -name "*.go" -exec gofmt -w {} \;
help: ## Display this help screen
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
goversion ?= "1.20"
test_version: ## Run tests inside Docker with given version (defaults to 1.20 oldest supported). Example: make test_version goversion=1.20
@docker run --rm -it -v $(shell pwd):/project golang:$(goversion) /bin/sh -c "cd /project && make race"

165
vendor/github.com/labstack/echo-jwt/v4/README.md generated vendored Normal file
View File

@@ -0,0 +1,165 @@
[![Sourcegraph](https://sourcegraph.com/github.com/labstack/echo-jwt/-/badge.svg?style=flat-square)](https://sourcegraph.com/github.com/labstack/echo-jwt?badge)
[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/labstack/echo-jwt/v4)
[![Go Report Card](https://goreportcard.com/badge/github.com/labstack/echo-jwt?style=flat-square)](https://goreportcard.com/report/github.com/labstack/echo-jwt)
[![Codecov](https://img.shields.io/codecov/c/github/labstack/echo-jwt.svg?style=flat-square)](https://codecov.io/gh/labstack/echo-jwt)
[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/labstack/echo-jwt/master/LICENSE)
# Echo JWT middleware
JWT middleware for [Echo](https://github.com/labstack/echo) framework. This middleware uses by default [golang-jwt/jwt/v5](https://github.com/golang-jwt/jwt)
as JWT implementation.
## Versioning
This repository does not use semantic versioning. MAJOR version tracks which Echo version should be used. MINOR version
tracks API changes (possibly backwards incompatible) and PATCH version is incremented for fixes.
NB: When `golang-jwt` MAJOR version changes this library will release MINOR version with **breaking change**. Always
add at least one integration test in your project.
For Echo `v4` use `v4.x.y` releases.
Minimal needed Echo versions:
* `v4.0.0` needs Echo `v4.7.0+`
`main` branch is compatible with the latest Echo version.
## Usage
Add JWT middleware dependency with go modules
```bash
go get github.com/labstack/echo-jwt/v4
```
Use as import statement
```go
import "github.com/labstack/echo-jwt/v4"
```
Add middleware in simplified form, by providing only the secret key
```go
e.Use(echojwt.JWT([]byte("secret")))
```
Add middleware with configuration options
```go
e.Use(echojwt.WithConfig(echojwt.Config{
// ...
SigningKey: []byte("secret"),
// ...
}))
```
Extract token in handler
```go
import "github.com/golang-jwt/jwt/v5"
// ...
e.GET("/", func(c echo.Context) error {
token, ok := c.Get("user").(*jwt.Token) // by default token is stored under `user` key
if !ok {
return errors.New("JWT token missing or invalid")
}
claims, ok := token.Claims.(jwt.MapClaims) // by default claims is of type `jwt.MapClaims`
if !ok {
return errors.New("failed to cast claims as jwt.MapClaims")
}
return c.JSON(http.StatusOK, claims)
})
```
## IMPORTANT: Integration Testing with JWT Library
Ensure that your project includes at least one integration test to detect changes in major versions of the `golang-jwt/jwt` library early.
This is crucial because type assertions like `token := c.Get("user").(*jwt.Token)` may fail silently if the imported version of the JWT library (e.g., `import "github.com/golang-jwt/jwt/v5"`) differs from the version used internally by dependencies (e.g., echo-jwt may now use `v6`). Such discrepancies can lead to invalid casts, causing your handlers to panic or throw errors. Integration tests help safeguard against these version mismatches.
```go
func TestIntegrationMiddlewareWithHandler(t *testing.T) {
e := echo.New()
e.Use(echojwt.WithConfig(echojwt.Config{
SigningKey: []byte("secret"),
}))
// use handler that gets token from context to fail your CI flow when `golang-jwt/jwt` library version changes
// a) `token, ok := c.Get("user").(*jwt.Token)`
// b) `token := c.Get("user").(*jwt.Token)`
e.GET("/example", exampleHandler)
req := httptest.NewRequest(http.MethodGet, "/example", nil)
req.Header.Set(echo.HeaderAuthorization, "Bearer <TOKEN>")
res := httptest.NewRecorder()
e.ServeHTTP(res, req)
if res.Code != 200 {
t.Failed()
}
}
```
## Full example
```go
package main
import (
"errors"
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo-jwt/v4"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"log"
"net/http"
)
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
e.Use(echojwt.WithConfig(echojwt.Config{
SigningKey: []byte("secret"),
}))
e.GET("/", func(c echo.Context) error {
token, ok := c.Get("user").(*jwt.Token) // by default token is stored under `user` key
if !ok {
return errors.New("JWT token missing or invalid")
}
claims, ok := token.Claims.(jwt.MapClaims) // by default claims is of type `jwt.MapClaims`
if !ok {
return errors.New("failed to cast claims as jwt.MapClaims")
}
return c.JSON(http.StatusOK, claims)
})
if err := e.Start(":8080"); err != http.ErrServerClosed {
log.Fatal(err)
}
}
```
Test with
```bash
curl -v -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ" http://localhost:8080
```
Output should be
```bash
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Date: Sun, 27 Nov 2022 21:34:17 GMT
< Content-Length: 52
<
{"admin":true,"name":"John Doe","sub":"1234567890"}
```

11
vendor/github.com/labstack/echo-jwt/v4/codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,11 @@
coverage:
status:
project:
default:
threshold: 1.0%
patch:
default:
threshold: 1.0%
comment:
require_changes: true

205
vendor/github.com/labstack/echo-jwt/v4/extractors.go generated vendored Normal file
View File

@@ -0,0 +1,205 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2016 LabStack and Echo contributors
package echojwt
import (
"errors"
"fmt"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"net/textproto"
"strings"
)
const (
// extractorLimit is arbitrary number to limit values extractor can return. this limits possible resource exhaustion
// attack vector
extractorLimit = 20
)
// TokenExtractionError is catch all type for all errors that occur when the token is extracted from the request. This
// helps to distinguish extractor errors from token parsing errors even if custom extractors or token parsing functions
// are being used that have their own custom errors.
type TokenExtractionError struct {
Err error
}
// Is checks if target error is same as TokenExtractionError
func (e TokenExtractionError) Is(target error) bool { return target == ErrJWTMissing } // to provide some compatibility with older error handling logic
func (e *TokenExtractionError) Error() string { return e.Err.Error() }
func (e *TokenExtractionError) Unwrap() error { return e.Err }
var errHeaderExtractorValueMissing = errors.New("missing value in request header")
var errHeaderExtractorValueInvalid = errors.New("invalid value in request header")
var errQueryExtractorValueMissing = errors.New("missing value in the query string")
var errParamExtractorValueMissing = errors.New("missing value in path params")
var errCookieExtractorValueMissing = errors.New("missing value in cookies")
var errFormExtractorValueMissing = errors.New("missing value in the form")
// CreateExtractors creates ValuesExtractors from given lookups.
// Lookups is a string in the form of "<source>:<name>" or "<source>:<name>,<source>:<name>" that is used
// to extract key from the request.
// Possible values:
// - "header:<name>" or "header:<name>:<cut-prefix>"
// `<cut-prefix>` is argument value to cut/trim prefix of the extracted value. This is useful if header
// value has static prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we
// want to cut is `<auth-scheme> ` note the space at the end.
// In case of basic authentication `Authorization: Basic <credentials>` prefix we want to remove is `Basic `.
// - "query:<name>"
// - "param:<name>"
// - "form:<name>"
// - "cookie:<name>"
//
// Multiple sources example:
// - "header:Authorization,header:X-Api-Key"
func CreateExtractors(lookups string) ([]middleware.ValuesExtractor, error) {
if lookups == "" {
return nil, nil
}
sources := strings.Split(lookups, ",")
var extractors = make([]middleware.ValuesExtractor, 0)
for _, source := range sources {
parts := strings.Split(source, ":")
if len(parts) < 2 {
return nil, fmt.Errorf("extractor source for lookup could not be split into needed parts: %v", source)
}
switch parts[0] {
case "query":
extractors = append(extractors, valuesFromQuery(parts[1]))
case "param":
extractors = append(extractors, valuesFromParam(parts[1]))
case "cookie":
extractors = append(extractors, valuesFromCookie(parts[1]))
case "form":
extractors = append(extractors, valuesFromForm(parts[1]))
case "header":
prefix := ""
if len(parts) > 2 {
prefix = parts[2]
}
extractors = append(extractors, valuesFromHeader(parts[1], prefix))
}
}
return extractors, nil
}
// valuesFromHeader returns a functions that extracts values from the request header.
// valuePrefix is parameter to remove first part (prefix) of the extracted value. This is useful if header value has static
// prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we want to remove is `<auth-scheme> `
// note the space at the end. In case of basic authentication `Authorization: Basic <credentials>` prefix we want to remove
// is `Basic `. In case of JWT tokens `Authorization: Bearer <token>` prefix is `Bearer `.
// If prefix is left empty the whole value is returned.
func valuesFromHeader(header string, valuePrefix string) middleware.ValuesExtractor {
prefixLen := len(valuePrefix)
// standard library parses http.Request header keys in canonical form but we may provide something else so fix this
header = textproto.CanonicalMIMEHeaderKey(header)
return func(c echo.Context) ([]string, error) {
values := c.Request().Header.Values(header)
if len(values) == 0 {
return nil, errHeaderExtractorValueMissing
}
result := make([]string, 0)
for i, value := range values {
if prefixLen == 0 {
result = append(result, value)
if i >= extractorLimit-1 {
break
}
continue
}
if len(value) > prefixLen && strings.EqualFold(value[:prefixLen], valuePrefix) {
result = append(result, value[prefixLen:])
if i >= extractorLimit-1 {
break
}
}
}
if len(result) == 0 {
if prefixLen > 0 {
return nil, errHeaderExtractorValueInvalid
}
return nil, errHeaderExtractorValueMissing
}
return result, nil
}
}
// valuesFromQuery returns a function that extracts values from the query string.
func valuesFromQuery(param string) middleware.ValuesExtractor {
return func(c echo.Context) ([]string, error) {
result := c.QueryParams()[param]
if len(result) == 0 {
return nil, errQueryExtractorValueMissing
} else if len(result) > extractorLimit-1 {
result = result[:extractorLimit]
}
return result, nil
}
}
// valuesFromParam returns a function that extracts values from the url param string.
func valuesFromParam(param string) middleware.ValuesExtractor {
return func(c echo.Context) ([]string, error) {
result := make([]string, 0)
paramVales := c.ParamValues()
for i, p := range c.ParamNames() {
if param == p {
result = append(result, paramVales[i])
if i >= extractorLimit-1 {
break
}
}
}
if len(result) == 0 {
return nil, errParamExtractorValueMissing
}
return result, nil
}
}
// valuesFromCookie returns a function that extracts values from the named cookie.
func valuesFromCookie(name string) middleware.ValuesExtractor {
return func(c echo.Context) ([]string, error) {
cookies := c.Cookies()
if len(cookies) == 0 {
return nil, errCookieExtractorValueMissing
}
result := make([]string, 0)
for i, cookie := range cookies {
if name == cookie.Name {
result = append(result, cookie.Value)
if i >= extractorLimit-1 {
break
}
}
}
if len(result) == 0 {
return nil, errCookieExtractorValueMissing
}
return result, nil
}
}
// valuesFromForm returns a function that extracts values from the form field.
func valuesFromForm(name string) middleware.ValuesExtractor {
return func(c echo.Context) ([]string, error) {
if c.Request().Form == nil {
_ = c.Request().ParseMultipartForm(32 << 20) // same what `c.Request().FormValue(name)` does
}
values := c.Request().Form[name]
if len(values) == 0 {
return nil, errFormExtractorValueMissing
}
if len(values) > extractorLimit-1 {
values = values[:extractorLimit]
}
result := append([]string{}, values...)
return result, nil
}
}

297
vendor/github.com/labstack/echo-jwt/v4/jwt.go generated vendored Normal file
View File

@@ -0,0 +1,297 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: © 2016 LabStack and Echo contributors
package echojwt
import (
"errors"
"fmt"
"net/http"
"github.com/golang-jwt/jwt/v5"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
// Config defines the config for JWT middleware.
type Config struct {
// Skipper defines a function to skip middleware.
Skipper middleware.Skipper
// BeforeFunc defines a function which is executed just before the middleware.
BeforeFunc middleware.BeforeFunc
// SuccessHandler defines a function which is executed for a valid token.
SuccessHandler func(c echo.Context)
// ErrorHandler defines a function which is executed when all lookups have been done and none of them passed Validator
// function. ErrorHandler is executed with last missing (ErrExtractionValueMissing) or an invalid key.
// It may be used to define a custom JWT error.
//
// Note: when error handler swallows the error (returns nil) middleware continues handler chain execution towards handler.
// This is useful in cases when portion of your site/api is publicly accessible and has extra features for authorized users
// In that case you can use ErrorHandler to set default public JWT token value to request and continue with handler chain.
ErrorHandler func(c echo.Context, err error) error
// ContinueOnIgnoredError allows the next middleware/handler to be called when ErrorHandler decides to
// ignore the error (by returning `nil`).
// This is useful when parts of your site/api allow public access and some authorized routes provide extra functionality.
// In that case you can use ErrorHandler to set a default public JWT token value in the request context
// and continue. Some logic down the remaining execution chain needs to check that (public) token value then.
ContinueOnIgnoredError bool
// Context key to store user information from the token into context.
// Optional. Default value "user".
ContextKey string
// Signing key to validate token.
// 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 user-defined KeyFunc nor SigningKeys is provided.
SigningKey interface{}
// Map of signing keys to validate token with kid field usage.
// 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 user-defined KeyFunc nor SigningKey is provided.
SigningKeys map[string]interface{}
// Signing method used to check the token's signing algorithm.
// Optional. Default value HS256.
SigningMethod string
// 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.
// Used by default ParseTokenFunc implementation.
//
// 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.
// Not used if custom ParseTokenFunc is set.
// Default to an internal implementation verifying the signing algorithm and selecting the proper key.
KeyFunc jwt.Keyfunc
// TokenLookup is a string in the form of "<source>:<name>" or "<source>:<name>,<source>:<name>" that is used
// to extract token from the request.
// Optional. Default value "header:Authorization".
// Possible values:
// - "header:<name>" or "header:<name>:<cut-prefix>"
// `<cut-prefix>` is argument value to cut/trim prefix of the extracted value. This is useful if header
// value has static prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we
// want to cut is `<auth-scheme> ` note the space at the end.
// In case of JWT tokens `Authorization: Bearer <token>` prefix we cut is `Bearer `.
// If prefix is left empty the whole value is returned.
// - "query:<name>"
// - "param:<name>"
// - "cookie:<name>"
// - "form:<name>"
// Multiple sources example:
// - "header:Authorization:Bearer ,cookie:myowncookie"
TokenLookup string
// TokenLookupFuncs defines a list of user-defined functions that extract JWT token from the given context.
// This is one of the two options to provide a token extractor.
// The order of precedence is user-defined TokenLookupFuncs, and TokenLookup.
// You can also provide both if you want.
TokenLookupFuncs []middleware.ValuesExtractor
// ParseTokenFunc defines a user-defined function that parses token from given auth. Returns an error when token
// parsing fails or parsed token is invalid.
// Defaults to implementation using `github.com/golang-jwt/jwt` as JWT implementation library
ParseTokenFunc func(c echo.Context, auth string) (interface{}, error)
// Claims are extendable claims data defining token content. Used by default ParseTokenFunc implementation.
// Not used if custom ParseTokenFunc is set.
// Optional. Defaults to function returning jwt.MapClaims
NewClaimsFunc func(c echo.Context) jwt.Claims
}
const (
// AlgorithmHS256 is token signing algorithm
AlgorithmHS256 = "HS256"
)
// ErrJWTMissing denotes an error raised when JWT token value could not be extracted from request
var ErrJWTMissing = echo.NewHTTPError(http.StatusUnauthorized, "missing or malformed jwt")
// ErrJWTInvalid denotes an error raised when JWT token value is invalid or expired
var ErrJWTInvalid = echo.NewHTTPError(http.StatusUnauthorized, "invalid or expired jwt")
// TokenParsingError is catch all type for all errors that occur when token is parsed. In case of library default
// token parsing functions are being used this error instance wraps TokenError. This helps to distinguish extractor
// errors from token parsing errors even if custom extractors or token parsing functions are being used that have
// their own custom errors.
type TokenParsingError struct {
Err error
}
// Is checks if target error is same as TokenParsingError
func (e TokenParsingError) Is(target error) bool { return target == ErrJWTInvalid } // to provide some compatibility with older error handling logic
func (e *TokenParsingError) Error() string { return e.Err.Error() }
func (e *TokenParsingError) Unwrap() error { return e.Err }
// TokenError is used to return error with error occurred JWT token when processing JWT token
type TokenError struct {
Token *jwt.Token
Err error
}
func (e *TokenError) Error() string { return e.Err.Error() }
func (e *TokenError) Unwrap() error { return e.Err }
// JWT returns a JSON Web Token (JWT) auth middleware.
//
// For valid token, it sets the user in context and calls next handler.
// For invalid token, it returns "401 - Unauthorized" error.
// For missing token, it returns "400 - Bad Request" error.
//
// See: https://jwt.io/introduction
func JWT(signingKey interface{}) echo.MiddlewareFunc {
return WithConfig(Config{SigningKey: signingKey})
}
// WithConfig returns a JSON Web Token (JWT) auth middleware or panics if configuration is invalid.
//
// For valid token, it sets the user in context and calls next handler.
// For invalid token, it returns "401 - Unauthorized" error.
// For missing token, it returns "400 - Bad Request" error.
//
// See: https://jwt.io/introduction
func WithConfig(config Config) echo.MiddlewareFunc {
mw, err := config.ToMiddleware()
if err != nil {
panic(err)
}
return mw
}
// ToMiddleware converts Config to middleware or returns an error for invalid configuration
func (config Config) ToMiddleware() (echo.MiddlewareFunc, error) {
if config.Skipper == nil {
config.Skipper = middleware.DefaultSkipper
}
if config.ContextKey == "" {
config.ContextKey = "user"
}
if config.TokenLookup == "" && len(config.TokenLookupFuncs) == 0 {
config.TokenLookup = "header:Authorization:Bearer "
}
if config.SigningMethod == "" {
config.SigningMethod = AlgorithmHS256
}
if config.NewClaimsFunc == nil {
config.NewClaimsFunc = func(c echo.Context) jwt.Claims {
return jwt.MapClaims{}
}
}
if config.SigningKey == nil && len(config.SigningKeys) == 0 && config.KeyFunc == nil && config.ParseTokenFunc == nil {
return nil, errors.New("jwt middleware requires signing key")
}
if config.KeyFunc == nil {
config.KeyFunc = config.defaultKeyFunc
}
if config.ParseTokenFunc == nil {
config.ParseTokenFunc = config.defaultParseTokenFunc
}
extractors, ceErr := CreateExtractors(config.TokenLookup)
if ceErr != nil {
return nil, ceErr
}
if len(config.TokenLookupFuncs) > 0 {
extractors = append(config.TokenLookupFuncs, extractors...)
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if config.Skipper(c) {
return next(c)
}
if config.BeforeFunc != nil {
config.BeforeFunc(c)
}
var lastExtractorErr error
var lastTokenErr error
for _, extractor := range extractors {
auths, extrErr := extractor(c)
if extrErr != nil {
lastExtractorErr = extrErr
continue
}
for _, auth := range auths {
token, err := config.ParseTokenFunc(c, auth)
if err != nil {
lastTokenErr = err
continue
}
// Store user information from token into context.
c.Set(config.ContextKey, token)
if config.SuccessHandler != nil {
config.SuccessHandler(c)
}
return next(c)
}
}
// prioritize token errors over extracting errors as parsing is occurs further in process, meaning we managed to
// extract at least one token and failed to parse it
var err error
if lastTokenErr != nil {
err = &TokenParsingError{Err: lastTokenErr}
} else if lastExtractorErr != nil {
err = &TokenExtractionError{Err: lastExtractorErr}
}
if config.ErrorHandler != nil {
tmpErr := config.ErrorHandler(c, err)
if config.ContinueOnIgnoredError && tmpErr == nil {
return next(c)
}
return tmpErr
}
if lastTokenErr == nil {
return echo.NewHTTPError(http.StatusBadRequest, "missing or malformed jwt").SetInternal(err)
}
return echo.NewHTTPError(http.StatusUnauthorized, "invalid or expired jwt").SetInternal(err)
}
}, nil
}
// defaultKeyFunc creates JWTGo implementation for KeyFunc.
//
// error returns TokenError.
func (config Config) defaultKeyFunc(token *jwt.Token) (interface{}, error) {
if token.Method.Alg() != config.SigningMethod {
return nil, &TokenError{Token: token, Err: fmt.Errorf("unexpected jwt signing method=%v", token.Header["alg"])}
}
if len(config.SigningKeys) == 0 {
return config.SigningKey, nil
}
if kid, ok := token.Header["kid"].(string); ok {
if key, ok := config.SigningKeys[kid]; ok {
return key, nil
}
}
return nil, &TokenError{Token: token, Err: fmt.Errorf("unexpected jwt key id=%v", token.Header["kid"])}
}
// defaultParseTokenFunc creates JWTGo implementation for ParseTokenFunc.
//
// error returns TokenError.
func (config Config) defaultParseTokenFunc(c echo.Context, auth string) (interface{}, error) {
token, err := jwt.ParseWithClaims(auth, config.NewClaimsFunc(c), config.KeyFunc)
if err != nil {
return nil, &TokenError{Token: token, Err: err}
}
if !token.Valid {
return nil, &TokenError{Token: token, Err: errors.New("invalid token")}
}
return token, nil
}

3
vendor/modules.txt vendored
View File

@@ -27,6 +27,9 @@ github.com/justinmichaelvieira/escpos
# github.com/justinmichaelvieira/iconv v0.1.0 # github.com/justinmichaelvieira/iconv v0.1.0
## explicit ## explicit
github.com/justinmichaelvieira/iconv github.com/justinmichaelvieira/iconv
# github.com/labstack/echo-jwt/v4 v4.3.0
## explicit; go 1.20
github.com/labstack/echo-jwt/v4
# github.com/labstack/echo/v4 v4.13.3 # github.com/labstack/echo/v4 v4.13.3
## explicit; go 1.20 ## explicit; go 1.20
github.com/labstack/echo/v4 github.com/labstack/echo/v4