diff --git a/app/app.go b/app/app.go
index 927526810..6eee6594d 100644
--- a/app/app.go
+++ b/app/app.go
@@ -9,7 +9,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/options"
"github.com/cortezaproject/corteza-server/pkg/plugin"
"github.com/cortezaproject/corteza-server/store"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/spf13/cobra"
"go.uber.org/zap"
"google.golang.org/grpc"
diff --git a/app/boot_levels_test.go b/app/boot_levels_test.go
index 3cb659913..dd7d82cff 100644
--- a/app/boot_levels_test.go
+++ b/app/boot_levels_test.go
@@ -8,7 +8,7 @@ import (
"github.com/cortezaproject/corteza-server/auth/external"
authSettings "github.com/cortezaproject/corteza-server/auth/settings"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/stretchr/testify/require"
)
diff --git a/app/servers.go b/app/servers.go
index 72f3b836a..9d48aad46 100644
--- a/app/servers.go
+++ b/app/servers.go
@@ -19,7 +19,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/webapp"
systemRest "github.com/cortezaproject/corteza-server/system/rest"
"github.com/cortezaproject/corteza-server/system/scim"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
diff --git a/auth/auth.go b/auth/auth.go
index 092e638b6..09681297a 100644
--- a/auth/auth.go
+++ b/auth/auth.go
@@ -31,7 +31,7 @@ import (
"github.com/cortezaproject/corteza-server/store"
systemService "github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
oauth2def "github.com/go-oauth2/oauth2/v4"
"go.uber.org/zap"
"golang.org/x/text/language"
diff --git a/auth/handlers/handle_external.go b/auth/handlers/handle_external.go
index c8dcde837..4639c18d2 100644
--- a/auth/handlers/handle_external.go
+++ b/auth/handlers/handle_external.go
@@ -10,7 +10,7 @@ import (
"github.com/cortezaproject/corteza-server/auth/request"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
diff --git a/auth/handlers/routes.go b/auth/handlers/routes.go
index 3aa50a4a1..d49c720c8 100644
--- a/auth/handlers/routes.go
+++ b/auth/handlers/routes.go
@@ -6,7 +6,7 @@ import (
"github.com/cortezaproject/corteza-server/auth/request"
"github.com/cortezaproject/corteza-server/pkg/actionlog"
"github.com/cortezaproject/corteza-server/pkg/locale"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/go-chi/httprate"
"github.com/gorilla/csrf"
)
diff --git a/automation/rest/handlers/eventTypes.go b/automation/rest/handlers/eventTypes.go
index 3c5f77a32..253ee65cf 100644
--- a/automation/rest/handlers/eventTypes.go
+++ b/automation/rest/handlers/eventTypes.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/automation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/automation/rest/handlers/function.go b/automation/rest/handlers/function.go
index b4ddfbbb1..ebbe56d0b 100644
--- a/automation/rest/handlers/function.go
+++ b/automation/rest/handlers/function.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/automation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/automation/rest/handlers/permissions.go b/automation/rest/handlers/permissions.go
index ac9a9961e..9dffe0340 100644
--- a/automation/rest/handlers/permissions.go
+++ b/automation/rest/handlers/permissions.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/automation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/automation/rest/handlers/session.go b/automation/rest/handlers/session.go
index f94659114..6c5c0e2b5 100644
--- a/automation/rest/handlers/session.go
+++ b/automation/rest/handlers/session.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/automation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/automation/rest/handlers/trigger.go b/automation/rest/handlers/trigger.go
index af8da4625..c0b6bc6ff 100644
--- a/automation/rest/handlers/trigger.go
+++ b/automation/rest/handlers/trigger.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/automation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/automation/rest/handlers/type.go b/automation/rest/handlers/type.go
index 1e4457175..be92d5484 100644
--- a/automation/rest/handlers/type.go
+++ b/automation/rest/handlers/type.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/automation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/automation/rest/handlers/workflow.go b/automation/rest/handlers/workflow.go
index 81890c62e..abf3adbd9 100644
--- a/automation/rest/handlers/workflow.go
+++ b/automation/rest/handlers/workflow.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/automation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/automation/rest/request/eventTypes.go b/automation/rest/request/eventTypes.go
index 25359f1e6..7a0a1d947 100644
--- a/automation/rest/request/eventTypes.go
+++ b/automation/rest/request/eventTypes.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/automation/rest/request/function.go b/automation/rest/request/function.go
index 93ec8372c..bbbd4ca37 100644
--- a/automation/rest/request/function.go
+++ b/automation/rest/request/function.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/automation/rest/request/permissions.go b/automation/rest/request/permissions.go
index e02e560f9..37f567241 100644
--- a/automation/rest/request/permissions.go
+++ b/automation/rest/request/permissions.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/pkg/rbac"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/automation/rest/request/session.go b/automation/rest/request/session.go
index 4a62d4a1e..cd0101150 100644
--- a/automation/rest/request/session.go
+++ b/automation/rest/request/session.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/automation/types"
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/automation/rest/request/trigger.go b/automation/rest/request/trigger.go
index 39ef415bd..24ca2427b 100644
--- a/automation/rest/request/trigger.go
+++ b/automation/rest/request/trigger.go
@@ -15,7 +15,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/automation/rest/request/type.go b/automation/rest/request/type.go
index 3116aee1b..c30927fa2 100644
--- a/automation/rest/request/type.go
+++ b/automation/rest/request/type.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/automation/rest/request/workflow.go b/automation/rest/request/workflow.go
index 436cc03a2..ea87cb0ce 100644
--- a/automation/rest/request/workflow.go
+++ b/automation/rest/request/workflow.go
@@ -15,7 +15,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/automation/rest/router.go b/automation/rest/router.go
index 234f85c76..56934d572 100644
--- a/automation/rest/router.go
+++ b/automation/rest/router.go
@@ -1,7 +1,7 @@
package rest
import (
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/cortezaproject/corteza-server/automation/rest/handlers"
"github.com/cortezaproject/corteza-server/pkg/auth"
diff --git a/compose/rest/handlers/attachment.go b/compose/rest/handlers/attachment.go
index 6082c7313..e139f46ab 100644
--- a/compose/rest/handlers/attachment.go
+++ b/compose/rest/handlers/attachment.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/automation.go b/compose/rest/handlers/automation.go
index 1c7867e05..6924708a3 100644
--- a/compose/rest/handlers/automation.go
+++ b/compose/rest/handlers/automation.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/chart.go b/compose/rest/handlers/chart.go
index ea4aa68bd..f22857b97 100644
--- a/compose/rest/handlers/chart.go
+++ b/compose/rest/handlers/chart.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/module.go b/compose/rest/handlers/module.go
index 6c89c582a..8599f6168 100644
--- a/compose/rest/handlers/module.go
+++ b/compose/rest/handlers/module.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/namespace.go b/compose/rest/handlers/namespace.go
index 1605307fc..7f5910952 100644
--- a/compose/rest/handlers/namespace.go
+++ b/compose/rest/handlers/namespace.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/notification.go b/compose/rest/handlers/notification.go
index 531dd3013..d7435cc73 100644
--- a/compose/rest/handlers/notification.go
+++ b/compose/rest/handlers/notification.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/page.go b/compose/rest/handlers/page.go
index 02c0f1c32..19e009b2a 100644
--- a/compose/rest/handlers/page.go
+++ b/compose/rest/handlers/page.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/permissions.go b/compose/rest/handlers/permissions.go
index 1161d70dd..10767bc32 100644
--- a/compose/rest/handlers/permissions.go
+++ b/compose/rest/handlers/permissions.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/handlers/record.go b/compose/rest/handlers/record.go
index 9161ba8fd..1e4a9e5d3 100644
--- a/compose/rest/handlers/record.go
+++ b/compose/rest/handlers/record.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/compose/rest/request/attachment.go b/compose/rest/request/attachment.go
index e905ca350..f165f9c82 100644
--- a/compose/rest/request/attachment.go
+++ b/compose/rest/request/attachment.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/compose/rest/request/automation.go b/compose/rest/request/automation.go
index f097f6761..90da26644 100644
--- a/compose/rest/request/automation.go
+++ b/compose/rest/request/automation.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/compose/rest/request/chart.go b/compose/rest/request/chart.go
index 8aca18788..a4c52f933 100644
--- a/compose/rest/request/chart.go
+++ b/compose/rest/request/chart.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
"io"
"mime/multipart"
diff --git a/compose/rest/request/module.go b/compose/rest/request/module.go
index f1867764e..4a8924f3b 100644
--- a/compose/rest/request/module.go
+++ b/compose/rest/request/module.go
@@ -15,7 +15,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
"io"
"mime/multipart"
diff --git a/compose/rest/request/namespace.go b/compose/rest/request/namespace.go
index 407059646..e3fc19702 100644
--- a/compose/rest/request/namespace.go
+++ b/compose/rest/request/namespace.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
"io"
"mime/multipart"
diff --git a/compose/rest/request/notification.go b/compose/rest/request/notification.go
index 4452ad30c..e3f09f754 100644
--- a/compose/rest/request/notification.go
+++ b/compose/rest/request/notification.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
"io"
"mime/multipart"
diff --git a/compose/rest/request/page.go b/compose/rest/request/page.go
index b2dc910fd..40c182cdb 100644
--- a/compose/rest/request/page.go
+++ b/compose/rest/request/page.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
"io"
"mime/multipart"
diff --git a/compose/rest/request/permissions.go b/compose/rest/request/permissions.go
index e02e560f9..37f567241 100644
--- a/compose/rest/request/permissions.go
+++ b/compose/rest/request/permissions.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/pkg/rbac"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/compose/rest/request/record.go b/compose/rest/request/record.go
index 5217e7f0c..c636bb4ed 100644
--- a/compose/rest/request/record.go
+++ b/compose/rest/request/record.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/compose/rest/router.go b/compose/rest/router.go
index 8a00f5d33..1c2596146 100644
--- a/compose/rest/router.go
+++ b/compose/rest/router.go
@@ -1,7 +1,7 @@
package rest
import (
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/cortezaproject/corteza-server/compose/rest/handlers"
"github.com/cortezaproject/corteza-server/pkg/auth"
diff --git a/federation/rest/handlers/manageStructure.go b/federation/rest/handlers/manageStructure.go
index 0b975b115..1ea6893e1 100644
--- a/federation/rest/handlers/manageStructure.go
+++ b/federation/rest/handlers/manageStructure.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/federation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/federation/rest/handlers/node.go b/federation/rest/handlers/node.go
index a4a1b4db1..edc7f440e 100644
--- a/federation/rest/handlers/node.go
+++ b/federation/rest/handlers/node.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/federation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/federation/rest/handlers/nodeHandshake.go b/federation/rest/handlers/nodeHandshake.go
index b3bf1dae1..e32236701 100644
--- a/federation/rest/handlers/nodeHandshake.go
+++ b/federation/rest/handlers/nodeHandshake.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/federation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/federation/rest/handlers/permissions.go b/federation/rest/handlers/permissions.go
index b6e2fa70d..35430592c 100644
--- a/federation/rest/handlers/permissions.go
+++ b/federation/rest/handlers/permissions.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/federation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/federation/rest/handlers/syncData.go b/federation/rest/handlers/syncData.go
index 59ece5b82..cb8ae48f8 100644
--- a/federation/rest/handlers/syncData.go
+++ b/federation/rest/handlers/syncData.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/federation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/federation/rest/handlers/syncStructure.go b/federation/rest/handlers/syncStructure.go
index 55292a357..8a5c9e68e 100644
--- a/federation/rest/handlers/syncStructure.go
+++ b/federation/rest/handlers/syncStructure.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/federation/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/federation/rest/request/manageStructure.go b/federation/rest/request/manageStructure.go
index f42cbac94..4695198a8 100644
--- a/federation/rest/request/manageStructure.go
+++ b/federation/rest/request/manageStructure.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/federation/types"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/federation/rest/request/node.go b/federation/rest/request/node.go
index f7aacabbe..8ed27be62 100644
--- a/federation/rest/request/node.go
+++ b/federation/rest/request/node.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/federation/rest/request/nodeHandshake.go b/federation/rest/request/nodeHandshake.go
index cd7d135d9..5b51f1cef 100644
--- a/federation/rest/request/nodeHandshake.go
+++ b/federation/rest/request/nodeHandshake.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/federation/rest/request/permissions.go b/federation/rest/request/permissions.go
index e02e560f9..37f567241 100644
--- a/federation/rest/request/permissions.go
+++ b/federation/rest/request/permissions.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/pkg/rbac"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/federation/rest/request/syncData.go b/federation/rest/request/syncData.go
index f7983195e..279777af9 100644
--- a/federation/rest/request/syncData.go
+++ b/federation/rest/request/syncData.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/federation/rest/request/syncStructure.go b/federation/rest/request/syncStructure.go
index 1376d2a4e..bb86a58d4 100644
--- a/federation/rest/request/syncStructure.go
+++ b/federation/rest/request/syncStructure.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/federation/rest/router.go b/federation/rest/router.go
index 905586607..eaef6362c 100644
--- a/federation/rest/router.go
+++ b/federation/rest/router.go
@@ -1,7 +1,7 @@
package rest
import (
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/cortezaproject/corteza-server/federation/rest/handlers"
"github.com/cortezaproject/corteza-server/pkg/auth"
diff --git a/go.mod b/go.mod
index bf53469be..715b3d5aa 100644
--- a/go.mod
+++ b/go.mod
@@ -34,10 +34,11 @@ require (
github.com/fsnotify/fsnotify v1.5.1
github.com/gabriel-vasile/mimetype v1.4.0
github.com/getsentry/sentry-go v0.12.0
- github.com/go-chi/chi v3.3.4+incompatible
- github.com/go-chi/cors v1.0.0
- github.com/go-chi/httprate v0.4.0
- github.com/go-chi/jwtauth v0.0.0-20190109153619-47840abb19b3
+ github.com/go-chi/chi v3.3.4+incompatible // indirect
+ github.com/go-chi/chi/v5 v5.0.7
+ github.com/go-chi/cors v1.2.0
+ github.com/go-chi/httprate v0.5.2
+ github.com/go-chi/jwtauth v1.2.0
github.com/go-oauth2/oauth2/v4 v4.4.2
github.com/go-sql-driver/mysql v1.6.0
github.com/golang/mock v1.6.0
diff --git a/go.sum b/go.sum
index 0a26c639e..6c420baab 100644
--- a/go.sum
+++ b/go.sum
@@ -255,14 +255,23 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
+github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
github.com/go-chi/chi v3.3.4+incompatible h1:X+OApYAmoQS6jr1WoUgW+t5Ry5RYGXq2A//WAL5xdAU=
github.com/go-chi/chi v3.3.4+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
+github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
+github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.0.0 h1:e6x8k7uWbUwYs+aXDoiUzeQFT6l0cygBYyNhD7/1Tg0=
github.com/go-chi/cors v1.0.0/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
+github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=
+github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-chi/httprate v0.4.0 h1:M2qVV0w6ksgLs6L8lTrvqNeaVm0ZJNVdbYM8u2T8HaE=
github.com/go-chi/httprate v0.4.0/go.mod h1:7e7qjQtHzEbdyW5TYQrl4X2uNRCnlTajictc7B4ftgc=
+github.com/go-chi/httprate v0.5.2 h1:pynJZu4jbSSHFRjpbT7EJJf8b9qt2CLZnqqKy0F+jH4=
+github.com/go-chi/httprate v0.5.2/go.mod h1:kYR4lorHX3It9tTh4eTdHhcF2bzrYnCrRNlv5+IBm2M=
github.com/go-chi/jwtauth v0.0.0-20190109153619-47840abb19b3 h1:cFmETtRzAus3IXaJbth19NdbGOrgjCP86WVK7E8xONM=
github.com/go-chi/jwtauth v0.0.0-20190109153619-47840abb19b3/go.mod h1:ZdbLUuUBAgS2xbrMtdm0EXaHZ2uruR+RMdGn2bnlUtU=
+github.com/go-chi/jwtauth v1.2.0 h1:Z116SPpevIABBYsv8ih/AHYBHmd4EufKSKsLUnWdrTM=
+github.com/go-chi/jwtauth v1.2.0/go.mod h1:NTUpKoTQV6o25UwYE6w/VaLUu83hzrVKYTVo+lE6qDA=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -291,6 +300,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
+github.com/goccy/go-json v0.3.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec=
github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
@@ -525,24 +535,30 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
+github.com/lestrrat-go/backoff/v2 v2.0.7/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
+github.com/lestrrat-go/codegen v1.0.0/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
+github.com/lestrrat-go/iter v1.0.0/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/jwx v0.9.0 h1:Fnd0EWzTm0kFrBPzE/PEPp9nzllES5buMkksPMjEKpM=
github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk=
+github.com/lestrrat-go/jwx v1.1.0/go.mod h1:vn9FzD6gJtKkgYs7RTKV7CjWtEka8F/voUollhnn4QE=
github.com/lestrrat-go/jwx v1.2.11 h1:e9BS5NQ003hxXogNsgf5fEWf01ZJvj4Aj1qy7Dykqm8=
github.com/lestrrat-go/jwx v1.2.11/go.mod h1:25DcLbNWArPA/Ew5CcBmewl32cJKxOk5cbepBsIJFzw=
github.com/lestrrat-go/jwx v1.2.14 h1:69OeaiFKCTn8xDmBGzHTgv/GBoO1LJcXw99GfYCDKzg=
github.com/lestrrat-go/jwx v1.2.14/go.mod h1:3Q3Re8TaOcVTdpx4Tvz++OWmryDklihTDqrrwQiyS2A=
+github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCiIqbJRmKeiADU4=
github.com/lestrrat-go/strftime v1.0.3 h1:qqOPU7y+TM8Y803I8fG9c/DyKG3xH/xkng6keC1015Q=
github.com/lestrrat-go/strftime v1.0.3/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE=
@@ -1173,6 +1189,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
+golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f h1:18s2P7JILnVhIF2+ZtGJQ9czV5bvTsb13/UGtNPDbjA=
golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -1181,6 +1198,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
diff --git a/pkg/actionlog/service.go b/pkg/actionlog/service.go
index 90e63f399..e7aa2c5c1 100644
--- a/pkg/actionlog/service.go
+++ b/pkg/actionlog/service.go
@@ -6,7 +6,7 @@ import (
"time"
"github.com/cortezaproject/corteza-server/pkg/id"
- "github.com/go-chi/chi/middleware"
+ "github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
"github.com/cortezaproject/corteza-server/pkg/api"
diff --git a/pkg/api/server/debug.go b/pkg/api/server/debug.go
index 74210efc0..81a407efc 100644
--- a/pkg/api/server/debug.go
+++ b/pkg/api/server/debug.go
@@ -9,7 +9,7 @@ import (
"reflect"
"runtime"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
)
func debugRoutes(r chi.Routes) http.HandlerFunc {
diff --git a/pkg/api/server/logger.go b/pkg/api/server/logger.go
index bc2a24f62..565fdd5f2 100644
--- a/pkg/api/server/logger.go
+++ b/pkg/api/server/logger.go
@@ -2,7 +2,7 @@ package server
import (
"github.com/cortezaproject/corteza-server/pkg/logger"
- "github.com/go-chi/chi/middleware"
+ "github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
"net/http"
"strings"
diff --git a/pkg/api/server/metrics.go b/pkg/api/server/metrics.go
index b78394fb9..bc57f6101 100644
--- a/pkg/api/server/metrics.go
+++ b/pkg/api/server/metrics.go
@@ -5,7 +5,7 @@ import (
"net/http"
"github.com/766b/chi-prometheus"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
diff --git a/pkg/api/server/middleware.go b/pkg/api/server/middleware.go
index ccd958f4f..ae944bc42 100644
--- a/pkg/api/server/middleware.go
+++ b/pkg/api/server/middleware.go
@@ -9,7 +9,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/getsentry/sentry-go/http"
- "github.com/go-chi/chi/middleware"
+ "github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
)
diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go
index d01c0e3dc..61b4c4350 100644
--- a/pkg/api/server/server.go
+++ b/pkg/api/server/server.go
@@ -12,8 +12,8 @@ import (
"github.com/cortezaproject/corteza-server/pkg/healthcheck"
"github.com/cortezaproject/corteza-server/pkg/options"
"github.com/cortezaproject/corteza-server/pkg/version"
- "github.com/go-chi/chi"
- "github.com/go-chi/chi/middleware"
+ "github.com/go-chi/chi/v5"
+ "github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
)
diff --git a/pkg/api/server/waitfor.go b/pkg/api/server/waitfor.go
index 57127d5a1..025ef7ae4 100644
--- a/pkg/api/server/waitfor.go
+++ b/pkg/api/server/waitfor.go
@@ -10,7 +10,7 @@ import (
"sync"
"time"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/pkg/errors"
"go.uber.org/zap"
)
diff --git a/pkg/apigw/pipeline/chain.go b/pkg/apigw/pipeline/chain.go
index 9a3e0bbf3..5a9373a94 100644
--- a/pkg/apigw/pipeline/chain.go
+++ b/pkg/apigw/pipeline/chain.go
@@ -3,7 +3,7 @@ package pipeline
import (
"net/http"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
)
type (
diff --git a/pkg/apigw/service.go b/pkg/apigw/service.go
index 0b6bed775..d63a8b963 100644
--- a/pkg/apigw/service.go
+++ b/pkg/apigw/service.go
@@ -15,7 +15,7 @@ import (
f "github.com/cortezaproject/corteza-server/pkg/filter"
"github.com/cortezaproject/corteza-server/pkg/options"
st "github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
diff --git a/pkg/codegen/assets/rest_handler.go.tpl b/pkg/codegen/assets/rest_handler.go.tpl
index da2e5084f..bf2a56edf 100644
--- a/pkg/codegen/assets/rest_handler.go.tpl
+++ b/pkg/codegen/assets/rest_handler.go.tpl
@@ -11,7 +11,7 @@ package handlers
import (
"context"
"net/http"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/cortezaproject/corteza-server/{{ .App }}/rest/request"
"github.com/cortezaproject/corteza-server/pkg/api"
)
diff --git a/pkg/codegen/assets/rest_request.go.tpl b/pkg/codegen/assets/rest_request.go.tpl
index 63f70e10a..338f9cb04 100644
--- a/pkg/codegen/assets/rest_request.go.tpl
+++ b/pkg/codegen/assets/rest_request.go.tpl
@@ -11,7 +11,7 @@ package request
import (
"encoding/json"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"fmt"
"mime/multipart"
diff --git a/pkg/corredor/service.go b/pkg/corredor/service.go
index c2de552d5..5f768d833 100644
--- a/pkg/corredor/service.go
+++ b/pkg/corredor/service.go
@@ -13,7 +13,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/options"
"github.com/cortezaproject/corteza-server/pkg/sentry"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi/middleware"
+ "github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
diff --git a/pkg/logger/context.go b/pkg/logger/context.go
index a3916bfa8..efba75b3d 100644
--- a/pkg/logger/context.go
+++ b/pkg/logger/context.go
@@ -2,7 +2,7 @@ package logger
import (
"context"
- "github.com/go-chi/chi/middleware"
+ "github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap"
)
diff --git a/pkg/webapp/serve.go b/pkg/webapp/serve.go
index eeaa4a32a..e32bc7d85 100644
--- a/pkg/webapp/serve.go
+++ b/pkg/webapp/serve.go
@@ -12,7 +12,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/pkg/options"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
diff --git a/pkg/websocket/router.go b/pkg/websocket/router.go
index 25ce1a059..82adb17ce 100644
--- a/pkg/websocket/router.go
+++ b/pkg/websocket/router.go
@@ -1,7 +1,7 @@
package websocket
import (
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
)
// MountRoutes initialize route for websocket
diff --git a/system/rest/handlers/actionlog.go b/system/rest/handlers/actionlog.go
index fa5d64570..ffbfa9dcd 100644
--- a/system/rest/handlers/actionlog.go
+++ b/system/rest/handlers/actionlog.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/apigwFilter.go b/system/rest/handlers/apigwFilter.go
index 2cb744785..06d4445e9 100644
--- a/system/rest/handlers/apigwFilter.go
+++ b/system/rest/handlers/apigwFilter.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/apigwRoute.go b/system/rest/handlers/apigwRoute.go
index d34587a4d..ea8b770e8 100644
--- a/system/rest/handlers/apigwRoute.go
+++ b/system/rest/handlers/apigwRoute.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/application.go b/system/rest/handlers/application.go
index b0a35f313..66ab25914 100644
--- a/system/rest/handlers/application.go
+++ b/system/rest/handlers/application.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/attachment.go b/system/rest/handlers/attachment.go
index 17001dac6..2f694af97 100644
--- a/system/rest/handlers/attachment.go
+++ b/system/rest/handlers/attachment.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/auth.go b/system/rest/handlers/auth.go
index cc5f63289..5b43c5f1e 100644
--- a/system/rest/handlers/auth.go
+++ b/system/rest/handlers/auth.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/authClient.go b/system/rest/handlers/authClient.go
index 2ae431f98..bf3b996c3 100644
--- a/system/rest/handlers/authClient.go
+++ b/system/rest/handlers/authClient.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/automation.go b/system/rest/handlers/automation.go
index 1cfde31e1..2f9bd0f4e 100644
--- a/system/rest/handlers/automation.go
+++ b/system/rest/handlers/automation.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/locale.go b/system/rest/handlers/locale.go
index 53c0aeaa6..9e79f6cee 100644
--- a/system/rest/handlers/locale.go
+++ b/system/rest/handlers/locale.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/permissions.go b/system/rest/handlers/permissions.go
index 0d2f92dcc..979b283dc 100644
--- a/system/rest/handlers/permissions.go
+++ b/system/rest/handlers/permissions.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/queues.go b/system/rest/handlers/queues.go
index 354a0d6de..bffeafbe8 100644
--- a/system/rest/handlers/queues.go
+++ b/system/rest/handlers/queues.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/reminder.go b/system/rest/handlers/reminder.go
index abf6cda97..b1c3ea57b 100644
--- a/system/rest/handlers/reminder.go
+++ b/system/rest/handlers/reminder.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/report.go b/system/rest/handlers/report.go
index f841f6edb..54f72e5db 100644
--- a/system/rest/handlers/report.go
+++ b/system/rest/handlers/report.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/role.go b/system/rest/handlers/role.go
index 11a75ca3f..233798c0c 100644
--- a/system/rest/handlers/role.go
+++ b/system/rest/handlers/role.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/settings.go b/system/rest/handlers/settings.go
index 2b60ec1bf..30c9f997d 100644
--- a/system/rest/handlers/settings.go
+++ b/system/rest/handlers/settings.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/stats.go b/system/rest/handlers/stats.go
index 76c55f8dd..de3741f24 100644
--- a/system/rest/handlers/stats.go
+++ b/system/rest/handlers/stats.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/template.go b/system/rest/handlers/template.go
index 4e49f76fb..cbfab34a5 100644
--- a/system/rest/handlers/template.go
+++ b/system/rest/handlers/template.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/handlers/user.go b/system/rest/handlers/user.go
index aef5ab27a..ce9817c63 100644
--- a/system/rest/handlers/user.go
+++ b/system/rest/handlers/user.go
@@ -12,7 +12,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/system/rest/request"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"net/http"
)
diff --git a/system/rest/request/actionlog.go b/system/rest/request/actionlog.go
index c5afd1664..f9b62a6e9 100644
--- a/system/rest/request/actionlog.go
+++ b/system/rest/request/actionlog.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/apigwFilter.go b/system/rest/request/apigwFilter.go
index e306626f0..a130fde7e 100644
--- a/system/rest/request/apigwFilter.go
+++ b/system/rest/request/apigwFilter.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/apigwRoute.go b/system/rest/request/apigwRoute.go
index 3b01558a8..77bc59cc9 100644
--- a/system/rest/request/apigwRoute.go
+++ b/system/rest/request/apigwRoute.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/application.go b/system/rest/request/application.go
index 96487599d..947c5edbb 100644
--- a/system/rest/request/application.go
+++ b/system/rest/request/application.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
"io"
"mime/multipart"
diff --git a/system/rest/request/attachment.go b/system/rest/request/attachment.go
index 92c26262c..1e5dd67b3 100644
--- a/system/rest/request/attachment.go
+++ b/system/rest/request/attachment.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/auth.go b/system/rest/request/auth.go
index 8b40f366c..372eebeab 100644
--- a/system/rest/request/auth.go
+++ b/system/rest/request/auth.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/authClient.go b/system/rest/request/authClient.go
index 2827b4430..a04a6ff28 100644
--- a/system/rest/request/authClient.go
+++ b/system/rest/request/authClient.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/automation.go b/system/rest/request/automation.go
index f097f6761..90da26644 100644
--- a/system/rest/request/automation.go
+++ b/system/rest/request/automation.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/locale.go b/system/rest/request/locale.go
index 201062a45..deae1ff72 100644
--- a/system/rest/request/locale.go
+++ b/system/rest/request/locale.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/permissions.go b/system/rest/request/permissions.go
index 674cead0c..2afb631e6 100644
--- a/system/rest/request/permissions.go
+++ b/system/rest/request/permissions.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/pkg/rbac"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/queues.go b/system/rest/request/queues.go
index 617139675..f68511a28 100644
--- a/system/rest/request/queues.go
+++ b/system/rest/request/queues.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/reminder.go b/system/rest/request/reminder.go
index 48c974b37..e8bfdda41 100644
--- a/system/rest/request/reminder.go
+++ b/system/rest/request/reminder.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
sqlxTypes "github.com/jmoiron/sqlx/types"
"io"
"mime/multipart"
diff --git a/system/rest/request/report.go b/system/rest/request/report.go
index c35c2455f..e635409ad 100644
--- a/system/rest/request/report.go
+++ b/system/rest/request/report.go
@@ -15,7 +15,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/pkg/report"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/role.go b/system/rest/request/role.go
index 60886db66..fdaab6133 100644
--- a/system/rest/request/role.go
+++ b/system/rest/request/role.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/settings.go b/system/rest/request/settings.go
index b8c5fa343..9ca09ba3a 100644
--- a/system/rest/request/settings.go
+++ b/system/rest/request/settings.go
@@ -13,7 +13,7 @@ import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/stats.go b/system/rest/request/stats.go
index f9b93b6d5..2fa0ca34e 100644
--- a/system/rest/request/stats.go
+++ b/system/rest/request/stats.go
@@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/payload"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/template.go b/system/rest/request/template.go
index 5c72a26a0..2f8907bb5 100644
--- a/system/rest/request/template.go
+++ b/system/rest/request/template.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/request/user.go b/system/rest/request/user.go
index f879ee46a..f657a0ae1 100644
--- a/system/rest/request/user.go
+++ b/system/rest/request/user.go
@@ -14,7 +14,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/label"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"io"
"mime/multipart"
"net/http"
diff --git a/system/rest/router.go b/system/rest/router.go
index 2aa1a7963..020276f11 100644
--- a/system/rest/router.go
+++ b/system/rest/router.go
@@ -1,7 +1,7 @@
package rest
import (
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/system/rest/handlers"
diff --git a/system/scim/group_handler.go b/system/scim/group_handler.go
index 92cb441b6..93fdb7cf3 100644
--- a/system/scim/group_handler.go
+++ b/system/scim/group_handler.go
@@ -11,7 +11,7 @@ import (
"github.com/cortezaproject/corteza-server/store"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
)
type (
diff --git a/system/scim/routes.go b/system/scim/routes.go
index e27a820be..c7b03b119 100644
--- a/system/scim/routes.go
+++ b/system/scim/routes.go
@@ -6,7 +6,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/options"
"github.com/cortezaproject/corteza-server/system/service"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"go.uber.org/zap"
)
diff --git a/system/scim/user_handler.go b/system/scim/user_handler.go
index 0eb29637c..b23d82e8b 100644
--- a/system/scim/user_handler.go
+++ b/system/scim/user_handler.go
@@ -10,7 +10,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/errors"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
)
type (
diff --git a/tests/apigw/main_test.go b/tests/apigw/main_test.go
index f67c9d1fe..97db42401 100644
--- a/tests/apigw/main_test.go
+++ b/tests/apigw/main_test.go
@@ -30,7 +30,7 @@ import (
"github.com/cortezaproject/corteza-server/system/service"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
diff --git a/tests/automation/main_test.go b/tests/automation/main_test.go
index 26285fd65..013f75a4d 100644
--- a/tests/automation/main_test.go
+++ b/tests/automation/main_test.go
@@ -23,7 +23,7 @@ import (
"github.com/cortezaproject/corteza-server/store/sqlite3"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
diff --git a/tests/compose/main_test.go b/tests/compose/main_test.go
index 3a01cc66d..3d563d517 100644
--- a/tests/compose/main_test.go
+++ b/tests/compose/main_test.go
@@ -29,7 +29,7 @@ import (
"github.com/cortezaproject/corteza-server/store"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/spf13/afero"
"github.com/steinfletcher/apitest"
diff --git a/tests/federation/main_test.go b/tests/federation/main_test.go
index 6a9c1aef2..65c875af1 100644
--- a/tests/federation/main_test.go
+++ b/tests/federation/main_test.go
@@ -17,7 +17,7 @@ import (
"github.com/cortezaproject/corteza-server/pkg/logger"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
diff --git a/tests/helpers/auth.go b/tests/helpers/auth.go
index 44266649c..adb127b8f 100644
--- a/tests/helpers/auth.go
+++ b/tests/helpers/auth.go
@@ -3,7 +3,7 @@ package helpers
import (
"net/http"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/steinfletcher/apitest"
"github.com/cortezaproject/corteza-server/pkg/auth"
diff --git a/tests/messagebus/main_test.go b/tests/messagebus/main_test.go
index 05a87244e..18f5e2abd 100644
--- a/tests/messagebus/main_test.go
+++ b/tests/messagebus/main_test.go
@@ -20,7 +20,7 @@ import (
"github.com/cortezaproject/corteza-server/system/service"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/stretchr/testify/require"
)
diff --git a/tests/reporter/main_test.go b/tests/reporter/main_test.go
index 2b6b2c9e6..b86ffa59f 100644
--- a/tests/reporter/main_test.go
+++ b/tests/reporter/main_test.go
@@ -33,7 +33,7 @@ import (
"github.com/cortezaproject/corteza-server/system/types"
sysTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/spf13/afero"
"github.com/steinfletcher/apitest"
diff --git a/tests/system/main_test.go b/tests/system/main_test.go
index ef55cb66d..212640ef4 100644
--- a/tests/system/main_test.go
+++ b/tests/system/main_test.go
@@ -29,7 +29,7 @@ import (
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/spf13/afero"
"github.com/steinfletcher/apitest"
diff --git a/tests/system/scim_test.go b/tests/system/scim_test.go
index 122959fcf..b28c87d76 100644
--- a/tests/system/scim_test.go
+++ b/tests/system/scim_test.go
@@ -13,7 +13,7 @@ import (
"github.com/cortezaproject/corteza-server/system/scim"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
- "github.com/go-chi/chi"
+ "github.com/go-chi/chi/v5"
"github.com/steinfletcher/apitest"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
)
diff --git a/vendor/github.com/go-chi/chi/v5/.gitignore b/vendor/github.com/go-chi/chi/v5/.gitignore
new file mode 100644
index 000000000..ba22c99a9
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/.gitignore
@@ -0,0 +1,3 @@
+.idea
+*.sw?
+.vscode
diff --git a/vendor/github.com/go-chi/chi/v5/CHANGELOG.md b/vendor/github.com/go-chi/chi/v5/CHANGELOG.md
new file mode 100644
index 000000000..88c68c6d0
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/CHANGELOG.md
@@ -0,0 +1,315 @@
+# Changelog
+
+## v5.0.7 (2021-11-18)
+
+- History of changes: see https://github.com/go-chi/chi/compare/v5.0.6...v5.0.7
+
+
+## v5.0.6 (2021-11-15)
+
+- History of changes: see https://github.com/go-chi/chi/compare/v5.0.5...v5.0.6
+
+
+## v5.0.5 (2021-10-27)
+
+- History of changes: see https://github.com/go-chi/chi/compare/v5.0.4...v5.0.5
+
+
+## v5.0.4 (2021-08-29)
+
+- History of changes: see https://github.com/go-chi/chi/compare/v5.0.3...v5.0.4
+
+
+## v5.0.3 (2021-04-29)
+
+- History of changes: see https://github.com/go-chi/chi/compare/v5.0.2...v5.0.3
+
+
+## v5.0.2 (2021-03-25)
+
+- History of changes: see https://github.com/go-chi/chi/compare/v5.0.1...v5.0.2
+
+
+## v5.0.1 (2021-03-10)
+
+- Small improvements
+- History of changes: see https://github.com/go-chi/chi/compare/v5.0.0...v5.0.1
+
+
+## v5.0.0 (2021-02-27)
+
+- chi v5, `github.com/go-chi/chi/v5` introduces the adoption of Go's SIV to adhere to the current state-of-the-tools in Go.
+- chi v1.5.x did not work out as planned, as the Go tooling is too powerful and chi's adoption is too wide.
+ The most responsible thing to do for everyone's benefit is to just release v5 with SIV, so I present to you all,
+ chi v5 at `github.com/go-chi/chi/v5`. I hope someday the developer experience and ergonomics I've been seeking
+ will still come to fruition in some form, see https://github.com/golang/go/issues/44550
+- History of changes: see https://github.com/go-chi/chi/compare/v1.5.4...v5.0.0
+
+
+## v1.5.4 (2021-02-27)
+
+- Undo prior retraction in v1.5.3 as we prepare for v5.0.0 release
+- History of changes: see https://github.com/go-chi/chi/compare/v1.5.3...v1.5.4
+
+
+## v1.5.3 (2021-02-21)
+
+- Update go.mod to go 1.16 with new retract directive marking all versions without prior go.mod support
+- History of changes: see https://github.com/go-chi/chi/compare/v1.5.2...v1.5.3
+
+
+## v1.5.2 (2021-02-10)
+
+- Reverting allocation optimization as a precaution as go test -race fails.
+- Minor improvements, see history below
+- History of changes: see https://github.com/go-chi/chi/compare/v1.5.1...v1.5.2
+
+
+## v1.5.1 (2020-12-06)
+
+- Performance improvement: removing 1 allocation by foregoing context.WithValue, thank you @bouk for
+ your contribution (https://github.com/go-chi/chi/pull/555). Note: new benchmarks posted in README.
+- `middleware.CleanPath`: new middleware that clean's request path of double slashes
+- deprecate & remove `chi.ServerBaseContext` in favour of stdlib `http.Server#BaseContext`
+- plus other tiny improvements, see full commit history below
+- History of changes: see https://github.com/go-chi/chi/compare/v4.1.2...v1.5.1
+
+
+## v1.5.0 (2020-11-12) - now with go.mod support
+
+`chi` dates back to 2016 with it's original implementation as one of the first routers to adopt the newly introduced
+context.Context api to the stdlib -- set out to design a router that is faster, more modular and simpler than anything
+else out there -- while not introducing any custom handler types or dependencies. Today, `chi` still has zero dependencies,
+and in many ways is future proofed from changes, given it's minimal nature. Between versions, chi's iterations have been very
+incremental, with the architecture and api being the same today as it was originally designed in 2016. For this reason it
+makes chi a pretty easy project to maintain, as well thanks to the many amazing community contributions over the years
+to who all help make chi better (total of 86 contributors to date -- thanks all!).
+
+Chi has been an labour of love, art and engineering, with the goals to offer beautiful ergonomics, flexibility, performance
+and simplicity when building HTTP services with Go. I've strived to keep the router very minimal in surface area / code size,
+and always improving the code wherever possible -- and as of today the `chi` package is just 1082 lines of code (not counting
+middlewares, which are all optional). As well, I don't have the exact metrics, but from my analysis and email exchanges from
+companies and developers, chi is used by thousands of projects around the world -- thank you all as there is no better form of
+joy for me than to have art I had started be helpful and enjoyed by others. And of course I use chi in all of my own projects too :)
+
+For me, the asthetics of chi's code and usage are very important. With the introduction of Go's module support
+(which I'm a big fan of), chi's past versioning scheme choice to v2, v3 and v4 would mean I'd require the import path
+of "github.com/go-chi/chi/v4", leading to the lengthy discussion at https://github.com/go-chi/chi/issues/462.
+Haha, to some, you may be scratching your head why I've spent > 1 year stalling to adopt "/vXX" convention in the import
+path -- which isn't horrible in general -- but for chi, I'm unable to accept it as I strive for perfection in it's API design,
+aesthetics and simplicity. It just doesn't feel good to me given chi's simple nature -- I do not foresee a "v5" or "v6",
+and upgrading between versions in the future will also be just incremental.
+
+I do understand versioning is a part of the API design as well, which is why the solution for a while has been to "do nothing",
+as Go supports both old and new import paths with/out go.mod. However, now that Go module support has had time to iron out kinks and
+is adopted everywhere, it's time for chi to get with the times. Luckily, I've discovered a path forward that will make me happy,
+while also not breaking anyone's app who adopted a prior versioning from tags in v2/v3/v4. I've made an experimental release of
+v1.5.0 with go.mod silently, and tested it with new and old projects, to ensure the developer experience is preserved, and it's
+largely unnoticed. Fortunately, Go's toolchain will check the tags of a repo and consider the "latest" tag the one with go.mod.
+However, you can still request a specific older tag such as v4.1.2, and everything will "just work". But new users can just
+`go get github.com/go-chi/chi` or `go get github.com/go-chi/chi@latest` and they will get the latest version which contains
+go.mod support, which is v1.5.0+. `chi` will not change very much over the years, just like it hasn't changed much from 4 years ago.
+Therefore, we will stay on v1.x from here on, starting from v1.5.0. Any breaking changes will bump a "minor" release and
+backwards-compatible improvements/fixes will bump a "tiny" release.
+
+For existing projects who want to upgrade to the latest go.mod version, run: `go get -u github.com/go-chi/chi@v1.5.0`,
+which will get you on the go.mod version line (as Go's mod cache may still remember v4.x). Brand new systems can run
+`go get -u github.com/go-chi/chi` or `go get -u github.com/go-chi/chi@latest` to install chi, which will install v1.5.0+
+built with go.mod support.
+
+My apologies to the developers who will disagree with the decisions above, but, hope you'll try it and see it's a very
+minor request which is backwards compatible and won't break your existing installations.
+
+Cheers all, happy coding!
+
+
+---
+
+
+## v4.1.2 (2020-06-02)
+
+- fix that handles MethodNotAllowed with path variables, thank you @caseyhadden for your contribution
+- fix to replace nested wildcards correctly in RoutePattern, thank you @@unmultimedio for your contribution
+- History of changes: see https://github.com/go-chi/chi/compare/v4.1.1...v4.1.2
+
+
+## v4.1.1 (2020-04-16)
+
+- fix for issue https://github.com/go-chi/chi/issues/411 which allows for overlapping regexp
+ route to the correct handler through a recursive tree search, thanks to @Jahaja for the PR/fix!
+- new middleware.RouteHeaders as a simple router for request headers with wildcard support
+- History of changes: see https://github.com/go-chi/chi/compare/v4.1.0...v4.1.1
+
+
+## v4.1.0 (2020-04-1)
+
+- middleware.LogEntry: Write method on interface now passes the response header
+ and an extra interface type useful for custom logger implementations.
+- middleware.WrapResponseWriter: minor fix
+- middleware.Recoverer: a bit prettier
+- History of changes: see https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0
+
+## v4.0.4 (2020-03-24)
+
+- middleware.Recoverer: new pretty stack trace printing (https://github.com/go-chi/chi/pull/496)
+- a few minor improvements and fixes
+- History of changes: see https://github.com/go-chi/chi/compare/v4.0.3...v4.0.4
+
+
+## v4.0.3 (2020-01-09)
+
+- core: fix regexp routing to include default value when param is not matched
+- middleware: rewrite of middleware.Compress
+- middleware: suppress http.ErrAbortHandler in middleware.Recoverer
+- History of changes: see https://github.com/go-chi/chi/compare/v4.0.2...v4.0.3
+
+
+## v4.0.2 (2019-02-26)
+
+- Minor fixes
+- History of changes: see https://github.com/go-chi/chi/compare/v4.0.1...v4.0.2
+
+
+## v4.0.1 (2019-01-21)
+
+- Fixes issue with compress middleware: #382 #385
+- History of changes: see https://github.com/go-chi/chi/compare/v4.0.0...v4.0.1
+
+
+## v4.0.0 (2019-01-10)
+
+- chi v4 requires Go 1.10.3+ (or Go 1.9.7+) - we have deprecated support for Go 1.7 and 1.8
+- router: respond with 404 on router with no routes (#362)
+- router: additional check to ensure wildcard is at the end of a url pattern (#333)
+- middleware: deprecate use of http.CloseNotifier (#347)
+- middleware: fix RedirectSlashes to include query params on redirect (#334)
+- History of changes: see https://github.com/go-chi/chi/compare/v3.3.4...v4.0.0
+
+
+## v3.3.4 (2019-01-07)
+
+- Minor middleware improvements. No changes to core library/router. Moving v3 into its
+- own branch as a version of chi for Go 1.7, 1.8, 1.9, 1.10, 1.11
+- History of changes: see https://github.com/go-chi/chi/compare/v3.3.3...v3.3.4
+
+
+## v3.3.3 (2018-08-27)
+
+- Minor release
+- See https://github.com/go-chi/chi/compare/v3.3.2...v3.3.3
+
+
+## v3.3.2 (2017-12-22)
+
+- Support to route trailing slashes on mounted sub-routers (#281)
+- middleware: new `ContentCharset` to check matching charsets. Thank you
+ @csucu for your community contribution!
+
+
+## v3.3.1 (2017-11-20)
+
+- middleware: new `AllowContentType` handler for explicit whitelist of accepted request Content-Types
+- middleware: new `SetHeader` handler for short-hand middleware to set a response header key/value
+- Minor bug fixes
+
+
+## v3.3.0 (2017-10-10)
+
+- New chi.RegisterMethod(method) to add support for custom HTTP methods, see _examples/custom-method for usage
+- Deprecated LINK and UNLINK methods from the default list, please use `chi.RegisterMethod("LINK")` and `chi.RegisterMethod("UNLINK")` in an `init()` function
+
+
+## v3.2.1 (2017-08-31)
+
+- Add new `Match(rctx *Context, method, path string) bool` method to `Routes` interface
+ and `Mux`. Match searches the mux's routing tree for a handler that matches the method/path
+- Add new `RouteMethod` to `*Context`
+- Add new `Routes` pointer to `*Context`
+- Add new `middleware.GetHead` to route missing HEAD requests to GET handler
+- Updated benchmarks (see README)
+
+
+## v3.1.5 (2017-08-02)
+
+- Setup golint and go vet for the project
+- As per golint, we've redefined `func ServerBaseContext(h http.Handler, baseCtx context.Context) http.Handler`
+ to `func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler`
+
+
+## v3.1.0 (2017-07-10)
+
+- Fix a few minor issues after v3 release
+- Move `docgen` sub-pkg to https://github.com/go-chi/docgen
+- Move `render` sub-pkg to https://github.com/go-chi/render
+- Add new `URLFormat` handler to chi/middleware sub-pkg to make working with url mime
+ suffixes easier, ie. parsing `/articles/1.json` and `/articles/1.xml`. See comments in
+ https://github.com/go-chi/chi/blob/master/middleware/url_format.go for example usage.
+
+
+## v3.0.0 (2017-06-21)
+
+- Major update to chi library with many exciting updates, but also some *breaking changes*
+- URL parameter syntax changed from `/:id` to `/{id}` for even more flexible routing, such as
+ `/articles/{month}-{day}-{year}-{slug}`, `/articles/{id}`, and `/articles/{id}.{ext}` on the
+ same router
+- Support for regexp for routing patterns, in the form of `/{paramKey:regExp}` for example:
+ `r.Get("/articles/{name:[a-z]+}", h)` and `chi.URLParam(r, "name")`
+- Add `Method` and `MethodFunc` to `chi.Router` to allow routing definitions such as
+ `r.Method("GET", "/", h)` which provides a cleaner interface for custom handlers like
+ in `_examples/custom-handler`
+- Deprecating `mux#FileServer` helper function. Instead, we encourage users to create their
+ own using file handler with the stdlib, see `_examples/fileserver` for an example
+- Add support for LINK/UNLINK http methods via `r.Method()` and `r.MethodFunc()`
+- Moved the chi project to its own organization, to allow chi-related community packages to
+ be easily discovered and supported, at: https://github.com/go-chi
+- *NOTE:* please update your import paths to `"github.com/go-chi/chi"`
+- *NOTE:* chi v2 is still available at https://github.com/go-chi/chi/tree/v2
+
+
+## v2.1.0 (2017-03-30)
+
+- Minor improvements and update to the chi core library
+- Introduced a brand new `chi/render` sub-package to complete the story of building
+ APIs to offer a pattern for managing well-defined request / response payloads. Please
+ check out the updated `_examples/rest` example for how it works.
+- Added `MethodNotAllowed(h http.HandlerFunc)` to chi.Router interface
+
+
+## v2.0.0 (2017-01-06)
+
+- After many months of v2 being in an RC state with many companies and users running it in
+ production, the inclusion of some improvements to the middlewares, we are very pleased to
+ announce v2.0.0 of chi.
+
+
+## v2.0.0-rc1 (2016-07-26)
+
+- Huge update! chi v2 is a large refactor targetting Go 1.7+. As of Go 1.7, the popular
+ community `"net/context"` package has been included in the standard library as `"context"` and
+ utilized by `"net/http"` and `http.Request` to managing deadlines, cancelation signals and other
+ request-scoped values. We're very excited about the new context addition and are proud to
+ introduce chi v2, a minimal and powerful routing package for building large HTTP services,
+ with zero external dependencies. Chi focuses on idiomatic design and encourages the use of
+ stdlib HTTP handlers and middlwares.
+- chi v2 deprecates its `chi.Handler` interface and requires `http.Handler` or `http.HandlerFunc`
+- chi v2 stores URL routing parameters and patterns in the standard request context: `r.Context()`
+- chi v2 lower-level routing context is accessible by `chi.RouteContext(r.Context()) *chi.Context`,
+ which provides direct access to URL routing parameters, the routing path and the matching
+ routing patterns.
+- Users upgrading from chi v1 to v2, need to:
+ 1. Update the old chi.Handler signature, `func(ctx context.Context, w http.ResponseWriter, r *http.Request)` to
+ the standard http.Handler: `func(w http.ResponseWriter, r *http.Request)`
+ 2. Use `chi.URLParam(r *http.Request, paramKey string) string`
+ or `URLParamFromCtx(ctx context.Context, paramKey string) string` to access a url parameter value
+
+
+## v1.0.0 (2016-07-01)
+
+- Released chi v1 stable https://github.com/go-chi/chi/tree/v1.0.0 for Go 1.6 and older.
+
+
+## v0.9.0 (2016-03-31)
+
+- Reuse context objects via sync.Pool for zero-allocation routing [#33](https://github.com/go-chi/chi/pull/33)
+- BREAKING NOTE: due to subtle API changes, previously `chi.URLParams(ctx)["id"]` used to access url parameters
+ has changed to: `chi.URLParam(ctx, "id")`
diff --git a/vendor/github.com/go-chi/chi/v5/CONTRIBUTING.md b/vendor/github.com/go-chi/chi/v5/CONTRIBUTING.md
new file mode 100644
index 000000000..c0ac2dfe8
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Contributing
+
+## Prerequisites
+
+1. [Install Go][go-install].
+2. Download the sources and switch the working directory:
+
+ ```bash
+ go get -u -d github.com/go-chi/chi
+ cd $GOPATH/src/github.com/go-chi/chi
+ ```
+
+## Submitting a Pull Request
+
+A typical workflow is:
+
+1. [Fork the repository.][fork] [This tip maybe also helpful.][go-fork-tip]
+2. [Create a topic branch.][branch]
+3. Add tests for your change.
+4. Run `go test`. If your tests pass, return to the step 3.
+5. Implement the change and ensure the steps from the previous step pass.
+6. Run `goimports -w .`, to ensure the new code conforms to Go formatting guideline.
+7. [Add, commit and push your changes.][git-help]
+8. [Submit a pull request.][pull-req]
+
+[go-install]: https://golang.org/doc/install
+[go-fork-tip]: http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html
+[fork]: https://help.github.com/articles/fork-a-repo
+[branch]: http://learn.github.com/p/branching.html
+[git-help]: https://guides.github.com
+[pull-req]: https://help.github.com/articles/using-pull-requests
diff --git a/vendor/github.com/go-chi/chi/v5/LICENSE b/vendor/github.com/go-chi/chi/v5/LICENSE
new file mode 100644
index 000000000..d99f02ffa
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc.
+
+MIT License
+
+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.
diff --git a/vendor/github.com/go-chi/chi/v5/Makefile b/vendor/github.com/go-chi/chi/v5/Makefile
new file mode 100644
index 000000000..970a21968
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/Makefile
@@ -0,0 +1,18 @@
+all:
+ @echo "**********************************************************"
+ @echo "** chi build tool **"
+ @echo "**********************************************************"
+
+
+test:
+ go clean -testcache && $(MAKE) test-router && $(MAKE) test-middleware
+
+test-router:
+ go test -race -v .
+
+test-middleware:
+ go test -race -v ./middleware
+
+.PHONY: docs
+docs:
+ npx docsify-cli serve ./docs
diff --git a/vendor/github.com/go-chi/chi/v5/README.md b/vendor/github.com/go-chi/chi/v5/README.md
new file mode 100644
index 000000000..5d504d18c
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/README.md
@@ -0,0 +1,500 @@
+#
+
+
+[![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis]
+
+`chi` is a lightweight, idiomatic and composable router for building Go HTTP services. It's
+especially good at helping you write large REST API services that are kept maintainable as your
+project grows and changes. `chi` is built on the new `context` package introduced in Go 1.7 to
+handle signaling, cancelation and request-scoped values across a handler chain.
+
+The focus of the project has been to seek out an elegant and comfortable design for writing
+REST API servers, written during the development of the Pressly API service that powers our
+public API service, which in turn powers all of our client-side applications.
+
+The key considerations of chi's design are: project structure, maintainability, standard http
+handlers (stdlib-only), developer productivity, and deconstructing a large system into many small
+parts. The core router `github.com/go-chi/chi` is quite small (less than 1000 LOC), but we've also
+included some useful/optional subpackages: [middleware](/middleware), [render](https://github.com/go-chi/render)
+and [docgen](https://github.com/go-chi/docgen). We hope you enjoy it too!
+
+## Install
+
+`go get -u github.com/go-chi/chi/v5`
+
+
+## Features
+
+* **Lightweight** - cloc'd in ~1000 LOC for the chi router
+* **Fast** - yes, see [benchmarks](#benchmarks)
+* **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http`
+* **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and sub-router mounting
+* **Context control** - built on new `context` package, providing value chaining, cancellations and timeouts
+* **Robust** - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91))
+* **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown
+* **Go.mod support** - as of v5, go.mod support (see [CHANGELOG](https://github.com/go-chi/chi/blob/master/CHANGELOG.md))
+* **No external dependencies** - plain ol' Go stdlib + net/http
+
+
+## Examples
+
+See [_examples/](https://github.com/go-chi/chi/blob/master/_examples/) for a variety of examples.
+
+
+**As easy as:**
+
+```go
+package main
+
+import (
+ "net/http"
+
+ "github.com/go-chi/chi/v5"
+ "github.com/go-chi/chi/v5/middleware"
+)
+
+func main() {
+ r := chi.NewRouter()
+ r.Use(middleware.Logger)
+ r.Get("/", func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("welcome"))
+ })
+ http.ListenAndServe(":3000", r)
+}
+```
+
+**REST Preview:**
+
+Here is a little preview of how routing looks like with chi. Also take a look at the generated routing docs
+in JSON ([routes.json](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.json)) and in
+Markdown ([routes.md](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.md)).
+
+I highly recommend reading the source of the [examples](https://github.com/go-chi/chi/blob/master/_examples/) listed
+above, they will show you all the features of chi and serve as a good form of documentation.
+
+```go
+import (
+ //...
+ "context"
+ "github.com/go-chi/chi/v5"
+ "github.com/go-chi/chi/v5/middleware"
+)
+
+func main() {
+ r := chi.NewRouter()
+
+ // A good base middleware stack
+ r.Use(middleware.RequestID)
+ r.Use(middleware.RealIP)
+ r.Use(middleware.Logger)
+ r.Use(middleware.Recoverer)
+
+ // Set a timeout value on the request context (ctx), that will signal
+ // through ctx.Done() that the request has timed out and further
+ // processing should be stopped.
+ r.Use(middleware.Timeout(60 * time.Second))
+
+ r.Get("/", func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("hi"))
+ })
+
+ // RESTy routes for "articles" resource
+ r.Route("/articles", func(r chi.Router) {
+ r.With(paginate).Get("/", listArticles) // GET /articles
+ r.With(paginate).Get("/{month}-{day}-{year}", listArticlesByDate) // GET /articles/01-16-2017
+
+ r.Post("/", createArticle) // POST /articles
+ r.Get("/search", searchArticles) // GET /articles/search
+
+ // Regexp url parameters:
+ r.Get("/{articleSlug:[a-z-]+}", getArticleBySlug) // GET /articles/home-is-toronto
+
+ // Subrouters:
+ r.Route("/{articleID}", func(r chi.Router) {
+ r.Use(ArticleCtx)
+ r.Get("/", getArticle) // GET /articles/123
+ r.Put("/", updateArticle) // PUT /articles/123
+ r.Delete("/", deleteArticle) // DELETE /articles/123
+ })
+ })
+
+ // Mount the admin sub-router
+ r.Mount("/admin", adminRouter())
+
+ http.ListenAndServe(":3333", r)
+}
+
+func ArticleCtx(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ articleID := chi.URLParam(r, "articleID")
+ article, err := dbGetArticle(articleID)
+ if err != nil {
+ http.Error(w, http.StatusText(404), 404)
+ return
+ }
+ ctx := context.WithValue(r.Context(), "article", article)
+ next.ServeHTTP(w, r.WithContext(ctx))
+ })
+}
+
+func getArticle(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ article, ok := ctx.Value("article").(*Article)
+ if !ok {
+ http.Error(w, http.StatusText(422), 422)
+ return
+ }
+ w.Write([]byte(fmt.Sprintf("title:%s", article.Title)))
+}
+
+// A completely separate router for administrator routes
+func adminRouter() http.Handler {
+ r := chi.NewRouter()
+ r.Use(AdminOnly)
+ r.Get("/", adminIndex)
+ r.Get("/accounts", adminListAccounts)
+ return r
+}
+
+func AdminOnly(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ perm, ok := ctx.Value("acl.permission").(YourPermissionType)
+ if !ok || !perm.IsAdmin() {
+ http.Error(w, http.StatusText(403), 403)
+ return
+ }
+ next.ServeHTTP(w, r)
+ })
+}
+```
+
+
+## Router interface
+
+chi's router is based on a kind of [Patricia Radix trie](https://en.wikipedia.org/wiki/Radix_tree).
+The router is fully compatible with `net/http`.
+
+Built on top of the tree is the `Router` interface:
+
+```go
+// Router consisting of the core routing methods used by chi's Mux,
+// using only the standard net/http.
+type Router interface {
+ http.Handler
+ Routes
+
+ // Use appends one or more middlewares onto the Router stack.
+ Use(middlewares ...func(http.Handler) http.Handler)
+
+ // With adds inline middlewares for an endpoint handler.
+ With(middlewares ...func(http.Handler) http.Handler) Router
+
+ // Group adds a new inline-Router along the current routing
+ // path, with a fresh middleware stack for the inline-Router.
+ Group(fn func(r Router)) Router
+
+ // Route mounts a sub-Router along a `pattern`` string.
+ Route(pattern string, fn func(r Router)) Router
+
+ // Mount attaches another http.Handler along ./pattern/*
+ Mount(pattern string, h http.Handler)
+
+ // Handle and HandleFunc adds routes for `pattern` that matches
+ // all HTTP methods.
+ Handle(pattern string, h http.Handler)
+ HandleFunc(pattern string, h http.HandlerFunc)
+
+ // Method and MethodFunc adds routes for `pattern` that matches
+ // the `method` HTTP method.
+ Method(method, pattern string, h http.Handler)
+ MethodFunc(method, pattern string, h http.HandlerFunc)
+
+ // HTTP-method routing along `pattern`
+ Connect(pattern string, h http.HandlerFunc)
+ Delete(pattern string, h http.HandlerFunc)
+ Get(pattern string, h http.HandlerFunc)
+ Head(pattern string, h http.HandlerFunc)
+ Options(pattern string, h http.HandlerFunc)
+ Patch(pattern string, h http.HandlerFunc)
+ Post(pattern string, h http.HandlerFunc)
+ Put(pattern string, h http.HandlerFunc)
+ Trace(pattern string, h http.HandlerFunc)
+
+ // NotFound defines a handler to respond whenever a route could
+ // not be found.
+ NotFound(h http.HandlerFunc)
+
+ // MethodNotAllowed defines a handler to respond whenever a method is
+ // not allowed.
+ MethodNotAllowed(h http.HandlerFunc)
+}
+
+// Routes interface adds two methods for router traversal, which is also
+// used by the github.com/go-chi/docgen package to generate documentation for Routers.
+type Routes interface {
+ // Routes returns the routing tree in an easily traversable structure.
+ Routes() []Route
+
+ // Middlewares returns the list of middlewares in use by the router.
+ Middlewares() Middlewares
+
+ // Match searches the routing tree for a handler that matches
+ // the method/path - similar to routing a http request, but without
+ // executing the handler thereafter.
+ Match(rctx *Context, method, path string) bool
+}
+```
+
+Each routing method accepts a URL `pattern` and chain of `handlers`. The URL pattern
+supports named params (ie. `/users/{userID}`) and wildcards (ie. `/admin/*`). URL parameters
+can be fetched at runtime by calling `chi.URLParam(r, "userID")` for named parameters
+and `chi.URLParam(r, "*")` for a wildcard parameter.
+
+
+### Middleware handlers
+
+chi's middlewares are just stdlib net/http middleware handlers. There is nothing special
+about them, which means the router and all the tooling is designed to be compatible and
+friendly with any middleware in the community. This offers much better extensibility and reuse
+of packages and is at the heart of chi's purpose.
+
+Here is an example of a standard net/http middleware where we assign a context key `"user"`
+the value of `"123"`. This middleware sets a hypothetical user identifier on the request
+context and calls the next handler in the chain.
+
+```go
+// HTTP middleware setting a value on the request context
+func MyMiddleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // create new context from `r` request context, and assign key `"user"`
+ // to value of `"123"`
+ ctx := context.WithValue(r.Context(), "user", "123")
+
+ // call the next handler in the chain, passing the response writer and
+ // the updated request object with the new context value.
+ //
+ // note: context.Context values are nested, so any previously set
+ // values will be accessible as well, and the new `"user"` key
+ // will be accessible from this point forward.
+ next.ServeHTTP(w, r.WithContext(ctx))
+ })
+}
+```
+
+
+### Request handlers
+
+chi uses standard net/http request handlers. This little snippet is an example of a http.Handler
+func that reads a user identifier from the request context - hypothetically, identifying
+the user sending an authenticated request, validated+set by a previous middleware handler.
+
+```go
+// HTTP handler accessing data from the request context.
+func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
+ // here we read from the request context and fetch out `"user"` key set in
+ // the MyMiddleware example above.
+ user := r.Context().Value("user").(string)
+
+ // respond to the client
+ w.Write([]byte(fmt.Sprintf("hi %s", user)))
+}
+```
+
+
+### URL parameters
+
+chi's router parses and stores URL parameters right onto the request context. Here is
+an example of how to access URL params in your net/http handlers. And of course, middlewares
+are able to access the same information.
+
+```go
+// HTTP handler accessing the url routing parameters.
+func MyRequestHandler(w http.ResponseWriter, r *http.Request) {
+ // fetch the url parameter `"userID"` from the request of a matching
+ // routing pattern. An example routing pattern could be: /users/{userID}
+ userID := chi.URLParam(r, "userID")
+
+ // fetch `"key"` from the request context
+ ctx := r.Context()
+ key := ctx.Value("key").(string)
+
+ // respond to the client
+ w.Write([]byte(fmt.Sprintf("hi %v, %v", userID, key)))
+}
+```
+
+
+## Middlewares
+
+chi comes equipped with an optional `middleware` package, providing a suite of standard
+`net/http` middlewares. Please note, any middleware in the ecosystem that is also compatible
+with `net/http` can be used with chi's mux.
+
+### Core middlewares
+
+----------------------------------------------------------------------------------------------------
+| chi/middleware Handler | description |
+| :--------------------- | :---------------------------------------------------------------------- |
+| [AllowContentEncoding] | Enforces a whitelist of request Content-Encoding headers |
+| [AllowContentType] | Explicit whitelist of accepted request Content-Types |
+| [BasicAuth] | Basic HTTP authentication |
+| [Compress] | Gzip compression for clients that accept compressed responses |
+| [ContentCharset] | Ensure charset for Content-Type request headers |
+| [CleanPath] | Clean double slashes from request path |
+| [GetHead] | Automatically route undefined HEAD requests to GET handlers |
+| [Heartbeat] | Monitoring endpoint to check the servers pulse |
+| [Logger] | Logs the start and end of each request with the elapsed processing time |
+| [NoCache] | Sets response headers to prevent clients from caching |
+| [Profiler] | Easily attach net/http/pprof to your routers |
+| [RealIP] | Sets a http.Request's RemoteAddr to either X-Real-IP or X-Forwarded-For |
+| [Recoverer] | Gracefully absorb panics and prints the stack trace |
+| [RequestID] | Injects a request ID into the context of each request |
+| [RedirectSlashes] | Redirect slashes on routing paths |
+| [RouteHeaders] | Route handling for request headers |
+| [SetHeader] | Short-hand middleware to set a response header key/value |
+| [StripSlashes] | Strip slashes on routing paths |
+| [Throttle] | Puts a ceiling on the number of concurrent requests |
+| [Timeout] | Signals to the request context when the timeout deadline is reached |
+| [URLFormat] | Parse extension from url and put it on request context |
+| [WithValue] | Short-hand middleware to set a key/value on the request context |
+----------------------------------------------------------------------------------------------------
+
+[AllowContentEncoding]: https://pkg.go.dev/github.com/go-chi/chi/middleware#AllowContentEncoding
+[AllowContentType]: https://pkg.go.dev/github.com/go-chi/chi/middleware#AllowContentType
+[BasicAuth]: https://pkg.go.dev/github.com/go-chi/chi/middleware#BasicAuth
+[Compress]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compress
+[ContentCharset]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ContentCharset
+[CleanPath]: https://pkg.go.dev/github.com/go-chi/chi/middleware#CleanPath
+[GetHead]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetHead
+[GetReqID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#GetReqID
+[Heartbeat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Heartbeat
+[Logger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Logger
+[NoCache]: https://pkg.go.dev/github.com/go-chi/chi/middleware#NoCache
+[Profiler]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Profiler
+[RealIP]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RealIP
+[Recoverer]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Recoverer
+[RedirectSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RedirectSlashes
+[RequestLogger]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestLogger
+[RequestID]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RequestID
+[RouteHeaders]: https://pkg.go.dev/github.com/go-chi/chi/middleware#RouteHeaders
+[SetHeader]: https://pkg.go.dev/github.com/go-chi/chi/middleware#SetHeader
+[StripSlashes]: https://pkg.go.dev/github.com/go-chi/chi/middleware#StripSlashes
+[Throttle]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Throttle
+[ThrottleBacklog]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleBacklog
+[ThrottleWithOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleWithOpts
+[Timeout]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Timeout
+[URLFormat]: https://pkg.go.dev/github.com/go-chi/chi/middleware#URLFormat
+[WithLogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WithLogEntry
+[WithValue]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WithValue
+[Compressor]: https://pkg.go.dev/github.com/go-chi/chi/middleware#Compressor
+[DefaultLogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#DefaultLogFormatter
+[EncoderFunc]: https://pkg.go.dev/github.com/go-chi/chi/middleware#EncoderFunc
+[HeaderRoute]: https://pkg.go.dev/github.com/go-chi/chi/middleware#HeaderRoute
+[HeaderRouter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#HeaderRouter
+[LogEntry]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogEntry
+[LogFormatter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LogFormatter
+[LoggerInterface]: https://pkg.go.dev/github.com/go-chi/chi/middleware#LoggerInterface
+[ThrottleOpts]: https://pkg.go.dev/github.com/go-chi/chi/middleware#ThrottleOpts
+[WrapResponseWriter]: https://pkg.go.dev/github.com/go-chi/chi/middleware#WrapResponseWriter
+
+### Extra middlewares & packages
+
+Please see https://github.com/go-chi for additional packages.
+
+--------------------------------------------------------------------------------------------------------------------
+| package | description |
+|:---------------------------------------------------|:-------------------------------------------------------------
+| [cors](https://github.com/go-chi/cors) | Cross-origin resource sharing (CORS) |
+| [docgen](https://github.com/go-chi/docgen) | Print chi.Router routes at runtime |
+| [jwtauth](https://github.com/go-chi/jwtauth) | JWT authentication |
+| [hostrouter](https://github.com/go-chi/hostrouter) | Domain/host based request routing |
+| [httplog](https://github.com/go-chi/httplog) | Small but powerful structured HTTP request logging |
+| [httprate](https://github.com/go-chi/httprate) | HTTP request rate limiter |
+| [httptracer](https://github.com/go-chi/httptracer) | HTTP request performance tracing library |
+| [httpvcr](https://github.com/go-chi/httpvcr) | Write deterministic tests for external sources |
+| [stampede](https://github.com/go-chi/stampede) | HTTP request coalescer |
+--------------------------------------------------------------------------------------------------------------------
+
+
+## context?
+
+`context` is a tiny pkg that provides simple interface to signal context across call stacks
+and goroutines. It was originally written by [Sameer Ajmani](https://github.com/Sajmani)
+and is available in stdlib since go1.7.
+
+Learn more at https://blog.golang.org/context
+
+and..
+* Docs: https://golang.org/pkg/context
+* Source: https://github.com/golang/go/tree/master/src/context
+
+
+## Benchmarks
+
+The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark
+
+Results as of Nov 29, 2020 with Go 1.15.5 on Linux AMD 3950x
+
+```shell
+BenchmarkChi_Param 3075895 384 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_Param5 2116603 566 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_Param20 964117 1227 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_ParamWrite 2863413 420 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_GithubStatic 3045488 395 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_GithubParam 2204115 540 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_GithubAll 10000 113811 ns/op 81203 B/op 406 allocs/op
+BenchmarkChi_GPlusStatic 3337485 359 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_GPlusParam 2825853 423 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_GPlus2Params 2471697 483 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_GPlusAll 194220 5950 ns/op 5200 B/op 26 allocs/op
+BenchmarkChi_ParseStatic 3365324 356 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_ParseParam 2976614 404 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_Parse2Params 2638084 439 ns/op 400 B/op 2 allocs/op
+BenchmarkChi_ParseAll 109567 11295 ns/op 10400 B/op 52 allocs/op
+BenchmarkChi_StaticAll 16846 71308 ns/op 62802 B/op 314 allocs/op
+```
+
+Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc
+
+NOTE: the allocs in the benchmark above are from the calls to http.Request's
+`WithContext(context.Context)` method that clones the http.Request, sets the `Context()`
+on the duplicated (alloc'd) request and returns it the new request object. This is just
+how setting context on a request in Go works.
+
+
+## Credits
+
+* Carl Jackson for https://github.com/zenazn/goji
+ * Parts of chi's thinking comes from goji, and chi's middleware package
+ sources from goji.
+* Armon Dadgar for https://github.com/armon/go-radix
+* Contributions: [@VojtechVitek](https://github.com/VojtechVitek)
+
+We'll be more than happy to see [your contributions](./CONTRIBUTING.md)!
+
+
+## Beyond REST
+
+chi is just a http router that lets you decompose request handling into many smaller layers.
+Many companies use chi to write REST services for their public APIs. But, REST is just a convention
+for managing state via HTTP, and there's a lot of other pieces required to write a complete client-server
+system or network of microservices.
+
+Looking beyond REST, I also recommend some newer works in the field:
+* [webrpc](https://github.com/webrpc/webrpc) - Web-focused RPC client+server framework with code-gen
+* [gRPC](https://github.com/grpc/grpc-go) - Google's RPC framework via protobufs
+* [graphql](https://github.com/99designs/gqlgen) - Declarative query language
+* [NATS](https://nats.io) - lightweight pub-sub
+
+
+## License
+
+Copyright (c) 2015-present [Peter Kieltyka](https://github.com/pkieltyka)
+
+Licensed under [MIT License](./LICENSE)
+
+[GoDoc]: https://pkg.go.dev/github.com/go-chi/chi?tab=versions
+[GoDoc Widget]: https://godoc.org/github.com/go-chi/chi?status.svg
+[Travis]: https://travis-ci.org/go-chi/chi
+[Travis Widget]: https://travis-ci.org/go-chi/chi.svg?branch=master
diff --git a/vendor/github.com/go-chi/chi/v5/chain.go b/vendor/github.com/go-chi/chi/v5/chain.go
new file mode 100644
index 000000000..a2278414f
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/chain.go
@@ -0,0 +1,49 @@
+package chi
+
+import "net/http"
+
+// Chain returns a Middlewares type from a slice of middleware handlers.
+func Chain(middlewares ...func(http.Handler) http.Handler) Middlewares {
+ return Middlewares(middlewares)
+}
+
+// Handler builds and returns a http.Handler from the chain of middlewares,
+// with `h http.Handler` as the final handler.
+func (mws Middlewares) Handler(h http.Handler) http.Handler {
+ return &ChainHandler{h, chain(mws, h), mws}
+}
+
+// HandlerFunc builds and returns a http.Handler from the chain of middlewares,
+// with `h http.Handler` as the final handler.
+func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler {
+ return &ChainHandler{h, chain(mws, h), mws}
+}
+
+// ChainHandler is a http.Handler with support for handler composition and
+// execution.
+type ChainHandler struct {
+ Endpoint http.Handler
+ chain http.Handler
+ Middlewares Middlewares
+}
+
+func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ c.chain.ServeHTTP(w, r)
+}
+
+// chain builds a http.Handler composed of an inline middleware stack and endpoint
+// handler in the order they are passed.
+func chain(middlewares []func(http.Handler) http.Handler, endpoint http.Handler) http.Handler {
+ // Return ahead of time if there aren't any middlewares for the chain
+ if len(middlewares) == 0 {
+ return endpoint
+ }
+
+ // Wrap the end handler with the middleware chain
+ h := middlewares[len(middlewares)-1](endpoint)
+ for i := len(middlewares) - 2; i >= 0; i-- {
+ h = middlewares[i](h)
+ }
+
+ return h
+}
diff --git a/vendor/github.com/go-chi/chi/v5/chi.go b/vendor/github.com/go-chi/chi/v5/chi.go
new file mode 100644
index 000000000..d2e5354dc
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/chi.go
@@ -0,0 +1,134 @@
+//
+// Package chi is a small, idiomatic and composable router for building HTTP services.
+//
+// chi requires Go 1.10 or newer.
+//
+// Example:
+// package main
+//
+// import (
+// "net/http"
+//
+// "github.com/go-chi/chi/v5"
+// "github.com/go-chi/chi/v5/middleware"
+// )
+//
+// func main() {
+// r := chi.NewRouter()
+// r.Use(middleware.Logger)
+// r.Use(middleware.Recoverer)
+//
+// r.Get("/", func(w http.ResponseWriter, r *http.Request) {
+// w.Write([]byte("root."))
+// })
+//
+// http.ListenAndServe(":3333", r)
+// }
+//
+// See github.com/go-chi/chi/_examples/ for more in-depth examples.
+//
+// URL patterns allow for easy matching of path components in HTTP
+// requests. The matching components can then be accessed using
+// chi.URLParam(). All patterns must begin with a slash.
+//
+// A simple named placeholder {name} matches any sequence of characters
+// up to the next / or the end of the URL. Trailing slashes on paths must
+// be handled explicitly.
+//
+// A placeholder with a name followed by a colon allows a regular
+// expression match, for example {number:\\d+}. The regular expression
+// syntax is Go's normal regexp RE2 syntax, except that regular expressions
+// including { or } are not supported, and / will never be
+// matched. An anonymous regexp pattern is allowed, using an empty string
+// before the colon in the placeholder, such as {:\\d+}
+//
+// The special placeholder of asterisk matches the rest of the requested
+// URL. Any trailing characters in the pattern are ignored. This is the only
+// placeholder which will match / characters.
+//
+// Examples:
+// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/"
+// "/user/{name}/info" matches "/user/jsmith/info"
+// "/page/*" matches "/page/intro/latest"
+// "/page/*/index" also matches "/page/intro/latest"
+// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01"
+//
+package chi
+
+import "net/http"
+
+// NewRouter returns a new Mux object that implements the Router interface.
+func NewRouter() *Mux {
+ return NewMux()
+}
+
+// Router consisting of the core routing methods used by chi's Mux,
+// using only the standard net/http.
+type Router interface {
+ http.Handler
+ Routes
+
+ // Use appends one or more middlewares onto the Router stack.
+ Use(middlewares ...func(http.Handler) http.Handler)
+
+ // With adds inline middlewares for an endpoint handler.
+ With(middlewares ...func(http.Handler) http.Handler) Router
+
+ // Group adds a new inline-Router along the current routing
+ // path, with a fresh middleware stack for the inline-Router.
+ Group(fn func(r Router)) Router
+
+ // Route mounts a sub-Router along a `pattern`` string.
+ Route(pattern string, fn func(r Router)) Router
+
+ // Mount attaches another http.Handler along ./pattern/*
+ Mount(pattern string, h http.Handler)
+
+ // Handle and HandleFunc adds routes for `pattern` that matches
+ // all HTTP methods.
+ Handle(pattern string, h http.Handler)
+ HandleFunc(pattern string, h http.HandlerFunc)
+
+ // Method and MethodFunc adds routes for `pattern` that matches
+ // the `method` HTTP method.
+ Method(method, pattern string, h http.Handler)
+ MethodFunc(method, pattern string, h http.HandlerFunc)
+
+ // HTTP-method routing along `pattern`
+ Connect(pattern string, h http.HandlerFunc)
+ Delete(pattern string, h http.HandlerFunc)
+ Get(pattern string, h http.HandlerFunc)
+ Head(pattern string, h http.HandlerFunc)
+ Options(pattern string, h http.HandlerFunc)
+ Patch(pattern string, h http.HandlerFunc)
+ Post(pattern string, h http.HandlerFunc)
+ Put(pattern string, h http.HandlerFunc)
+ Trace(pattern string, h http.HandlerFunc)
+
+ // NotFound defines a handler to respond whenever a route could
+ // not be found.
+ NotFound(h http.HandlerFunc)
+
+ // MethodNotAllowed defines a handler to respond whenever a method is
+ // not allowed.
+ MethodNotAllowed(h http.HandlerFunc)
+}
+
+// Routes interface adds two methods for router traversal, which is also
+// used by the `docgen` subpackage to generation documentation for Routers.
+type Routes interface {
+ // Routes returns the routing tree in an easily traversable structure.
+ Routes() []Route
+
+ // Middlewares returns the list of middlewares in use by the router.
+ Middlewares() Middlewares
+
+ // Match searches the routing tree for a handler that matches
+ // the method/path - similar to routing a http request, but without
+ // executing the handler thereafter.
+ Match(rctx *Context, method, path string) bool
+}
+
+// Middlewares type is a slice of standard middleware handlers with methods
+// to compose middleware chains and http.Handler's.
+type Middlewares []func(http.Handler) http.Handler
diff --git a/vendor/github.com/go-chi/chi/v5/context.go b/vendor/github.com/go-chi/chi/v5/context.go
new file mode 100644
index 000000000..814c26301
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/context.go
@@ -0,0 +1,157 @@
+package chi
+
+import (
+ "context"
+ "net/http"
+ "strings"
+)
+
+// URLParam returns the url parameter from a http.Request object.
+func URLParam(r *http.Request, key string) string {
+ if rctx := RouteContext(r.Context()); rctx != nil {
+ return rctx.URLParam(key)
+ }
+ return ""
+}
+
+// URLParamFromCtx returns the url parameter from a http.Request Context.
+func URLParamFromCtx(ctx context.Context, key string) string {
+ if rctx := RouteContext(ctx); rctx != nil {
+ return rctx.URLParam(key)
+ }
+ return ""
+}
+
+// RouteContext returns chi's routing Context object from a
+// http.Request Context.
+func RouteContext(ctx context.Context) *Context {
+ val, _ := ctx.Value(RouteCtxKey).(*Context)
+ return val
+}
+
+// NewRouteContext returns a new routing Context object.
+func NewRouteContext() *Context {
+ return &Context{}
+}
+
+var (
+ // RouteCtxKey is the context.Context key to store the request context.
+ RouteCtxKey = &contextKey{"RouteContext"}
+)
+
+// Context is the default routing context set on the root node of a
+// request context to track route patterns, URL parameters and
+// an optional routing path.
+type Context struct {
+ Routes Routes
+
+ // parentCtx is the parent of this one, for using Context as a
+ // context.Context directly. This is an optimization that saves
+ // 1 allocation.
+ parentCtx context.Context
+
+ // Routing path/method override used during the route search.
+ // See Mux#routeHTTP method.
+ RoutePath string
+ RouteMethod string
+
+ // URLParams are the stack of routeParams captured during the
+ // routing lifecycle across a stack of sub-routers.
+ URLParams RouteParams
+
+ // Route parameters matched for the current sub-router. It is
+ // intentionally unexported so it cant be tampered.
+ routeParams RouteParams
+
+ // The endpoint routing pattern that matched the request URI path
+ // or `RoutePath` of the current sub-router. This value will update
+ // during the lifecycle of a request passing through a stack of
+ // sub-routers.
+ routePattern string
+
+ // Routing pattern stack throughout the lifecycle of the request,
+ // across all connected routers. It is a record of all matching
+ // patterns across a stack of sub-routers.
+ RoutePatterns []string
+
+ // methodNotAllowed hint
+ methodNotAllowed bool
+}
+
+// Reset a routing context to its initial state.
+func (x *Context) Reset() {
+ x.Routes = nil
+ x.RoutePath = ""
+ x.RouteMethod = ""
+ x.RoutePatterns = x.RoutePatterns[:0]
+ x.URLParams.Keys = x.URLParams.Keys[:0]
+ x.URLParams.Values = x.URLParams.Values[:0]
+
+ x.routePattern = ""
+ x.routeParams.Keys = x.routeParams.Keys[:0]
+ x.routeParams.Values = x.routeParams.Values[:0]
+ x.methodNotAllowed = false
+ x.parentCtx = nil
+}
+
+// URLParam returns the corresponding URL parameter value from the request
+// routing context.
+func (x *Context) URLParam(key string) string {
+ for k := len(x.URLParams.Keys) - 1; k >= 0; k-- {
+ if x.URLParams.Keys[k] == key {
+ return x.URLParams.Values[k]
+ }
+ }
+ return ""
+}
+
+// RoutePattern builds the routing pattern string for the particular
+// request, at the particular point during routing. This means, the value
+// will change throughout the execution of a request in a router. That is
+// why its advised to only use this value after calling the next handler.
+//
+// For example,
+//
+// func Instrument(next http.Handler) http.Handler {
+// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+// next.ServeHTTP(w, r)
+// routePattern := chi.RouteContext(r.Context()).RoutePattern()
+// measure(w, r, routePattern)
+// })
+// }
+func (x *Context) RoutePattern() string {
+ routePattern := strings.Join(x.RoutePatterns, "")
+ return replaceWildcards(routePattern)
+}
+
+// replaceWildcards takes a route pattern and recursively replaces all
+// occurrences of "/*/" to "/".
+func replaceWildcards(p string) string {
+ if strings.Contains(p, "/*/") {
+ return replaceWildcards(strings.Replace(p, "/*/", "/", -1))
+ }
+
+ return p
+}
+
+// RouteParams is a structure to track URL routing parameters efficiently.
+type RouteParams struct {
+ Keys, Values []string
+}
+
+// Add will append a URL parameter to the end of the route param
+func (s *RouteParams) Add(key, value string) {
+ s.Keys = append(s.Keys, key)
+ s.Values = append(s.Values, value)
+}
+
+// contextKey is a value for use with context.WithValue. It's used as
+// a pointer so it fits in an interface{} without allocation. This technique
+// for defining context keys was copied from Go 1.7's new use of context in net/http.
+type contextKey struct {
+ name string
+}
+
+func (k *contextKey) String() string {
+ return "chi context value " + k.name
+}
diff --git a/vendor/github.com/go-chi/chi/v5/go.mod b/vendor/github.com/go-chi/chi/v5/go.mod
new file mode 100644
index 000000000..c084c1d5e
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/go.mod
@@ -0,0 +1,3 @@
+module github.com/go-chi/chi/v5
+
+go 1.14
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go b/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go
new file mode 100644
index 000000000..a546c9e9e
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go
@@ -0,0 +1,33 @@
+package middleware
+
+import (
+ "crypto/subtle"
+ "fmt"
+ "net/http"
+)
+
+// BasicAuth implements a simple middleware handler for adding basic http auth to a route.
+func BasicAuth(realm string, creds map[string]string) func(next http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ user, pass, ok := r.BasicAuth()
+ if !ok {
+ basicAuthFailed(w, realm)
+ return
+ }
+
+ credPass, credUserOk := creds[user]
+ if !credUserOk || subtle.ConstantTimeCompare([]byte(pass), []byte(credPass)) != 1 {
+ basicAuthFailed(w, realm)
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+ }
+}
+
+func basicAuthFailed(w http.ResponseWriter, realm string) {
+ w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
+ w.WriteHeader(http.StatusUnauthorized)
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go b/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go
new file mode 100644
index 000000000..adeba4295
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go
@@ -0,0 +1,28 @@
+package middleware
+
+import (
+ "net/http"
+ "path"
+
+ "github.com/go-chi/chi/v5"
+)
+
+// CleanPath middleware will clean out double slash mistakes from a user's request path.
+// For example, if a user requests /users//1 or //users////1 will both be treated as: /users/1
+func CleanPath(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ rctx := chi.RouteContext(r.Context())
+
+ routePath := rctx.RoutePath
+ if routePath == "" {
+ if r.URL.RawPath != "" {
+ routePath = r.URL.RawPath
+ } else {
+ routePath = r.URL.Path
+ }
+ rctx.RoutePath = path.Clean(routePath)
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/compress.go b/vendor/github.com/go-chi/chi/v5/middleware/compress.go
new file mode 100644
index 000000000..6cafddc7c
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/compress.go
@@ -0,0 +1,399 @@
+package middleware
+
+import (
+ "bufio"
+ "compress/flate"
+ "compress/gzip"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "strings"
+ "sync"
+)
+
+var defaultCompressibleContentTypes = []string{
+ "text/html",
+ "text/css",
+ "text/plain",
+ "text/javascript",
+ "application/javascript",
+ "application/x-javascript",
+ "application/json",
+ "application/atom+xml",
+ "application/rss+xml",
+ "image/svg+xml",
+}
+
+// Compress is a middleware that compresses response
+// body of a given content types to a data format based
+// on Accept-Encoding request header. It uses a given
+// compression level.
+//
+// NOTE: make sure to set the Content-Type header on your response
+// otherwise this middleware will not compress the response body. For ex, in
+// your handler you should set w.Header().Set("Content-Type", http.DetectContentType(yourBody))
+// or set it manually.
+//
+// Passing a compression level of 5 is sensible value
+func Compress(level int, types ...string) func(next http.Handler) http.Handler {
+ compressor := NewCompressor(level, types...)
+ return compressor.Handler
+}
+
+// Compressor represents a set of encoding configurations.
+type Compressor struct {
+ // The mapping of encoder names to encoder functions.
+ encoders map[string]EncoderFunc
+ // The mapping of pooled encoders to pools.
+ pooledEncoders map[string]*sync.Pool
+ // The set of content types allowed to be compressed.
+ allowedTypes map[string]struct{}
+ allowedWildcards map[string]struct{}
+ // The list of encoders in order of decreasing precedence.
+ encodingPrecedence []string
+ level int // The compression level.
+}
+
+// NewCompressor creates a new Compressor that will handle encoding responses.
+//
+// The level should be one of the ones defined in the flate package.
+// The types are the content types that are allowed to be compressed.
+func NewCompressor(level int, types ...string) *Compressor {
+ // If types are provided, set those as the allowed types. If none are
+ // provided, use the default list.
+ allowedTypes := make(map[string]struct{})
+ allowedWildcards := make(map[string]struct{})
+ if len(types) > 0 {
+ for _, t := range types {
+ if strings.Contains(strings.TrimSuffix(t, "/*"), "*") {
+ panic(fmt.Sprintf("middleware/compress: Unsupported content-type wildcard pattern '%s'. Only '/*' supported", t))
+ }
+ if strings.HasSuffix(t, "/*") {
+ allowedWildcards[strings.TrimSuffix(t, "/*")] = struct{}{}
+ } else {
+ allowedTypes[t] = struct{}{}
+ }
+ }
+ } else {
+ for _, t := range defaultCompressibleContentTypes {
+ allowedTypes[t] = struct{}{}
+ }
+ }
+
+ c := &Compressor{
+ level: level,
+ encoders: make(map[string]EncoderFunc),
+ pooledEncoders: make(map[string]*sync.Pool),
+ allowedTypes: allowedTypes,
+ allowedWildcards: allowedWildcards,
+ }
+
+ // Set the default encoders. The precedence order uses the reverse
+ // ordering that the encoders were added. This means adding new encoders
+ // will move them to the front of the order.
+ //
+ // TODO:
+ // lzma: Opera.
+ // sdch: Chrome, Android. Gzip output + dictionary header.
+ // br: Brotli, see https://github.com/go-chi/chi/pull/326
+
+ // HTTP 1.1 "deflate" (RFC 2616) stands for DEFLATE data (RFC 1951)
+ // wrapped with zlib (RFC 1950). The zlib wrapper uses Adler-32
+ // checksum compared to CRC-32 used in "gzip" and thus is faster.
+ //
+ // But.. some old browsers (MSIE, Safari 5.1) incorrectly expect
+ // raw DEFLATE data only, without the mentioned zlib wrapper.
+ // Because of this major confusion, most modern browsers try it
+ // both ways, first looking for zlib headers.
+ // Quote by Mark Adler: http://stackoverflow.com/a/9186091/385548
+ //
+ // The list of browsers having problems is quite big, see:
+ // http://zoompf.com/blog/2012/02/lose-the-wait-http-compression
+ // https://web.archive.org/web/20120321182910/http://www.vervestudios.co/projects/compression-tests/results
+ //
+ // That's why we prefer gzip over deflate. It's just more reliable
+ // and not significantly slower than deflate.
+ c.SetEncoder("deflate", encoderDeflate)
+
+ // TODO: Exception for old MSIE browsers that can't handle non-HTML?
+ // https://zoompf.com/blog/2012/02/lose-the-wait-http-compression
+ c.SetEncoder("gzip", encoderGzip)
+
+ // NOTE: Not implemented, intentionally:
+ // case "compress": // LZW. Deprecated.
+ // case "bzip2": // Too slow on-the-fly.
+ // case "zopfli": // Too slow on-the-fly.
+ // case "xz": // Too slow on-the-fly.
+ return c
+}
+
+// SetEncoder can be used to set the implementation of a compression algorithm.
+//
+// The encoding should be a standardised identifier. See:
+// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
+//
+// For example, add the Brotli algortithm:
+//
+// import brotli_enc "gopkg.in/kothar/brotli-go.v0/enc"
+//
+// compressor := middleware.NewCompressor(5, "text/html")
+// compressor.SetEncoder("br", func(w http.ResponseWriter, level int) io.Writer {
+// params := brotli_enc.NewBrotliParams()
+// params.SetQuality(level)
+// return brotli_enc.NewBrotliWriter(params, w)
+// })
+func (c *Compressor) SetEncoder(encoding string, fn EncoderFunc) {
+ encoding = strings.ToLower(encoding)
+ if encoding == "" {
+ panic("the encoding can not be empty")
+ }
+ if fn == nil {
+ panic("attempted to set a nil encoder function")
+ }
+
+ // If we are adding a new encoder that is already registered, we have to
+ // clear that one out first.
+ if _, ok := c.pooledEncoders[encoding]; ok {
+ delete(c.pooledEncoders, encoding)
+ }
+ if _, ok := c.encoders[encoding]; ok {
+ delete(c.encoders, encoding)
+ }
+
+ // If the encoder supports Resetting (IoReseterWriter), then it can be pooled.
+ encoder := fn(ioutil.Discard, c.level)
+ if encoder != nil {
+ if _, ok := encoder.(ioResetterWriter); ok {
+ pool := &sync.Pool{
+ New: func() interface{} {
+ return fn(ioutil.Discard, c.level)
+ },
+ }
+ c.pooledEncoders[encoding] = pool
+ }
+ }
+ // If the encoder is not in the pooledEncoders, add it to the normal encoders.
+ if _, ok := c.pooledEncoders[encoding]; !ok {
+ c.encoders[encoding] = fn
+ }
+
+ for i, v := range c.encodingPrecedence {
+ if v == encoding {
+ c.encodingPrecedence = append(c.encodingPrecedence[:i], c.encodingPrecedence[i+1:]...)
+ }
+ }
+
+ c.encodingPrecedence = append([]string{encoding}, c.encodingPrecedence...)
+}
+
+// Handler returns a new middleware that will compress the response based on the
+// current Compressor.
+func (c *Compressor) Handler(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ encoder, encoding, cleanup := c.selectEncoder(r.Header, w)
+
+ cw := &compressResponseWriter{
+ ResponseWriter: w,
+ w: w,
+ contentTypes: c.allowedTypes,
+ contentWildcards: c.allowedWildcards,
+ encoding: encoding,
+ compressable: false, // determined in post-handler
+ }
+ if encoder != nil {
+ cw.w = encoder
+ }
+ // Re-add the encoder to the pool if applicable.
+ defer cleanup()
+ defer cw.Close()
+
+ next.ServeHTTP(cw, r)
+ })
+}
+
+// selectEncoder returns the encoder, the name of the encoder, and a closer function.
+func (c *Compressor) selectEncoder(h http.Header, w io.Writer) (io.Writer, string, func()) {
+ header := h.Get("Accept-Encoding")
+
+ // Parse the names of all accepted algorithms from the header.
+ accepted := strings.Split(strings.ToLower(header), ",")
+
+ // Find supported encoder by accepted list by precedence
+ for _, name := range c.encodingPrecedence {
+ if matchAcceptEncoding(accepted, name) {
+ if pool, ok := c.pooledEncoders[name]; ok {
+ encoder := pool.Get().(ioResetterWriter)
+ cleanup := func() {
+ pool.Put(encoder)
+ }
+ encoder.Reset(w)
+ return encoder, name, cleanup
+
+ }
+ if fn, ok := c.encoders[name]; ok {
+ return fn(w, c.level), name, func() {}
+ }
+ }
+
+ }
+
+ // No encoder found to match the accepted encoding
+ return nil, "", func() {}
+}
+
+func matchAcceptEncoding(accepted []string, encoding string) bool {
+ for _, v := range accepted {
+ if strings.Contains(v, encoding) {
+ return true
+ }
+ }
+ return false
+}
+
+// An EncoderFunc is a function that wraps the provided io.Writer with a
+// streaming compression algorithm and returns it.
+//
+// In case of failure, the function should return nil.
+type EncoderFunc func(w io.Writer, level int) io.Writer
+
+// Interface for types that allow resetting io.Writers.
+type ioResetterWriter interface {
+ io.Writer
+ Reset(w io.Writer)
+}
+
+type compressResponseWriter struct {
+ http.ResponseWriter
+
+ // The streaming encoder writer to be used if there is one. Otherwise,
+ // this is just the normal writer.
+ w io.Writer
+ contentTypes map[string]struct{}
+ contentWildcards map[string]struct{}
+ encoding string
+ wroteHeader bool
+ compressable bool
+}
+
+func (cw *compressResponseWriter) isCompressable() bool {
+ // Parse the first part of the Content-Type response header.
+ contentType := cw.Header().Get("Content-Type")
+ if idx := strings.Index(contentType, ";"); idx >= 0 {
+ contentType = contentType[0:idx]
+ }
+
+ // Is the content type compressable?
+ if _, ok := cw.contentTypes[contentType]; ok {
+ return true
+ }
+ if idx := strings.Index(contentType, "/"); idx > 0 {
+ contentType = contentType[0:idx]
+ _, ok := cw.contentWildcards[contentType]
+ return ok
+ }
+ return false
+}
+
+func (cw *compressResponseWriter) WriteHeader(code int) {
+ if cw.wroteHeader {
+ cw.ResponseWriter.WriteHeader(code) // Allow multiple calls to propagate.
+ return
+ }
+ cw.wroteHeader = true
+ defer cw.ResponseWriter.WriteHeader(code)
+
+ // Already compressed data?
+ if cw.Header().Get("Content-Encoding") != "" {
+ return
+ }
+
+ if !cw.isCompressable() {
+ cw.compressable = false
+ return
+ }
+
+ if cw.encoding != "" {
+ cw.compressable = true
+ cw.Header().Set("Content-Encoding", cw.encoding)
+ cw.Header().Set("Vary", "Accept-Encoding")
+
+ // The content-length after compression is unknown
+ cw.Header().Del("Content-Length")
+ }
+}
+
+func (cw *compressResponseWriter) Write(p []byte) (int, error) {
+ if !cw.wroteHeader {
+ cw.WriteHeader(http.StatusOK)
+ }
+
+ return cw.writer().Write(p)
+}
+
+func (cw *compressResponseWriter) writer() io.Writer {
+ if cw.compressable {
+ return cw.w
+ } else {
+ return cw.ResponseWriter
+ }
+}
+
+type compressFlusher interface {
+ Flush() error
+}
+
+func (cw *compressResponseWriter) Flush() {
+ if f, ok := cw.writer().(http.Flusher); ok {
+ f.Flush()
+ }
+ // If the underlying writer has a compression flush signature,
+ // call this Flush() method instead
+ if f, ok := cw.writer().(compressFlusher); ok {
+ f.Flush()
+
+ // Also flush the underlying response writer
+ if f, ok := cw.ResponseWriter.(http.Flusher); ok {
+ f.Flush()
+ }
+ }
+}
+
+func (cw *compressResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ if hj, ok := cw.writer().(http.Hijacker); ok {
+ return hj.Hijack()
+ }
+ return nil, nil, errors.New("chi/middleware: http.Hijacker is unavailable on the writer")
+}
+
+func (cw *compressResponseWriter) Push(target string, opts *http.PushOptions) error {
+ if ps, ok := cw.writer().(http.Pusher); ok {
+ return ps.Push(target, opts)
+ }
+ return errors.New("chi/middleware: http.Pusher is unavailable on the writer")
+}
+
+func (cw *compressResponseWriter) Close() error {
+ if c, ok := cw.writer().(io.WriteCloser); ok {
+ return c.Close()
+ }
+ return errors.New("chi/middleware: io.WriteCloser is unavailable on the writer")
+}
+
+func encoderGzip(w io.Writer, level int) io.Writer {
+ gw, err := gzip.NewWriterLevel(w, level)
+ if err != nil {
+ return nil
+ }
+ return gw
+}
+
+func encoderDeflate(w io.Writer, level int) io.Writer {
+ dw, err := flate.NewWriter(w, level)
+ if err != nil {
+ return nil
+ }
+ return dw
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go b/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go
new file mode 100644
index 000000000..07b5ce6f6
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go
@@ -0,0 +1,51 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+// ContentCharset generates a handler that writes a 415 Unsupported Media Type response if none of the charsets match.
+// An empty charset will allow requests with no Content-Type header or no specified charset.
+func ContentCharset(charsets ...string) func(next http.Handler) http.Handler {
+ for i, c := range charsets {
+ charsets[i] = strings.ToLower(c)
+ }
+
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if !contentEncoding(r.Header.Get("Content-Type"), charsets...) {
+ w.WriteHeader(http.StatusUnsupportedMediaType)
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ })
+ }
+}
+
+// Check the content encoding against a list of acceptable values.
+func contentEncoding(ce string, charsets ...string) bool {
+ _, ce = split(strings.ToLower(ce), ";")
+ _, ce = split(ce, "charset=")
+ ce, _ = split(ce, ";")
+ for _, c := range charsets {
+ if ce == c {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Split a string in two parts, cleaning any whitespace.
+func split(str, sep string) (string, string) {
+ var a, b string
+ var parts = strings.SplitN(str, sep, 2)
+ a = strings.TrimSpace(parts[0])
+ if len(parts) == 2 {
+ b = strings.TrimSpace(parts[1])
+ }
+
+ return a, b
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go b/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go
new file mode 100644
index 000000000..e0b9ccc08
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go
@@ -0,0 +1,34 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+// AllowContentEncoding enforces a whitelist of request Content-Encoding otherwise responds
+// with a 415 Unsupported Media Type status.
+func AllowContentEncoding(contentEncoding ...string) func(next http.Handler) http.Handler {
+ allowedEncodings := make(map[string]struct{}, len(contentEncoding))
+ for _, encoding := range contentEncoding {
+ allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))] = struct{}{}
+ }
+ return func(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ requestEncodings := r.Header["Content-Encoding"]
+ // skip check for empty content body or no Content-Encoding
+ if r.ContentLength == 0 {
+ next.ServeHTTP(w, r)
+ return
+ }
+ // All encodings in the request must be allowed
+ for _, encoding := range requestEncodings {
+ if _, ok := allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))]; !ok {
+ w.WriteHeader(http.StatusUnsupportedMediaType)
+ return
+ }
+ }
+ next.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/content_type.go b/vendor/github.com/go-chi/chi/v5/middleware/content_type.go
new file mode 100644
index 000000000..023978fac
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/content_type.go
@@ -0,0 +1,49 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+// SetHeader is a convenience handler to set a response header key/value
+func SetHeader(key, value string) func(next http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set(key, value)
+ next.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+ }
+}
+
+// AllowContentType enforces a whitelist of request Content-Types otherwise responds
+// with a 415 Unsupported Media Type status.
+func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler {
+ allowedContentTypes := make(map[string]struct{}, len(contentTypes))
+ for _, ctype := range contentTypes {
+ allowedContentTypes[strings.TrimSpace(strings.ToLower(ctype))] = struct{}{}
+ }
+
+ return func(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ if r.ContentLength == 0 {
+ // skip check for empty content body
+ next.ServeHTTP(w, r)
+ return
+ }
+
+ s := strings.ToLower(strings.TrimSpace(r.Header.Get("Content-Type")))
+ if i := strings.Index(s, ";"); i > -1 {
+ s = s[0:i]
+ }
+
+ if _, ok := allowedContentTypes[s]; ok {
+ next.ServeHTTP(w, r)
+ return
+ }
+
+ w.WriteHeader(http.StatusUnsupportedMediaType)
+ }
+ return http.HandlerFunc(fn)
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/get_head.go b/vendor/github.com/go-chi/chi/v5/middleware/get_head.go
new file mode 100644
index 000000000..d4606d8be
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/get_head.go
@@ -0,0 +1,39 @@
+package middleware
+
+import (
+ "net/http"
+
+ "github.com/go-chi/chi/v5"
+)
+
+// GetHead automatically route undefined HEAD requests to GET handlers.
+func GetHead(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "HEAD" {
+ rctx := chi.RouteContext(r.Context())
+ routePath := rctx.RoutePath
+ if routePath == "" {
+ if r.URL.RawPath != "" {
+ routePath = r.URL.RawPath
+ } else {
+ routePath = r.URL.Path
+ }
+ }
+
+ // Temporary routing context to look-ahead before routing the request
+ tctx := chi.NewRouteContext()
+
+ // Attempt to find a HEAD handler for the routing path, if not found, traverse
+ // the router as through its a GET route, but proceed with the request
+ // with the HEAD method.
+ if !rctx.Routes.Match(tctx, "HEAD", routePath) {
+ rctx.RouteMethod = "GET"
+ rctx.RoutePath = routePath
+ next.ServeHTTP(w, r)
+ return
+ }
+ }
+
+ next.ServeHTTP(w, r)
+ })
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go b/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go
new file mode 100644
index 000000000..f36e8ccf2
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go
@@ -0,0 +1,26 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+// Heartbeat endpoint middleware useful to setting up a path like
+// `/ping` that load balancers or uptime testing external services
+// can make a request before hitting any routes. It's also convenient
+// to place this above ACL middlewares as well.
+func Heartbeat(endpoint string) func(http.Handler) http.Handler {
+ f := func(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ if (r.Method == "GET" || r.Method == "HEAD") && strings.EqualFold(r.URL.Path, endpoint) {
+ w.Header().Set("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte("."))
+ return
+ }
+ h.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+ }
+ return f
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/logger.go b/vendor/github.com/go-chi/chi/v5/middleware/logger.go
new file mode 100644
index 000000000..66edc3dda
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/logger.go
@@ -0,0 +1,174 @@
+package middleware
+
+import (
+ "bytes"
+ "context"
+ "log"
+ "net/http"
+ "os"
+ "runtime"
+ "time"
+)
+
+var (
+ // LogEntryCtxKey is the context.Context key to store the request log entry.
+ LogEntryCtxKey = &contextKey{"LogEntry"}
+
+ // DefaultLogger is called by the Logger middleware handler to log each request.
+ // Its made a package-level variable so that it can be reconfigured for custom
+ // logging configurations.
+ DefaultLogger func(next http.Handler) http.Handler
+)
+
+// Logger is a middleware that logs the start and end of each request, along
+// with some useful data about what was requested, what the response status was,
+// and how long it took to return. When standard output is a TTY, Logger will
+// print in color, otherwise it will print in black and white. Logger prints a
+// request ID if one is provided.
+//
+// Alternatively, look at https://github.com/goware/httplog for a more in-depth
+// http logger with structured logging support.
+//
+// IMPORTANT NOTE: Logger should go before any other middleware that may change
+// the response, such as `middleware.Recoverer`. Example:
+//
+// ```go
+// r := chi.NewRouter()
+// r.Use(middleware.Logger) // <--<< Logger should come before Recoverer
+// r.Use(middleware.Recoverer)
+// r.Get("/", handler)
+// ```
+func Logger(next http.Handler) http.Handler {
+ return DefaultLogger(next)
+}
+
+// RequestLogger returns a logger handler using a custom LogFormatter.
+func RequestLogger(f LogFormatter) func(next http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ entry := f.NewLogEntry(r)
+ ww := NewWrapResponseWriter(w, r.ProtoMajor)
+
+ t1 := time.Now()
+ defer func() {
+ entry.Write(ww.Status(), ww.BytesWritten(), ww.Header(), time.Since(t1), nil)
+ }()
+
+ next.ServeHTTP(ww, WithLogEntry(r, entry))
+ }
+ return http.HandlerFunc(fn)
+ }
+}
+
+// LogFormatter initiates the beginning of a new LogEntry per request.
+// See DefaultLogFormatter for an example implementation.
+type LogFormatter interface {
+ NewLogEntry(r *http.Request) LogEntry
+}
+
+// LogEntry records the final log when a request completes.
+// See defaultLogEntry for an example implementation.
+type LogEntry interface {
+ Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{})
+ Panic(v interface{}, stack []byte)
+}
+
+// GetLogEntry returns the in-context LogEntry for a request.
+func GetLogEntry(r *http.Request) LogEntry {
+ entry, _ := r.Context().Value(LogEntryCtxKey).(LogEntry)
+ return entry
+}
+
+// WithLogEntry sets the in-context LogEntry for a request.
+func WithLogEntry(r *http.Request, entry LogEntry) *http.Request {
+ r = r.WithContext(context.WithValue(r.Context(), LogEntryCtxKey, entry))
+ return r
+}
+
+// LoggerInterface accepts printing to stdlib logger or compatible logger.
+type LoggerInterface interface {
+ Print(v ...interface{})
+}
+
+// DefaultLogFormatter is a simple logger that implements a LogFormatter.
+type DefaultLogFormatter struct {
+ Logger LoggerInterface
+ NoColor bool
+}
+
+// NewLogEntry creates a new LogEntry for the request.
+func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry {
+ useColor := !l.NoColor
+ entry := &defaultLogEntry{
+ DefaultLogFormatter: l,
+ request: r,
+ buf: &bytes.Buffer{},
+ useColor: useColor,
+ }
+
+ reqID := GetReqID(r.Context())
+ if reqID != "" {
+ cW(entry.buf, useColor, nYellow, "[%s] ", reqID)
+ }
+ cW(entry.buf, useColor, nCyan, "\"")
+ cW(entry.buf, useColor, bMagenta, "%s ", r.Method)
+
+ scheme := "http"
+ if r.TLS != nil {
+ scheme = "https"
+ }
+ cW(entry.buf, useColor, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto)
+
+ entry.buf.WriteString("from ")
+ entry.buf.WriteString(r.RemoteAddr)
+ entry.buf.WriteString(" - ")
+
+ return entry
+}
+
+type defaultLogEntry struct {
+ *DefaultLogFormatter
+ request *http.Request
+ buf *bytes.Buffer
+ useColor bool
+}
+
+func (l *defaultLogEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
+ switch {
+ case status < 200:
+ cW(l.buf, l.useColor, bBlue, "%03d", status)
+ case status < 300:
+ cW(l.buf, l.useColor, bGreen, "%03d", status)
+ case status < 400:
+ cW(l.buf, l.useColor, bCyan, "%03d", status)
+ case status < 500:
+ cW(l.buf, l.useColor, bYellow, "%03d", status)
+ default:
+ cW(l.buf, l.useColor, bRed, "%03d", status)
+ }
+
+ cW(l.buf, l.useColor, bBlue, " %dB", bytes)
+
+ l.buf.WriteString(" in ")
+ if elapsed < 500*time.Millisecond {
+ cW(l.buf, l.useColor, nGreen, "%s", elapsed)
+ } else if elapsed < 5*time.Second {
+ cW(l.buf, l.useColor, nYellow, "%s", elapsed)
+ } else {
+ cW(l.buf, l.useColor, nRed, "%s", elapsed)
+ }
+
+ l.Logger.Print(l.buf.String())
+}
+
+func (l *defaultLogEntry) Panic(v interface{}, stack []byte) {
+ PrintPrettyStack(v)
+}
+
+func init() {
+ color := true
+ if runtime.GOOS == "windows" {
+ color = false
+ }
+ DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: !color})
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/maybe.go b/vendor/github.com/go-chi/chi/v5/middleware/maybe.go
new file mode 100644
index 000000000..d8ca63b91
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/maybe.go
@@ -0,0 +1,18 @@
+package middleware
+
+import "net/http"
+
+// Maybe middleware will allow you to change the flow of the middleware stack execution depending on return
+// value of maybeFn(request). This is useful for example if you'd like to skip a middleware handler if
+// a request does not satisfied the maybeFn logic.
+func Maybe(mw func(http.Handler) http.Handler, maybeFn func(r *http.Request) bool) func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if maybeFn(r) {
+ mw(next).ServeHTTP(w, r)
+ } else {
+ next.ServeHTTP(w, r)
+ }
+ })
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/middleware.go b/vendor/github.com/go-chi/chi/v5/middleware/middleware.go
new file mode 100644
index 000000000..cc371e00a
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/middleware.go
@@ -0,0 +1,23 @@
+package middleware
+
+import "net/http"
+
+// New will create a new middleware handler from a http.Handler.
+func New(h http.Handler) func(next http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ h.ServeHTTP(w, r)
+ })
+ }
+}
+
+// contextKey is a value for use with context.WithValue. It's used as
+// a pointer so it fits in an interface{} without allocation. This technique
+// for defining context keys was copied from Go 1.7's new use of context in net/http.
+type contextKey struct {
+ name string
+}
+
+func (k *contextKey) String() string {
+ return "chi/middleware context value " + k.name
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/nocache.go b/vendor/github.com/go-chi/chi/v5/middleware/nocache.go
new file mode 100644
index 000000000..2412829e1
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/nocache.go
@@ -0,0 +1,58 @@
+package middleware
+
+// Ported from Goji's middleware, source:
+// https://github.com/zenazn/goji/tree/master/web/middleware
+
+import (
+ "net/http"
+ "time"
+)
+
+// Unix epoch time
+var epoch = time.Unix(0, 0).Format(time.RFC1123)
+
+// Taken from https://github.com/mytrile/nocache
+var noCacheHeaders = map[string]string{
+ "Expires": epoch,
+ "Cache-Control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0",
+ "Pragma": "no-cache",
+ "X-Accel-Expires": "0",
+}
+
+var etagHeaders = []string{
+ "ETag",
+ "If-Modified-Since",
+ "If-Match",
+ "If-None-Match",
+ "If-Range",
+ "If-Unmodified-Since",
+}
+
+// NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent
+// a router (or subrouter) from being cached by an upstream proxy and/or client.
+//
+// As per http://wiki.nginx.org/HttpProxyModule - NoCache sets:
+// Expires: Thu, 01 Jan 1970 00:00:00 UTC
+// Cache-Control: no-cache, private, max-age=0
+// X-Accel-Expires: 0
+// Pragma: no-cache (for HTTP/1.0 proxies/clients)
+func NoCache(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+
+ // Delete any ETag headers that may have been set
+ for _, v := range etagHeaders {
+ if r.Header.Get(v) != "" {
+ r.Header.Del(v)
+ }
+ }
+
+ // Set our NoCache headers
+ for k, v := range noCacheHeaders {
+ w.Header().Set(k, v)
+ }
+
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/page_route.go b/vendor/github.com/go-chi/chi/v5/middleware/page_route.go
new file mode 100644
index 000000000..32871b7e4
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/page_route.go
@@ -0,0 +1,20 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+// PageRoute is a simple middleware which allows you to route a static GET request
+// at the middleware stack level.
+func PageRoute(path string, handler http.Handler) func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "GET" && strings.EqualFold(r.URL.Path, path) {
+ handler.ServeHTTP(w, r)
+ return
+ }
+ next.ServeHTTP(w, r)
+ })
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go b/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go
new file mode 100644
index 000000000..99af62c0c
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go
@@ -0,0 +1,16 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+// PathRewrite is a simple middleware which allows you to rewrite the request URL path.
+func PathRewrite(old, new string) func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ r.URL.Path = strings.Replace(r.URL.Path, old, new, 1)
+ next.ServeHTTP(w, r)
+ })
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/profiler.go b/vendor/github.com/go-chi/chi/v5/middleware/profiler.go
new file mode 100644
index 000000000..3c36f878f
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/profiler.go
@@ -0,0 +1,62 @@
+package middleware
+
+import (
+ "expvar"
+ "fmt"
+ "net/http"
+ "net/http/pprof"
+
+ "github.com/go-chi/chi/v5"
+)
+
+// Profiler is a convenient subrouter used for mounting net/http/pprof. ie.
+//
+// func MyService() http.Handler {
+// r := chi.NewRouter()
+// // ..middlewares
+// r.Mount("/debug", middleware.Profiler())
+// // ..routes
+// return r
+// }
+func Profiler() http.Handler {
+ r := chi.NewRouter()
+ r.Use(NoCache)
+
+ r.Get("/", func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, r.RequestURI+"/pprof/", http.StatusMovedPermanently)
+ })
+ r.HandleFunc("/pprof", func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, r.RequestURI+"/", http.StatusMovedPermanently)
+ })
+
+ r.HandleFunc("/pprof/*", pprof.Index)
+ r.HandleFunc("/pprof/cmdline", pprof.Cmdline)
+ r.HandleFunc("/pprof/profile", pprof.Profile)
+ r.HandleFunc("/pprof/symbol", pprof.Symbol)
+ r.HandleFunc("/pprof/trace", pprof.Trace)
+ r.HandleFunc("/vars", expVars)
+
+ r.Handle("/pprof/goroutine", pprof.Handler("goroutine"))
+ r.Handle("/pprof/threadcreate", pprof.Handler("threadcreate"))
+ r.Handle("/pprof/mutex", pprof.Handler("mutex"))
+ r.Handle("/pprof/heap", pprof.Handler("heap"))
+ r.Handle("/pprof/block", pprof.Handler("block"))
+ r.Handle("/pprof/allocs", pprof.Handler("allocs"))
+
+ return r
+}
+
+// Replicated from expvar.go as not public.
+func expVars(w http.ResponseWriter, r *http.Request) {
+ first := true
+ w.Header().Set("Content-Type", "application/json")
+ fmt.Fprintf(w, "{\n")
+ expvar.Do(func(kv expvar.KeyValue) {
+ if !first {
+ fmt.Fprintf(w, ",\n")
+ }
+ first = false
+ fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
+ })
+ fmt.Fprintf(w, "\n}\n")
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/realip.go b/vendor/github.com/go-chi/chi/v5/middleware/realip.go
new file mode 100644
index 000000000..efc1894f2
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/realip.go
@@ -0,0 +1,57 @@
+package middleware
+
+// Ported from Goji's middleware, source:
+// https://github.com/zenazn/goji/tree/master/web/middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+var trueClientIP = http.CanonicalHeaderKey("True-Client-IP")
+var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For")
+var xRealIP = http.CanonicalHeaderKey("X-Real-IP")
+
+// RealIP is a middleware that sets a http.Request's RemoteAddr to the results
+// of parsing either the True-Client-IP, X-Real-IP or the X-Forwarded-For headers
+// (in that order).
+//
+// This middleware should be inserted fairly early in the middleware stack to
+// ensure that subsequent layers (e.g., request loggers) which examine the
+// RemoteAddr will see the intended value.
+//
+// You should only use this middleware if you can trust the headers passed to
+// you (in particular, the two headers this middleware uses), for example
+// because you have placed a reverse proxy like HAProxy or nginx in front of
+// chi. If your reverse proxies are configured to pass along arbitrary header
+// values from the client, or if you use this middleware without a reverse
+// proxy, malicious clients will be able to make you very sad (or, depending on
+// how you're using RemoteAddr, vulnerable to an attack of some sort).
+func RealIP(h http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ if rip := realIP(r); rip != "" {
+ r.RemoteAddr = rip
+ }
+ h.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+func realIP(r *http.Request) string {
+ var ip string
+
+ if tcip := r.Header.Get(trueClientIP); tcip != "" {
+ ip = tcip
+ } else if xrip := r.Header.Get(xRealIP); xrip != "" {
+ ip = xrip
+ } else if xff := r.Header.Get(xForwardedFor); xff != "" {
+ i := strings.Index(xff, ", ")
+ if i == -1 {
+ i = len(xff)
+ }
+ ip = xff[:i]
+ }
+
+ return ip
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go b/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go
new file mode 100644
index 000000000..c9eafff45
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go
@@ -0,0 +1,197 @@
+package middleware
+
+// The original work was derived from Goji's middleware, source:
+// https://github.com/zenazn/goji/tree/master/web/middleware
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "runtime/debug"
+ "strings"
+)
+
+// Recoverer is a middleware that recovers from panics, logs the panic (and a
+// backtrace), and returns a HTTP 500 (Internal Server Error) status if
+// possible. Recoverer prints a request ID if one is provided.
+//
+// Alternatively, look at https://github.com/pressly/lg middleware pkgs.
+func Recoverer(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ defer func() {
+ if rvr := recover(); rvr != nil && rvr != http.ErrAbortHandler {
+
+ logEntry := GetLogEntry(r)
+ if logEntry != nil {
+ logEntry.Panic(rvr, debug.Stack())
+ } else {
+ PrintPrettyStack(rvr)
+ }
+
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+ }()
+
+ next.ServeHTTP(w, r)
+ }
+
+ return http.HandlerFunc(fn)
+}
+
+// for ability to test the PrintPrettyStack function
+var recovererErrorWriter io.Writer = os.Stderr
+
+func PrintPrettyStack(rvr interface{}) {
+ debugStack := debug.Stack()
+ s := prettyStack{}
+ out, err := s.parse(debugStack, rvr)
+ if err == nil {
+ recovererErrorWriter.Write(out)
+ } else {
+ // print stdlib output as a fallback
+ os.Stderr.Write(debugStack)
+ }
+}
+
+type prettyStack struct {
+}
+
+func (s prettyStack) parse(debugStack []byte, rvr interface{}) ([]byte, error) {
+ var err error
+ useColor := true
+ buf := &bytes.Buffer{}
+
+ cW(buf, false, bRed, "\n")
+ cW(buf, useColor, bCyan, " panic: ")
+ cW(buf, useColor, bBlue, "%v", rvr)
+ cW(buf, false, bWhite, "\n \n")
+
+ // process debug stack info
+ stack := strings.Split(string(debugStack), "\n")
+ lines := []string{}
+
+ // locate panic line, as we may have nested panics
+ for i := len(stack) - 1; i > 0; i-- {
+ lines = append(lines, stack[i])
+ if strings.HasPrefix(stack[i], "panic(") {
+ lines = lines[0 : len(lines)-2] // remove boilerplate
+ break
+ }
+ }
+
+ // reverse
+ for i := len(lines)/2 - 1; i >= 0; i-- {
+ opp := len(lines) - 1 - i
+ lines[i], lines[opp] = lines[opp], lines[i]
+ }
+
+ // decorate
+ for i, line := range lines {
+ lines[i], err = s.decorateLine(line, useColor, i)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ for _, l := range lines {
+ fmt.Fprintf(buf, "%s", l)
+ }
+ return buf.Bytes(), nil
+}
+
+func (s prettyStack) decorateLine(line string, useColor bool, num int) (string, error) {
+ line = strings.TrimSpace(line)
+ if strings.HasPrefix(line, "\t") || strings.Contains(line, ".go:") {
+ return s.decorateSourceLine(line, useColor, num)
+ } else if strings.HasSuffix(line, ")") {
+ return s.decorateFuncCallLine(line, useColor, num)
+ } else {
+ if strings.HasPrefix(line, "\t") {
+ return strings.Replace(line, "\t", " ", 1), nil
+ } else {
+ return fmt.Sprintf(" %s\n", line), nil
+ }
+ }
+}
+
+func (s prettyStack) decorateFuncCallLine(line string, useColor bool, num int) (string, error) {
+ idx := strings.LastIndex(line, "(")
+ if idx < 0 {
+ return "", errors.New("not a func call line")
+ }
+
+ buf := &bytes.Buffer{}
+ pkg := line[0:idx]
+ // addr := line[idx:]
+ method := ""
+
+ if idx := strings.LastIndex(pkg, string(os.PathSeparator)); idx < 0 {
+ if idx := strings.Index(pkg, "."); idx > 0 {
+ method = pkg[idx:]
+ pkg = pkg[0:idx]
+ }
+ } else {
+ method = pkg[idx+1:]
+ pkg = pkg[0 : idx+1]
+ if idx := strings.Index(method, "."); idx > 0 {
+ pkg += method[0:idx]
+ method = method[idx:]
+ }
+ }
+ pkgColor := nYellow
+ methodColor := bGreen
+
+ if num == 0 {
+ cW(buf, useColor, bRed, " -> ")
+ pkgColor = bMagenta
+ methodColor = bRed
+ } else {
+ cW(buf, useColor, bWhite, " ")
+ }
+ cW(buf, useColor, pkgColor, "%s", pkg)
+ cW(buf, useColor, methodColor, "%s\n", method)
+ // cW(buf, useColor, nBlack, "%s", addr)
+ return buf.String(), nil
+}
+
+func (s prettyStack) decorateSourceLine(line string, useColor bool, num int) (string, error) {
+ idx := strings.LastIndex(line, ".go:")
+ if idx < 0 {
+ return "", errors.New("not a source line")
+ }
+
+ buf := &bytes.Buffer{}
+ path := line[0 : idx+3]
+ lineno := line[idx+3:]
+
+ idx = strings.LastIndex(path, string(os.PathSeparator))
+ dir := path[0 : idx+1]
+ file := path[idx+1:]
+
+ idx = strings.Index(lineno, " ")
+ if idx > 0 {
+ lineno = lineno[0:idx]
+ }
+ fileColor := bCyan
+ lineColor := bGreen
+
+ if num == 1 {
+ cW(buf, useColor, bRed, " -> ")
+ fileColor = bRed
+ lineColor = bMagenta
+ } else {
+ cW(buf, false, bWhite, " ")
+ }
+ cW(buf, useColor, bWhite, "%s", dir)
+ cW(buf, useColor, fileColor, "%s", file)
+ cW(buf, useColor, lineColor, "%s", lineno)
+ if num == 1 {
+ cW(buf, false, bWhite, "\n")
+ }
+ cW(buf, false, bWhite, "\n")
+
+ return buf.String(), nil
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/request_id.go b/vendor/github.com/go-chi/chi/v5/middleware/request_id.go
new file mode 100644
index 000000000..4903ecc21
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/request_id.go
@@ -0,0 +1,96 @@
+package middleware
+
+// Ported from Goji's middleware, source:
+// https://github.com/zenazn/goji/tree/master/web/middleware
+
+import (
+ "context"
+ "crypto/rand"
+ "encoding/base64"
+ "fmt"
+ "net/http"
+ "os"
+ "strings"
+ "sync/atomic"
+)
+
+// Key to use when setting the request ID.
+type ctxKeyRequestID int
+
+// RequestIDKey is the key that holds the unique request ID in a request context.
+const RequestIDKey ctxKeyRequestID = 0
+
+// RequestIDHeader is the name of the HTTP Header which contains the request id.
+// Exported so that it can be changed by developers
+var RequestIDHeader = "X-Request-Id"
+
+var prefix string
+var reqid uint64
+
+// A quick note on the statistics here: we're trying to calculate the chance that
+// two randomly generated base62 prefixes will collide. We use the formula from
+// http://en.wikipedia.org/wiki/Birthday_problem
+//
+// P[m, n] \approx 1 - e^{-m^2/2n}
+//
+// We ballpark an upper bound for $m$ by imagining (for whatever reason) a server
+// that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$
+//
+// For a $k$ character base-62 identifier, we have $n(k) = 62^k$
+//
+// Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for
+// our purposes, and is surely more than anyone would ever need in practice -- a
+// process that is rebooted a handful of times a day for a hundred years has less
+// than a millionth of a percent chance of generating two colliding IDs.
+
+func init() {
+ hostname, err := os.Hostname()
+ if hostname == "" || err != nil {
+ hostname = "localhost"
+ }
+ var buf [12]byte
+ var b64 string
+ for len(b64) < 10 {
+ rand.Read(buf[:])
+ b64 = base64.StdEncoding.EncodeToString(buf[:])
+ b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
+ }
+
+ prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10])
+}
+
+// RequestID is a middleware that injects a request ID into the context of each
+// request. A request ID is a string of the form "host.example.com/random-0001",
+// where "random" is a base62 random string that uniquely identifies this go
+// process, and where the last number is an atomically incremented request
+// counter.
+func RequestID(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+ requestID := r.Header.Get(RequestIDHeader)
+ if requestID == "" {
+ myid := atomic.AddUint64(&reqid, 1)
+ requestID = fmt.Sprintf("%s-%06d", prefix, myid)
+ }
+ ctx = context.WithValue(ctx, RequestIDKey, requestID)
+ next.ServeHTTP(w, r.WithContext(ctx))
+ }
+ return http.HandlerFunc(fn)
+}
+
+// GetReqID returns a request ID from the given context if one is present.
+// Returns the empty string if a request ID cannot be found.
+func GetReqID(ctx context.Context) string {
+ if ctx == nil {
+ return ""
+ }
+ if reqID, ok := ctx.Value(RequestIDKey).(string); ok {
+ return reqID
+ }
+ return ""
+}
+
+// NextRequestID generates the next request ID in the sequence.
+func NextRequestID() uint64 {
+ return atomic.AddUint64(&reqid, 1)
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go b/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go
new file mode 100644
index 000000000..ea914a1d3
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go
@@ -0,0 +1,160 @@
+package middleware
+
+import (
+ "net/http"
+ "strings"
+)
+
+// RouteHeaders is a neat little header-based router that allows you to direct
+// the flow of a request through a middleware stack based on a request header.
+//
+// For example, lets say you'd like to setup multiple routers depending on the
+// request Host header, you could then do something as so:
+//
+// r := chi.NewRouter()
+// rSubdomain := chi.NewRouter()
+//
+// r.Use(middleware.RouteHeaders().
+// Route("Host", "example.com", middleware.New(r)).
+// Route("Host", "*.example.com", middleware.New(rSubdomain)).
+// Handler)
+//
+// r.Get("/", h)
+// rSubdomain.Get("/", h2)
+//
+//
+// Another example, imagine you want to setup multiple CORS handlers, where for
+// your origin servers you allow authorized requests, but for third-party public
+// requests, authorization is disabled.
+//
+// r := chi.NewRouter()
+//
+// r.Use(middleware.RouteHeaders().
+// Route("Origin", "https://app.skyweaver.net", cors.Handler(cors.Options{
+// AllowedOrigins: []string{"https://api.skyweaver.net"},
+// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
+// AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
+// AllowCredentials: true, // <----------<<< allow credentials
+// })).
+// Route("Origin", "*", cors.Handler(cors.Options{
+// AllowedOrigins: []string{"*"},
+// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
+// AllowedHeaders: []string{"Accept", "Content-Type"},
+// AllowCredentials: false, // <----------<<< do not allow credentials
+// })).
+// Handler)
+//
+func RouteHeaders() HeaderRouter {
+ return HeaderRouter{}
+}
+
+type HeaderRouter map[string][]HeaderRoute
+
+func (hr HeaderRouter) Route(header, match string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter {
+ header = strings.ToLower(header)
+ k := hr[header]
+ if k == nil {
+ hr[header] = []HeaderRoute{}
+ }
+ hr[header] = append(hr[header], HeaderRoute{MatchOne: NewPattern(match), Middleware: middlewareHandler})
+ return hr
+}
+
+func (hr HeaderRouter) RouteAny(header string, match []string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter {
+ header = strings.ToLower(header)
+ k := hr[header]
+ if k == nil {
+ hr[header] = []HeaderRoute{}
+ }
+ patterns := []Pattern{}
+ for _, m := range match {
+ patterns = append(patterns, NewPattern(m))
+ }
+ hr[header] = append(hr[header], HeaderRoute{MatchAny: patterns, Middleware: middlewareHandler})
+ return hr
+}
+
+func (hr HeaderRouter) RouteDefault(handler func(next http.Handler) http.Handler) HeaderRouter {
+ hr["*"] = []HeaderRoute{{Middleware: handler}}
+ return hr
+}
+
+func (hr HeaderRouter) Handler(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if len(hr) == 0 {
+ // skip if no routes set
+ next.ServeHTTP(w, r)
+ }
+
+ // find first matching header route, and continue
+ for header, matchers := range hr {
+ headerValue := r.Header.Get(header)
+ if headerValue == "" {
+ continue
+ }
+ headerValue = strings.ToLower(headerValue)
+ for _, matcher := range matchers {
+ if matcher.IsMatch(headerValue) {
+ matcher.Middleware(next).ServeHTTP(w, r)
+ return
+ }
+ }
+ }
+
+ // if no match, check for "*" default route
+ matcher, ok := hr["*"]
+ if !ok || matcher[0].Middleware == nil {
+ next.ServeHTTP(w, r)
+ return
+ }
+ matcher[0].Middleware(next).ServeHTTP(w, r)
+ })
+}
+
+type HeaderRoute struct {
+ Middleware func(next http.Handler) http.Handler
+ MatchOne Pattern
+ MatchAny []Pattern
+}
+
+func (r HeaderRoute) IsMatch(value string) bool {
+ if len(r.MatchAny) > 0 {
+ for _, m := range r.MatchAny {
+ if m.Match(value) {
+ return true
+ }
+ }
+ } else if r.MatchOne.Match(value) {
+ return true
+ }
+ return false
+}
+
+type Pattern struct {
+ prefix string
+ suffix string
+ wildcard bool
+}
+
+func NewPattern(value string) Pattern {
+ p := Pattern{}
+ if i := strings.IndexByte(value, '*'); i >= 0 {
+ p.wildcard = true
+ p.prefix = value[0:i]
+ p.suffix = value[i+1:]
+ } else {
+ p.prefix = value
+ }
+ return p
+}
+
+func (p Pattern) Match(v string) bool {
+ if !p.wildcard {
+ if p.prefix == v {
+ return true
+ } else {
+ return false
+ }
+ }
+ return len(v) >= len(p.prefix+p.suffix) && strings.HasPrefix(v, p.prefix) && strings.HasSuffix(v, p.suffix)
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/strip.go b/vendor/github.com/go-chi/chi/v5/middleware/strip.go
new file mode 100644
index 000000000..ce8ebfcce
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/strip.go
@@ -0,0 +1,62 @@
+package middleware
+
+import (
+ "fmt"
+ "net/http"
+
+ "github.com/go-chi/chi/v5"
+)
+
+// StripSlashes is a middleware that will match request paths with a trailing
+// slash, strip it from the path and continue routing through the mux, if a route
+// matches, then it will serve the handler.
+func StripSlashes(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ var path string
+ rctx := chi.RouteContext(r.Context())
+ if rctx != nil && rctx.RoutePath != "" {
+ path = rctx.RoutePath
+ } else {
+ path = r.URL.Path
+ }
+ if len(path) > 1 && path[len(path)-1] == '/' {
+ newPath := path[:len(path)-1]
+ if rctx == nil {
+ r.URL.Path = newPath
+ } else {
+ rctx.RoutePath = newPath
+ }
+ }
+ next.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+}
+
+// RedirectSlashes is a middleware that will match request paths with a trailing
+// slash and redirect to the same path, less the trailing slash.
+//
+// NOTE: RedirectSlashes middleware is *incompatible* with http.FileServer,
+// see https://github.com/go-chi/chi/issues/343
+func RedirectSlashes(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ var path string
+ rctx := chi.RouteContext(r.Context())
+ if rctx != nil && rctx.RoutePath != "" {
+ path = rctx.RoutePath
+ } else {
+ path = r.URL.Path
+ }
+ if len(path) > 1 && path[len(path)-1] == '/' {
+ if r.URL.RawQuery != "" {
+ path = fmt.Sprintf("%s?%s", path[:len(path)-1], r.URL.RawQuery)
+ } else {
+ path = path[:len(path)-1]
+ }
+ redirectURL := fmt.Sprintf("//%s%s", r.Host, path)
+ http.Redirect(w, r, redirectURL, 301)
+ return
+ }
+ next.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/terminal.go b/vendor/github.com/go-chi/chi/v5/middleware/terminal.go
new file mode 100644
index 000000000..5ead7b924
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/terminal.go
@@ -0,0 +1,63 @@
+package middleware
+
+// Ported from Goji's middleware, source:
+// https://github.com/zenazn/goji/tree/master/web/middleware
+
+import (
+ "fmt"
+ "io"
+ "os"
+)
+
+var (
+ // Normal colors
+ nBlack = []byte{'\033', '[', '3', '0', 'm'}
+ nRed = []byte{'\033', '[', '3', '1', 'm'}
+ nGreen = []byte{'\033', '[', '3', '2', 'm'}
+ nYellow = []byte{'\033', '[', '3', '3', 'm'}
+ nBlue = []byte{'\033', '[', '3', '4', 'm'}
+ nMagenta = []byte{'\033', '[', '3', '5', 'm'}
+ nCyan = []byte{'\033', '[', '3', '6', 'm'}
+ nWhite = []byte{'\033', '[', '3', '7', 'm'}
+ // Bright colors
+ bBlack = []byte{'\033', '[', '3', '0', ';', '1', 'm'}
+ bRed = []byte{'\033', '[', '3', '1', ';', '1', 'm'}
+ bGreen = []byte{'\033', '[', '3', '2', ';', '1', 'm'}
+ bYellow = []byte{'\033', '[', '3', '3', ';', '1', 'm'}
+ bBlue = []byte{'\033', '[', '3', '4', ';', '1', 'm'}
+ bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'}
+ bCyan = []byte{'\033', '[', '3', '6', ';', '1', 'm'}
+ bWhite = []byte{'\033', '[', '3', '7', ';', '1', 'm'}
+
+ reset = []byte{'\033', '[', '0', 'm'}
+)
+
+var IsTTY bool
+
+func init() {
+ // This is sort of cheating: if stdout is a character device, we assume
+ // that means it's a TTY. Unfortunately, there are many non-TTY
+ // character devices, but fortunately stdout is rarely set to any of
+ // them.
+ //
+ // We could solve this properly by pulling in a dependency on
+ // code.google.com/p/go.crypto/ssh/terminal, for instance, but as a
+ // heuristic for whether to print in color or in black-and-white, I'd
+ // really rather not.
+ fi, err := os.Stdout.Stat()
+ if err == nil {
+ m := os.ModeDevice | os.ModeCharDevice
+ IsTTY = fi.Mode()&m == m
+ }
+}
+
+// colorWrite
+func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) {
+ if IsTTY && useColor {
+ w.Write(color)
+ }
+ fmt.Fprintf(w, s, args...)
+ if IsTTY && useColor {
+ w.Write(reset)
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/throttle.go b/vendor/github.com/go-chi/chi/v5/middleware/throttle.go
new file mode 100644
index 000000000..8dcb9440b
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/throttle.go
@@ -0,0 +1,132 @@
+package middleware
+
+import (
+ "net/http"
+ "strconv"
+ "time"
+)
+
+const (
+ errCapacityExceeded = "Server capacity exceeded."
+ errTimedOut = "Timed out while waiting for a pending request to complete."
+ errContextCanceled = "Context was canceled."
+)
+
+var (
+ defaultBacklogTimeout = time.Second * 60
+)
+
+// ThrottleOpts represents a set of throttling options.
+type ThrottleOpts struct {
+ RetryAfterFn func(ctxDone bool) time.Duration
+ Limit int
+ BacklogLimit int
+ BacklogTimeout time.Duration
+}
+
+// Throttle is a middleware that limits number of currently processed requests
+// at a time across all users. Note: Throttle is not a rate-limiter per user,
+// instead it just puts a ceiling on the number of currentl in-flight requests
+// being processed from the point from where the Throttle middleware is mounted.
+func Throttle(limit int) func(http.Handler) http.Handler {
+ return ThrottleWithOpts(ThrottleOpts{Limit: limit, BacklogTimeout: defaultBacklogTimeout})
+}
+
+// ThrottleBacklog is a middleware that limits number of currently processed
+// requests at a time and provides a backlog for holding a finite number of
+// pending requests.
+func ThrottleBacklog(limit, backlogLimit int, backlogTimeout time.Duration) func(http.Handler) http.Handler {
+ return ThrottleWithOpts(ThrottleOpts{Limit: limit, BacklogLimit: backlogLimit, BacklogTimeout: backlogTimeout})
+}
+
+// ThrottleWithOpts is a middleware that limits number of currently processed requests using passed ThrottleOpts.
+func ThrottleWithOpts(opts ThrottleOpts) func(http.Handler) http.Handler {
+ if opts.Limit < 1 {
+ panic("chi/middleware: Throttle expects limit > 0")
+ }
+
+ if opts.BacklogLimit < 0 {
+ panic("chi/middleware: Throttle expects backlogLimit to be positive")
+ }
+
+ t := throttler{
+ tokens: make(chan token, opts.Limit),
+ backlogTokens: make(chan token, opts.Limit+opts.BacklogLimit),
+ backlogTimeout: opts.BacklogTimeout,
+ retryAfterFn: opts.RetryAfterFn,
+ }
+
+ // Filling tokens.
+ for i := 0; i < opts.Limit+opts.BacklogLimit; i++ {
+ if i < opts.Limit {
+ t.tokens <- token{}
+ }
+ t.backlogTokens <- token{}
+ }
+
+ return func(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ select {
+
+ case <-ctx.Done():
+ t.setRetryAfterHeaderIfNeeded(w, true)
+ http.Error(w, errContextCanceled, http.StatusTooManyRequests)
+ return
+
+ case btok := <-t.backlogTokens:
+ timer := time.NewTimer(t.backlogTimeout)
+
+ defer func() {
+ t.backlogTokens <- btok
+ }()
+
+ select {
+ case <-timer.C:
+ t.setRetryAfterHeaderIfNeeded(w, false)
+ http.Error(w, errTimedOut, http.StatusTooManyRequests)
+ return
+ case <-ctx.Done():
+ timer.Stop()
+ t.setRetryAfterHeaderIfNeeded(w, true)
+ http.Error(w, errContextCanceled, http.StatusTooManyRequests)
+ return
+ case tok := <-t.tokens:
+ defer func() {
+ timer.Stop()
+ t.tokens <- tok
+ }()
+ next.ServeHTTP(w, r)
+ }
+ return
+
+ default:
+ t.setRetryAfterHeaderIfNeeded(w, false)
+ http.Error(w, errCapacityExceeded, http.StatusTooManyRequests)
+ return
+ }
+ }
+
+ return http.HandlerFunc(fn)
+ }
+}
+
+// token represents a request that is being processed.
+type token struct{}
+
+// throttler limits number of currently processed requests at a time.
+type throttler struct {
+ tokens chan token
+ backlogTokens chan token
+ retryAfterFn func(ctxDone bool) time.Duration
+ backlogTimeout time.Duration
+}
+
+// setRetryAfterHeaderIfNeeded sets Retry-After HTTP header if corresponding retryAfterFn option of throttler is initialized.
+func (t throttler) setRetryAfterHeaderIfNeeded(w http.ResponseWriter, ctxDone bool) {
+ if t.retryAfterFn == nil {
+ return
+ }
+ w.Header().Set("Retry-After", strconv.Itoa(int(t.retryAfterFn(ctxDone).Seconds())))
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/timeout.go b/vendor/github.com/go-chi/chi/v5/middleware/timeout.go
new file mode 100644
index 000000000..8e373536c
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/timeout.go
@@ -0,0 +1,49 @@
+package middleware
+
+import (
+ "context"
+ "net/http"
+ "time"
+)
+
+// Timeout is a middleware that cancels ctx after a given timeout and return
+// a 504 Gateway Timeout error to the client.
+//
+// It's required that you select the ctx.Done() channel to check for the signal
+// if the context has reached its deadline and return, otherwise the timeout
+// signal will be just ignored.
+//
+// ie. a route/handler may look like:
+//
+// r.Get("/long", func(w http.ResponseWriter, r *http.Request) {
+// ctx := r.Context()
+// processTime := time.Duration(rand.Intn(4)+1) * time.Second
+//
+// select {
+// case <-ctx.Done():
+// return
+//
+// case <-time.After(processTime):
+// // The above channel simulates some hard work.
+// }
+//
+// w.Write([]byte("done"))
+// })
+//
+func Timeout(timeout time.Duration) func(next http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ ctx, cancel := context.WithTimeout(r.Context(), timeout)
+ defer func() {
+ cancel()
+ if ctx.Err() == context.DeadlineExceeded {
+ w.WriteHeader(http.StatusGatewayTimeout)
+ }
+ }()
+
+ r = r.WithContext(ctx)
+ next.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/url_format.go b/vendor/github.com/go-chi/chi/v5/middleware/url_format.go
new file mode 100644
index 000000000..10d7134dc
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/url_format.go
@@ -0,0 +1,72 @@
+package middleware
+
+import (
+ "context"
+ "net/http"
+ "strings"
+
+ "github.com/go-chi/chi/v5"
+)
+
+var (
+ // URLFormatCtxKey is the context.Context key to store the URL format data
+ // for a request.
+ URLFormatCtxKey = &contextKey{"URLFormat"}
+)
+
+// URLFormat is a middleware that parses the url extension from a request path and stores it
+// on the context as a string under the key `middleware.URLFormatCtxKey`. The middleware will
+// trim the suffix from the routing path and continue routing.
+//
+// Routers should not include a url parameter for the suffix when using this middleware.
+//
+// Sample usage.. for url paths: `/articles/1`, `/articles/1.json` and `/articles/1.xml`
+//
+// func routes() http.Handler {
+// r := chi.NewRouter()
+// r.Use(middleware.URLFormat)
+//
+// r.Get("/articles/{id}", ListArticles)
+//
+// return r
+// }
+//
+// func ListArticles(w http.ResponseWriter, r *http.Request) {
+// urlFormat, _ := r.Context().Value(middleware.URLFormatCtxKey).(string)
+//
+// switch urlFormat {
+// case "json":
+// render.JSON(w, r, articles)
+// case "xml:"
+// render.XML(w, r, articles)
+// default:
+// render.JSON(w, r, articles)
+// }
+// }
+//
+func URLFormat(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ var format string
+ path := r.URL.Path
+
+ if strings.Index(path, ".") > 0 {
+ base := strings.LastIndex(path, "/")
+ idx := strings.LastIndex(path[base:], ".")
+
+ if idx > 0 {
+ idx += base
+ format = path[idx+1:]
+
+ rctx := chi.RouteContext(r.Context())
+ rctx.RoutePath = path[:idx]
+ }
+ }
+
+ r = r.WithContext(context.WithValue(ctx, URLFormatCtxKey, format))
+
+ next.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/value.go b/vendor/github.com/go-chi/chi/v5/middleware/value.go
new file mode 100644
index 000000000..a9dfd4345
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/value.go
@@ -0,0 +1,17 @@
+package middleware
+
+import (
+ "context"
+ "net/http"
+)
+
+// WithValue is a middleware that sets a given key/value in a context chain.
+func WithValue(key, val interface{}) func(next http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ fn := func(w http.ResponseWriter, r *http.Request) {
+ r = r.WithContext(context.WithValue(r.Context(), key, val))
+ next.ServeHTTP(w, r)
+ }
+ return http.HandlerFunc(fn)
+ }
+}
diff --git a/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go b/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go
new file mode 100644
index 000000000..cf5c44de1
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go
@@ -0,0 +1,219 @@
+package middleware
+
+// The original work was derived from Goji's middleware, source:
+// https://github.com/zenazn/goji/tree/master/web/middleware
+
+import (
+ "bufio"
+ "io"
+ "net"
+ "net/http"
+)
+
+// NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to
+// hook into various parts of the response process.
+func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter {
+ _, fl := w.(http.Flusher)
+
+ bw := basicWriter{ResponseWriter: w}
+
+ if protoMajor == 2 {
+ _, ps := w.(http.Pusher)
+ if fl && ps {
+ return &http2FancyWriter{bw}
+ }
+ } else {
+ _, hj := w.(http.Hijacker)
+ _, rf := w.(io.ReaderFrom)
+ if fl && hj && rf {
+ return &httpFancyWriter{bw}
+ }
+ if fl && hj {
+ return &flushHijackWriter{bw}
+ }
+ if hj {
+ return &hijackWriter{bw}
+ }
+ }
+
+ if fl {
+ return &flushWriter{bw}
+ }
+
+ return &bw
+}
+
+// WrapResponseWriter is a proxy around an http.ResponseWriter that allows you to hook
+// into various parts of the response process.
+type WrapResponseWriter interface {
+ http.ResponseWriter
+ // Status returns the HTTP status of the request, or 0 if one has not
+ // yet been sent.
+ Status() int
+ // BytesWritten returns the total number of bytes sent to the client.
+ BytesWritten() int
+ // Tee causes the response body to be written to the given io.Writer in
+ // addition to proxying the writes through. Only one io.Writer can be
+ // tee'd to at once: setting a second one will overwrite the first.
+ // Writes will be sent to the proxy before being written to this
+ // io.Writer. It is illegal for the tee'd writer to be modified
+ // concurrently with writes.
+ Tee(io.Writer)
+ // Unwrap returns the original proxied target.
+ Unwrap() http.ResponseWriter
+}
+
+// basicWriter wraps a http.ResponseWriter that implements the minimal
+// http.ResponseWriter interface.
+type basicWriter struct {
+ http.ResponseWriter
+ wroteHeader bool
+ code int
+ bytes int
+ tee io.Writer
+}
+
+func (b *basicWriter) WriteHeader(code int) {
+ if !b.wroteHeader {
+ b.code = code
+ b.wroteHeader = true
+ b.ResponseWriter.WriteHeader(code)
+ }
+}
+
+func (b *basicWriter) Write(buf []byte) (int, error) {
+ b.maybeWriteHeader()
+ n, err := b.ResponseWriter.Write(buf)
+ if b.tee != nil {
+ _, err2 := b.tee.Write(buf[:n])
+ // Prefer errors generated by the proxied writer.
+ if err == nil {
+ err = err2
+ }
+ }
+ b.bytes += n
+ return n, err
+}
+
+func (b *basicWriter) maybeWriteHeader() {
+ if !b.wroteHeader {
+ b.WriteHeader(http.StatusOK)
+ }
+}
+
+func (b *basicWriter) Status() int {
+ return b.code
+}
+
+func (b *basicWriter) BytesWritten() int {
+ return b.bytes
+}
+
+func (b *basicWriter) Tee(w io.Writer) {
+ b.tee = w
+}
+
+func (b *basicWriter) Unwrap() http.ResponseWriter {
+ return b.ResponseWriter
+}
+
+// flushWriter ...
+type flushWriter struct {
+ basicWriter
+}
+
+func (f *flushWriter) Flush() {
+ f.wroteHeader = true
+ fl := f.basicWriter.ResponseWriter.(http.Flusher)
+ fl.Flush()
+}
+
+var _ http.Flusher = &flushWriter{}
+
+// hijackWriter ...
+type hijackWriter struct {
+ basicWriter
+}
+
+func (f *hijackWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hj := f.basicWriter.ResponseWriter.(http.Hijacker)
+ return hj.Hijack()
+}
+
+var _ http.Hijacker = &hijackWriter{}
+
+// flushHijackWriter ...
+type flushHijackWriter struct {
+ basicWriter
+}
+
+func (f *flushHijackWriter) Flush() {
+ f.wroteHeader = true
+ fl := f.basicWriter.ResponseWriter.(http.Flusher)
+ fl.Flush()
+}
+
+func (f *flushHijackWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hj := f.basicWriter.ResponseWriter.(http.Hijacker)
+ return hj.Hijack()
+}
+
+var _ http.Flusher = &flushHijackWriter{}
+var _ http.Hijacker = &flushHijackWriter{}
+
+// httpFancyWriter is a HTTP writer that additionally satisfies
+// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case
+// of wrapping the http.ResponseWriter that package http gives you, in order to
+// make the proxied object support the full method set of the proxied object.
+type httpFancyWriter struct {
+ basicWriter
+}
+
+func (f *httpFancyWriter) Flush() {
+ f.wroteHeader = true
+ fl := f.basicWriter.ResponseWriter.(http.Flusher)
+ fl.Flush()
+}
+
+func (f *httpFancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ hj := f.basicWriter.ResponseWriter.(http.Hijacker)
+ return hj.Hijack()
+}
+
+func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error {
+ return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts)
+}
+
+func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) {
+ if f.basicWriter.tee != nil {
+ n, err := io.Copy(&f.basicWriter, r)
+ f.basicWriter.bytes += int(n)
+ return n, err
+ }
+ rf := f.basicWriter.ResponseWriter.(io.ReaderFrom)
+ f.basicWriter.maybeWriteHeader()
+ n, err := rf.ReadFrom(r)
+ f.basicWriter.bytes += int(n)
+ return n, err
+}
+
+var _ http.Flusher = &httpFancyWriter{}
+var _ http.Hijacker = &httpFancyWriter{}
+var _ http.Pusher = &http2FancyWriter{}
+var _ io.ReaderFrom = &httpFancyWriter{}
+
+// http2FancyWriter is a HTTP2 writer that additionally satisfies
+// http.Flusher, and io.ReaderFrom. It exists for the common case
+// of wrapping the http.ResponseWriter that package http gives you, in order to
+// make the proxied object support the full method set of the proxied object.
+type http2FancyWriter struct {
+ basicWriter
+}
+
+func (f *http2FancyWriter) Flush() {
+ f.wroteHeader = true
+ fl := f.basicWriter.ResponseWriter.(http.Flusher)
+ fl.Flush()
+}
+
+var _ http.Flusher = &http2FancyWriter{}
diff --git a/vendor/github.com/go-chi/chi/v5/mux.go b/vendor/github.com/go-chi/chi/v5/mux.go
new file mode 100644
index 000000000..fff6a3c5e
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/mux.go
@@ -0,0 +1,485 @@
+package chi
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "strings"
+ "sync"
+)
+
+var _ Router = &Mux{}
+
+// Mux is a simple HTTP route multiplexer that parses a request path,
+// records any URL params, and executes an end handler. It implements
+// the http.Handler interface and is friendly with the standard library.
+//
+// Mux is designed to be fast, minimal and offer a powerful API for building
+// modular and composable HTTP services with a large set of handlers. It's
+// particularly useful for writing large REST API services that break a handler
+// into many smaller parts composed of middlewares and end handlers.
+type Mux struct {
+ // The computed mux handler made of the chained middleware stack and
+ // the tree router
+ handler http.Handler
+
+ // The radix trie router
+ tree *node
+
+ // Custom method not allowed handler
+ methodNotAllowedHandler http.HandlerFunc
+
+ // Controls the behaviour of middleware chain generation when a mux
+ // is registered as an inline group inside another mux.
+ parent *Mux
+
+ // Routing context pool
+ pool *sync.Pool
+
+ // Custom route not found handler
+ notFoundHandler http.HandlerFunc
+
+ // The middleware stack
+ middlewares []func(http.Handler) http.Handler
+
+ inline bool
+}
+
+// NewMux returns a newly initialized Mux object that implements the Router
+// interface.
+func NewMux() *Mux {
+ mux := &Mux{tree: &node{}, pool: &sync.Pool{}}
+ mux.pool.New = func() interface{} {
+ return NewRouteContext()
+ }
+ return mux
+}
+
+// ServeHTTP is the single method of the http.Handler interface that makes
+// Mux interoperable with the standard library. It uses a sync.Pool to get and
+// reuse routing contexts for each request.
+func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ // Ensure the mux has some routes defined on the mux
+ if mx.handler == nil {
+ mx.NotFoundHandler().ServeHTTP(w, r)
+ return
+ }
+
+ // Check if a routing context already exists from a parent router.
+ rctx, _ := r.Context().Value(RouteCtxKey).(*Context)
+ if rctx != nil {
+ mx.handler.ServeHTTP(w, r)
+ return
+ }
+
+ // Fetch a RouteContext object from the sync pool, and call the computed
+ // mx.handler that is comprised of mx.middlewares + mx.routeHTTP.
+ // Once the request is finished, reset the routing context and put it back
+ // into the pool for reuse from another request.
+ rctx = mx.pool.Get().(*Context)
+ rctx.Reset()
+ rctx.Routes = mx
+ rctx.parentCtx = r.Context()
+
+ // NOTE: r.WithContext() causes 2 allocations and context.WithValue() causes 1 allocation
+ r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx))
+
+ // Serve the request and once its done, put the request context back in the sync pool
+ mx.handler.ServeHTTP(w, r)
+ mx.pool.Put(rctx)
+}
+
+// Use appends a middleware handler to the Mux middleware stack.
+//
+// The middleware stack for any Mux will execute before searching for a matching
+// route to a specific handler, which provides opportunity to respond early,
+// change the course of the request execution, or set request-scoped values for
+// the next http.Handler.
+func (mx *Mux) Use(middlewares ...func(http.Handler) http.Handler) {
+ if mx.handler != nil {
+ panic("chi: all middlewares must be defined before routes on a mux")
+ }
+ mx.middlewares = append(mx.middlewares, middlewares...)
+}
+
+// Handle adds the route `pattern` that matches any http method to
+// execute the `handler` http.Handler.
+func (mx *Mux) Handle(pattern string, handler http.Handler) {
+ mx.handle(mALL, pattern, handler)
+}
+
+// HandleFunc adds the route `pattern` that matches any http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) HandleFunc(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mALL, pattern, handlerFn)
+}
+
+// Method adds the route `pattern` that matches `method` http method to
+// execute the `handler` http.Handler.
+func (mx *Mux) Method(method, pattern string, handler http.Handler) {
+ m, ok := methodMap[strings.ToUpper(method)]
+ if !ok {
+ panic(fmt.Sprintf("chi: '%s' http method is not supported.", method))
+ }
+ mx.handle(m, pattern, handler)
+}
+
+// MethodFunc adds the route `pattern` that matches `method` http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) MethodFunc(method, pattern string, handlerFn http.HandlerFunc) {
+ mx.Method(method, pattern, handlerFn)
+}
+
+// Connect adds the route `pattern` that matches a CONNECT http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Connect(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mCONNECT, pattern, handlerFn)
+}
+
+// Delete adds the route `pattern` that matches a DELETE http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Delete(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mDELETE, pattern, handlerFn)
+}
+
+// Get adds the route `pattern` that matches a GET http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Get(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mGET, pattern, handlerFn)
+}
+
+// Head adds the route `pattern` that matches a HEAD http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Head(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mHEAD, pattern, handlerFn)
+}
+
+// Options adds the route `pattern` that matches a OPTIONS http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Options(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mOPTIONS, pattern, handlerFn)
+}
+
+// Patch adds the route `pattern` that matches a PATCH http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Patch(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mPATCH, pattern, handlerFn)
+}
+
+// Post adds the route `pattern` that matches a POST http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Post(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mPOST, pattern, handlerFn)
+}
+
+// Put adds the route `pattern` that matches a PUT http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Put(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mPUT, pattern, handlerFn)
+}
+
+// Trace adds the route `pattern` that matches a TRACE http method to
+// execute the `handlerFn` http.HandlerFunc.
+func (mx *Mux) Trace(pattern string, handlerFn http.HandlerFunc) {
+ mx.handle(mTRACE, pattern, handlerFn)
+}
+
+// NotFound sets a custom http.HandlerFunc for routing paths that could
+// not be found. The default 404 handler is `http.NotFound`.
+func (mx *Mux) NotFound(handlerFn http.HandlerFunc) {
+ // Build NotFound handler chain
+ m := mx
+ hFn := handlerFn
+ if mx.inline && mx.parent != nil {
+ m = mx.parent
+ hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP
+ }
+
+ // Update the notFoundHandler from this point forward
+ m.notFoundHandler = hFn
+ m.updateSubRoutes(func(subMux *Mux) {
+ if subMux.notFoundHandler == nil {
+ subMux.NotFound(hFn)
+ }
+ })
+}
+
+// MethodNotAllowed sets a custom http.HandlerFunc for routing paths where the
+// method is unresolved. The default handler returns a 405 with an empty body.
+func (mx *Mux) MethodNotAllowed(handlerFn http.HandlerFunc) {
+ // Build MethodNotAllowed handler chain
+ m := mx
+ hFn := handlerFn
+ if mx.inline && mx.parent != nil {
+ m = mx.parent
+ hFn = Chain(mx.middlewares...).HandlerFunc(hFn).ServeHTTP
+ }
+
+ // Update the methodNotAllowedHandler from this point forward
+ m.methodNotAllowedHandler = hFn
+ m.updateSubRoutes(func(subMux *Mux) {
+ if subMux.methodNotAllowedHandler == nil {
+ subMux.MethodNotAllowed(hFn)
+ }
+ })
+}
+
+// With adds inline middlewares for an endpoint handler.
+func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {
+ // Similarly as in handle(), we must build the mux handler once additional
+ // middleware registration isn't allowed for this stack, like now.
+ if !mx.inline && mx.handler == nil {
+ mx.updateRouteHandler()
+ }
+
+ // Copy middlewares from parent inline muxs
+ var mws Middlewares
+ if mx.inline {
+ mws = make(Middlewares, len(mx.middlewares))
+ copy(mws, mx.middlewares)
+ }
+ mws = append(mws, middlewares...)
+
+ im := &Mux{
+ pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws,
+ notFoundHandler: mx.notFoundHandler, methodNotAllowedHandler: mx.methodNotAllowedHandler,
+ }
+
+ return im
+}
+
+// Group creates a new inline-Mux with a fresh middleware stack. It's useful
+// for a group of handlers along the same routing path that use an additional
+// set of middlewares. See _examples/.
+func (mx *Mux) Group(fn func(r Router)) Router {
+ im := mx.With().(*Mux)
+ if fn != nil {
+ fn(im)
+ }
+ return im
+}
+
+// Route creates a new Mux with a fresh middleware stack and mounts it
+// along the `pattern` as a subrouter. Effectively, this is a short-hand
+// call to Mount. See _examples/.
+func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
+ if fn == nil {
+ panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern))
+ }
+ subRouter := NewRouter()
+ fn(subRouter)
+ mx.Mount(pattern, subRouter)
+ return subRouter
+}
+
+// Mount attaches another http.Handler or chi Router as a subrouter along a routing
+// path. It's very useful to split up a large API as many independent routers and
+// compose them as a single service using Mount. See _examples/.
+//
+// Note that Mount() simply sets a wildcard along the `pattern` that will continue
+// routing at the `handler`, which in most cases is another chi.Router. As a result,
+// if you define two Mount() routes on the exact same pattern the mount will panic.
+func (mx *Mux) Mount(pattern string, handler http.Handler) {
+ if handler == nil {
+ panic(fmt.Sprintf("chi: attempting to Mount() a nil handler on '%s'", pattern))
+ }
+
+ // Provide runtime safety for ensuring a pattern isn't mounted on an existing
+ // routing pattern.
+ if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") {
+ panic(fmt.Sprintf("chi: attempting to Mount() a handler on an existing path, '%s'", pattern))
+ }
+
+ // Assign sub-Router's with the parent not found & method not allowed handler if not specified.
+ subr, ok := handler.(*Mux)
+ if ok && subr.notFoundHandler == nil && mx.notFoundHandler != nil {
+ subr.NotFound(mx.notFoundHandler)
+ }
+ if ok && subr.methodNotAllowedHandler == nil && mx.methodNotAllowedHandler != nil {
+ subr.MethodNotAllowed(mx.methodNotAllowedHandler)
+ }
+
+ mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ rctx := RouteContext(r.Context())
+
+ // shift the url path past the previous subrouter
+ rctx.RoutePath = mx.nextRoutePath(rctx)
+
+ // reset the wildcard URLParam which connects the subrouter
+ n := len(rctx.URLParams.Keys) - 1
+ if n >= 0 && rctx.URLParams.Keys[n] == "*" && len(rctx.URLParams.Values) > n {
+ rctx.URLParams.Values[n] = ""
+ }
+
+ handler.ServeHTTP(w, r)
+ })
+
+ if pattern == "" || pattern[len(pattern)-1] != '/' {
+ mx.handle(mALL|mSTUB, pattern, mountHandler)
+ mx.handle(mALL|mSTUB, pattern+"/", mountHandler)
+ pattern += "/"
+ }
+
+ method := mALL
+ subroutes, _ := handler.(Routes)
+ if subroutes != nil {
+ method |= mSTUB
+ }
+ n := mx.handle(method, pattern+"*", mountHandler)
+
+ if subroutes != nil {
+ n.subroutes = subroutes
+ }
+}
+
+// Routes returns a slice of routing information from the tree,
+// useful for traversing available routes of a router.
+func (mx *Mux) Routes() []Route {
+ return mx.tree.routes()
+}
+
+// Middlewares returns a slice of middleware handler functions.
+func (mx *Mux) Middlewares() Middlewares {
+ return mx.middlewares
+}
+
+// Match searches the routing tree for a handler that matches the method/path.
+// It's similar to routing a http request, but without executing the handler
+// thereafter.
+//
+// Note: the *Context state is updated during execution, so manage
+// the state carefully or make a NewRouteContext().
+func (mx *Mux) Match(rctx *Context, method, path string) bool {
+ m, ok := methodMap[method]
+ if !ok {
+ return false
+ }
+
+ node, _, h := mx.tree.FindRoute(rctx, m, path)
+
+ if node != nil && node.subroutes != nil {
+ rctx.RoutePath = mx.nextRoutePath(rctx)
+ return node.subroutes.Match(rctx, method, rctx.RoutePath)
+ }
+
+ return h != nil
+}
+
+// NotFoundHandler returns the default Mux 404 responder whenever a route
+// cannot be found.
+func (mx *Mux) NotFoundHandler() http.HandlerFunc {
+ if mx.notFoundHandler != nil {
+ return mx.notFoundHandler
+ }
+ return http.NotFound
+}
+
+// MethodNotAllowedHandler returns the default Mux 405 responder whenever
+// a method cannot be resolved for a route.
+func (mx *Mux) MethodNotAllowedHandler() http.HandlerFunc {
+ if mx.methodNotAllowedHandler != nil {
+ return mx.methodNotAllowedHandler
+ }
+ return methodNotAllowedHandler
+}
+
+// handle registers a http.Handler in the routing tree for a particular http method
+// and routing pattern.
+func (mx *Mux) handle(method methodTyp, pattern string, handler http.Handler) *node {
+ if len(pattern) == 0 || pattern[0] != '/' {
+ panic(fmt.Sprintf("chi: routing pattern must begin with '/' in '%s'", pattern))
+ }
+
+ // Build the computed routing handler for this routing pattern.
+ if !mx.inline && mx.handler == nil {
+ mx.updateRouteHandler()
+ }
+
+ // Build endpoint handler with inline middlewares for the route
+ var h http.Handler
+ if mx.inline {
+ mx.handler = http.HandlerFunc(mx.routeHTTP)
+ h = Chain(mx.middlewares...).Handler(handler)
+ } else {
+ h = handler
+ }
+
+ // Add the endpoint to the tree and return the node
+ return mx.tree.InsertRoute(method, pattern, h)
+}
+
+// routeHTTP routes a http.Request through the Mux routing tree to serve
+// the matching handler for a particular http method.
+func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) {
+ // Grab the route context object
+ rctx := r.Context().Value(RouteCtxKey).(*Context)
+
+ // The request routing path
+ routePath := rctx.RoutePath
+ if routePath == "" {
+ if r.URL.RawPath != "" {
+ routePath = r.URL.RawPath
+ } else {
+ routePath = r.URL.Path
+ }
+ if routePath == "" {
+ routePath = "/"
+ }
+ }
+
+ // Check if method is supported by chi
+ if rctx.RouteMethod == "" {
+ rctx.RouteMethod = r.Method
+ }
+ method, ok := methodMap[rctx.RouteMethod]
+ if !ok {
+ mx.MethodNotAllowedHandler().ServeHTTP(w, r)
+ return
+ }
+
+ // Find the route
+ if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil {
+ h.ServeHTTP(w, r)
+ return
+ }
+ if rctx.methodNotAllowed {
+ mx.MethodNotAllowedHandler().ServeHTTP(w, r)
+ } else {
+ mx.NotFoundHandler().ServeHTTP(w, r)
+ }
+}
+
+func (mx *Mux) nextRoutePath(rctx *Context) string {
+ routePath := "/"
+ nx := len(rctx.routeParams.Keys) - 1 // index of last param in list
+ if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx {
+ routePath = "/" + rctx.routeParams.Values[nx]
+ }
+ return routePath
+}
+
+// Recursively update data on child routers.
+func (mx *Mux) updateSubRoutes(fn func(subMux *Mux)) {
+ for _, r := range mx.tree.routes() {
+ subMux, ok := r.SubRoutes.(*Mux)
+ if !ok {
+ continue
+ }
+ fn(subMux)
+ }
+}
+
+// updateRouteHandler builds the single mux handler that is a chain of the middleware
+// stack, as defined by calls to Use(), and the tree router (Mux) itself. After this
+// point, no other middlewares can be registered on this Mux's stack. But you can still
+// compose additional middlewares via Group()'s or using a chained middleware handler.
+func (mx *Mux) updateRouteHandler() {
+ mx.handler = chain(mx.middlewares, http.HandlerFunc(mx.routeHTTP))
+}
+
+// methodNotAllowedHandler is a helper function to respond with a 405,
+// method not allowed.
+func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(405)
+ w.Write(nil)
+}
diff --git a/vendor/github.com/go-chi/chi/v5/tree.go b/vendor/github.com/go-chi/chi/v5/tree.go
new file mode 100644
index 000000000..4189b5228
--- /dev/null
+++ b/vendor/github.com/go-chi/chi/v5/tree.go
@@ -0,0 +1,866 @@
+package chi
+
+// Radix tree implementation below is a based on the original work by
+// Armon Dadgar in https://github.com/armon/go-radix/blob/master/radix.go
+// (MIT licensed). It's been heavily modified for use as a HTTP routing tree.
+
+import (
+ "fmt"
+ "net/http"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+type methodTyp uint
+
+const (
+ mSTUB methodTyp = 1 << iota
+ mCONNECT
+ mDELETE
+ mGET
+ mHEAD
+ mOPTIONS
+ mPATCH
+ mPOST
+ mPUT
+ mTRACE
+)
+
+var mALL = mCONNECT | mDELETE | mGET | mHEAD |
+ mOPTIONS | mPATCH | mPOST | mPUT | mTRACE
+
+var methodMap = map[string]methodTyp{
+ http.MethodConnect: mCONNECT,
+ http.MethodDelete: mDELETE,
+ http.MethodGet: mGET,
+ http.MethodHead: mHEAD,
+ http.MethodOptions: mOPTIONS,
+ http.MethodPatch: mPATCH,
+ http.MethodPost: mPOST,
+ http.MethodPut: mPUT,
+ http.MethodTrace: mTRACE,
+}
+
+// RegisterMethod adds support for custom HTTP method handlers, available
+// via Router#Method and Router#MethodFunc
+func RegisterMethod(method string) {
+ if method == "" {
+ return
+ }
+ method = strings.ToUpper(method)
+ if _, ok := methodMap[method]; ok {
+ return
+ }
+ n := len(methodMap)
+ if n > strconv.IntSize-2 {
+ panic(fmt.Sprintf("chi: max number of methods reached (%d)", strconv.IntSize))
+ }
+ mt := methodTyp(2 << n)
+ methodMap[method] = mt
+ mALL |= mt
+}
+
+type nodeTyp uint8
+
+const (
+ ntStatic nodeTyp = iota // /home
+ ntRegexp // /{id:[0-9]+}
+ ntParam // /{user}
+ ntCatchAll // /api/v1/*
+)
+
+type node struct {
+ // subroutes on the leaf node
+ subroutes Routes
+
+ // regexp matcher for regexp nodes
+ rex *regexp.Regexp
+
+ // HTTP handler endpoints on the leaf node
+ endpoints endpoints
+
+ // prefix is the common prefix we ignore
+ prefix string
+
+ // child nodes should be stored in-order for iteration,
+ // in groups of the node type.
+ children [ntCatchAll + 1]nodes
+
+ // first byte of the child prefix
+ tail byte
+
+ // node type: static, regexp, param, catchAll
+ typ nodeTyp
+
+ // first byte of the prefix
+ label byte
+}
+
+// endpoints is a mapping of http method constants to handlers
+// for a given route.
+type endpoints map[methodTyp]*endpoint
+
+type endpoint struct {
+ // endpoint handler
+ handler http.Handler
+
+ // pattern is the routing pattern for handler nodes
+ pattern string
+
+ // parameter keys recorded on handler nodes
+ paramKeys []string
+}
+
+func (s endpoints) Value(method methodTyp) *endpoint {
+ mh, ok := s[method]
+ if !ok {
+ mh = &endpoint{}
+ s[method] = mh
+ }
+ return mh
+}
+
+func (n *node) InsertRoute(method methodTyp, pattern string, handler http.Handler) *node {
+ var parent *node
+ search := pattern
+
+ for {
+ // Handle key exhaustion
+ if len(search) == 0 {
+ // Insert or update the node's leaf handler
+ n.setEndpoint(method, handler, pattern)
+ return n
+ }
+
+ // We're going to be searching for a wild node next,
+ // in this case, we need to get the tail
+ var label = search[0]
+ var segTail byte
+ var segEndIdx int
+ var segTyp nodeTyp
+ var segRexpat string
+ if label == '{' || label == '*' {
+ segTyp, _, segRexpat, segTail, _, segEndIdx = patNextSegment(search)
+ }
+
+ var prefix string
+ if segTyp == ntRegexp {
+ prefix = segRexpat
+ }
+
+ // Look for the edge to attach to
+ parent = n
+ n = n.getEdge(segTyp, label, segTail, prefix)
+
+ // No edge, create one
+ if n == nil {
+ child := &node{label: label, tail: segTail, prefix: search}
+ hn := parent.addChild(child, search)
+ hn.setEndpoint(method, handler, pattern)
+
+ return hn
+ }
+
+ // Found an edge to match the pattern
+
+ if n.typ > ntStatic {
+ // We found a param node, trim the param from the search path and continue.
+ // This param/wild pattern segment would already be on the tree from a previous
+ // call to addChild when creating a new node.
+ search = search[segEndIdx:]
+ continue
+ }
+
+ // Static nodes fall below here.
+ // Determine longest prefix of the search key on match.
+ commonPrefix := longestPrefix(search, n.prefix)
+ if commonPrefix == len(n.prefix) {
+ // the common prefix is as long as the current node's prefix we're attempting to insert.
+ // keep the search going.
+ search = search[commonPrefix:]
+ continue
+ }
+
+ // Split the node
+ child := &node{
+ typ: ntStatic,
+ prefix: search[:commonPrefix],
+ }
+ parent.replaceChild(search[0], segTail, child)
+
+ // Restore the existing node
+ n.label = n.prefix[commonPrefix]
+ n.prefix = n.prefix[commonPrefix:]
+ child.addChild(n, n.prefix)
+
+ // If the new key is a subset, set the method/handler on this node and finish.
+ search = search[commonPrefix:]
+ if len(search) == 0 {
+ child.setEndpoint(method, handler, pattern)
+ return child
+ }
+
+ // Create a new edge for the node
+ subchild := &node{
+ typ: ntStatic,
+ label: search[0],
+ prefix: search,
+ }
+ hn := child.addChild(subchild, search)
+ hn.setEndpoint(method, handler, pattern)
+ return hn
+ }
+}
+
+// addChild appends the new `child` node to the tree using the `pattern` as the trie key.
+// For a URL router like chi's, we split the static, param, regexp and wildcard segments
+// into different nodes. In addition, addChild will recursively call itself until every
+// pattern segment is added to the url pattern tree as individual nodes, depending on type.
+func (n *node) addChild(child *node, prefix string) *node {
+ search := prefix
+
+ // handler leaf node added to the tree is the child.
+ // this may be overridden later down the flow
+ hn := child
+
+ // Parse next segment
+ segTyp, _, segRexpat, segTail, segStartIdx, segEndIdx := patNextSegment(search)
+
+ // Add child depending on next up segment
+ switch segTyp {
+
+ case ntStatic:
+ // Search prefix is all static (that is, has no params in path)
+ // noop
+
+ default:
+ // Search prefix contains a param, regexp or wildcard
+
+ if segTyp == ntRegexp {
+ rex, err := regexp.Compile(segRexpat)
+ if err != nil {
+ panic(fmt.Sprintf("chi: invalid regexp pattern '%s' in route param", segRexpat))
+ }
+ child.prefix = segRexpat
+ child.rex = rex
+ }
+
+ if segStartIdx == 0 {
+ // Route starts with a param
+ child.typ = segTyp
+
+ if segTyp == ntCatchAll {
+ segStartIdx = -1
+ } else {
+ segStartIdx = segEndIdx
+ }
+ if segStartIdx < 0 {
+ segStartIdx = len(search)
+ }
+ child.tail = segTail // for params, we set the tail
+
+ if segStartIdx != len(search) {
+ // add static edge for the remaining part, split the end.
+ // its not possible to have adjacent param nodes, so its certainly
+ // going to be a static node next.
+
+ search = search[segStartIdx:] // advance search position
+
+ nn := &node{
+ typ: ntStatic,
+ label: search[0],
+ prefix: search,
+ }
+ hn = child.addChild(nn, search)
+ }
+
+ } else if segStartIdx > 0 {
+ // Route has some param
+
+ // starts with a static segment
+ child.typ = ntStatic
+ child.prefix = search[:segStartIdx]
+ child.rex = nil
+
+ // add the param edge node
+ search = search[segStartIdx:]
+
+ nn := &node{
+ typ: segTyp,
+ label: search[0],
+ tail: segTail,
+ }
+ hn = child.addChild(nn, search)
+
+ }
+ }
+
+ n.children[child.typ] = append(n.children[child.typ], child)
+ n.children[child.typ].Sort()
+ return hn
+}
+
+func (n *node) replaceChild(label, tail byte, child *node) {
+ for i := 0; i < len(n.children[child.typ]); i++ {
+ if n.children[child.typ][i].label == label && n.children[child.typ][i].tail == tail {
+ n.children[child.typ][i] = child
+ n.children[child.typ][i].label = label
+ n.children[child.typ][i].tail = tail
+ return
+ }
+ }
+ panic("chi: replacing missing child")
+}
+
+func (n *node) getEdge(ntyp nodeTyp, label, tail byte, prefix string) *node {
+ nds := n.children[ntyp]
+ for i := 0; i < len(nds); i++ {
+ if nds[i].label == label && nds[i].tail == tail {
+ if ntyp == ntRegexp && nds[i].prefix != prefix {
+ continue
+ }
+ return nds[i]
+ }
+ }
+ return nil
+}
+
+func (n *node) setEndpoint(method methodTyp, handler http.Handler, pattern string) {
+ // Set the handler for the method type on the node
+ if n.endpoints == nil {
+ n.endpoints = make(endpoints)
+ }
+
+ paramKeys := patParamKeys(pattern)
+
+ if method&mSTUB == mSTUB {
+ n.endpoints.Value(mSTUB).handler = handler
+ }
+ if method&mALL == mALL {
+ h := n.endpoints.Value(mALL)
+ h.handler = handler
+ h.pattern = pattern
+ h.paramKeys = paramKeys
+ for _, m := range methodMap {
+ h := n.endpoints.Value(m)
+ h.handler = handler
+ h.pattern = pattern
+ h.paramKeys = paramKeys
+ }
+ } else {
+ h := n.endpoints.Value(method)
+ h.handler = handler
+ h.pattern = pattern
+ h.paramKeys = paramKeys
+ }
+}
+
+func (n *node) FindRoute(rctx *Context, method methodTyp, path string) (*node, endpoints, http.Handler) {
+ // Reset the context routing pattern and params
+ rctx.routePattern = ""
+ rctx.routeParams.Keys = rctx.routeParams.Keys[:0]
+ rctx.routeParams.Values = rctx.routeParams.Values[:0]
+
+ // Find the routing handlers for the path
+ rn := n.findRoute(rctx, method, path)
+ if rn == nil {
+ return nil, nil, nil
+ }
+
+ // Record the routing params in the request lifecycle
+ rctx.URLParams.Keys = append(rctx.URLParams.Keys, rctx.routeParams.Keys...)
+ rctx.URLParams.Values = append(rctx.URLParams.Values, rctx.routeParams.Values...)
+
+ // Record the routing pattern in the request lifecycle
+ if rn.endpoints[method].pattern != "" {
+ rctx.routePattern = rn.endpoints[method].pattern
+ rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.routePattern)
+ }
+
+ return rn, rn.endpoints, rn.endpoints[method].handler
+}
+
+// Recursive edge traversal by checking all nodeTyp groups along the way.
+// It's like searching through a multi-dimensional radix trie.
+func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node {
+ nn := n
+ search := path
+
+ for t, nds := range nn.children {
+ ntyp := nodeTyp(t)
+ if len(nds) == 0 {
+ continue
+ }
+
+ var xn *node
+ xsearch := search
+
+ var label byte
+ if search != "" {
+ label = search[0]
+ }
+
+ switch ntyp {
+ case ntStatic:
+ xn = nds.findEdge(label)
+ if xn == nil || !strings.HasPrefix(xsearch, xn.prefix) {
+ continue
+ }
+ xsearch = xsearch[len(xn.prefix):]
+
+ case ntParam, ntRegexp:
+ // short-circuit and return no matching route for empty param values
+ if xsearch == "" {
+ continue
+ }
+
+ // serially loop through each node grouped by the tail delimiter
+ for idx := 0; idx < len(nds); idx++ {
+ xn = nds[idx]
+
+ // label for param nodes is the delimiter byte
+ p := strings.IndexByte(xsearch, xn.tail)
+
+ if p < 0 {
+ if xn.tail == '/' {
+ p = len(xsearch)
+ } else {
+ continue
+ }
+ } else if ntyp == ntRegexp && p == 0 {
+ continue
+ }
+
+ if ntyp == ntRegexp && xn.rex != nil {
+ if !xn.rex.MatchString(xsearch[:p]) {
+ continue
+ }
+ } else if strings.IndexByte(xsearch[:p], '/') != -1 {
+ // avoid a match across path segments
+ continue
+ }
+
+ prevlen := len(rctx.routeParams.Values)
+ rctx.routeParams.Values = append(rctx.routeParams.Values, xsearch[:p])
+ xsearch = xsearch[p:]
+
+ if len(xsearch) == 0 {
+ if xn.isLeaf() {
+ h := xn.endpoints[method]
+ if h != nil && h.handler != nil {
+ rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...)
+ return xn
+ }
+
+ // flag that the routing context found a route, but not a corresponding
+ // supported method
+ rctx.methodNotAllowed = true
+ }
+ }
+
+ // recursively find the next node on this branch
+ fin := xn.findRoute(rctx, method, xsearch)
+ if fin != nil {
+ return fin
+ }
+
+ // not found on this branch, reset vars
+ rctx.routeParams.Values = rctx.routeParams.Values[:prevlen]
+ xsearch = search
+ }
+
+ rctx.routeParams.Values = append(rctx.routeParams.Values, "")
+
+ default:
+ // catch-all nodes
+ rctx.routeParams.Values = append(rctx.routeParams.Values, search)
+ xn = nds[0]
+ xsearch = ""
+ }
+
+ if xn == nil {
+ continue
+ }
+
+ // did we find it yet?
+ if len(xsearch) == 0 {
+ if xn.isLeaf() {
+ h := xn.endpoints[method]
+ if h != nil && h.handler != nil {
+ rctx.routeParams.Keys = append(rctx.routeParams.Keys, h.paramKeys...)
+ return xn
+ }
+
+ // flag that the routing context found a route, but not a corresponding
+ // supported method
+ rctx.methodNotAllowed = true
+ }
+ }
+
+ // recursively find the next node..
+ fin := xn.findRoute(rctx, method, xsearch)
+ if fin != nil {
+ return fin
+ }
+
+ // Did not find final handler, let's remove the param here if it was set
+ if xn.typ > ntStatic {
+ if len(rctx.routeParams.Values) > 0 {
+ rctx.routeParams.Values = rctx.routeParams.Values[:len(rctx.routeParams.Values)-1]
+ }
+ }
+
+ }
+
+ return nil
+}
+
+func (n *node) findEdge(ntyp nodeTyp, label byte) *node {
+ nds := n.children[ntyp]
+ num := len(nds)
+ idx := 0
+
+ switch ntyp {
+ case ntStatic, ntParam, ntRegexp:
+ i, j := 0, num-1
+ for i <= j {
+ idx = i + (j-i)/2
+ if label > nds[idx].label {
+ i = idx + 1
+ } else if label < nds[idx].label {
+ j = idx - 1
+ } else {
+ i = num // breaks cond
+ }
+ }
+ if nds[idx].label != label {
+ return nil
+ }
+ return nds[idx]
+
+ default: // catch all
+ return nds[idx]
+ }
+}
+
+func (n *node) isLeaf() bool {
+ return n.endpoints != nil
+}
+
+func (n *node) findPattern(pattern string) bool {
+ nn := n
+ for _, nds := range nn.children {
+ if len(nds) == 0 {
+ continue
+ }
+
+ n = nn.findEdge(nds[0].typ, pattern[0])
+ if n == nil {
+ continue
+ }
+
+ var idx int
+ var xpattern string
+
+ switch n.typ {
+ case ntStatic:
+ idx = longestPrefix(pattern, n.prefix)
+ if idx < len(n.prefix) {
+ continue
+ }
+
+ case ntParam, ntRegexp:
+ idx = strings.IndexByte(pattern, '}') + 1
+
+ case ntCatchAll:
+ idx = longestPrefix(pattern, "*")
+
+ default:
+ panic("chi: unknown node type")
+ }
+
+ xpattern = pattern[idx:]
+ if len(xpattern) == 0 {
+ return true
+ }
+
+ return n.findPattern(xpattern)
+ }
+ return false
+}
+
+func (n *node) routes() []Route {
+ rts := []Route{}
+
+ n.walk(func(eps endpoints, subroutes Routes) bool {
+ if eps[mSTUB] != nil && eps[mSTUB].handler != nil && subroutes == nil {
+ return false
+ }
+
+ // Group methodHandlers by unique patterns
+ pats := make(map[string]endpoints)
+
+ for mt, h := range eps {
+ if h.pattern == "" {
+ continue
+ }
+ p, ok := pats[h.pattern]
+ if !ok {
+ p = endpoints{}
+ pats[h.pattern] = p
+ }
+ p[mt] = h
+ }
+
+ for p, mh := range pats {
+ hs := make(map[string]http.Handler)
+ if mh[mALL] != nil && mh[mALL].handler != nil {
+ hs["*"] = mh[mALL].handler
+ }
+
+ for mt, h := range mh {
+ if h.handler == nil {
+ continue
+ }
+ m := methodTypString(mt)
+ if m == "" {
+ continue
+ }
+ hs[m] = h.handler
+ }
+
+ rt := Route{subroutes, hs, p}
+ rts = append(rts, rt)
+ }
+
+ return false
+ })
+
+ return rts
+}
+
+func (n *node) walk(fn func(eps endpoints, subroutes Routes) bool) bool {
+ // Visit the leaf values if any
+ if (n.endpoints != nil || n.subroutes != nil) && fn(n.endpoints, n.subroutes) {
+ return true
+ }
+
+ // Recurse on the children
+ for _, ns := range n.children {
+ for _, cn := range ns {
+ if cn.walk(fn) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// patNextSegment returns the next segment details from a pattern:
+// node type, param key, regexp string, param tail byte, param starting index, param ending index
+func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) {
+ ps := strings.Index(pattern, "{")
+ ws := strings.Index(pattern, "*")
+
+ if ps < 0 && ws < 0 {
+ return ntStatic, "", "", 0, 0, len(pattern) // we return the entire thing
+ }
+
+ // Sanity check
+ if ps >= 0 && ws >= 0 && ws < ps {
+ panic("chi: wildcard '*' must be the last pattern in a route, otherwise use a '{param}'")
+ }
+
+ var tail byte = '/' // Default endpoint tail to / byte
+
+ if ps >= 0 {
+ // Param/Regexp pattern is next
+ nt := ntParam
+
+ // Read to closing } taking into account opens and closes in curl count (cc)
+ cc := 0
+ pe := ps
+ for i, c := range pattern[ps:] {
+ if c == '{' {
+ cc++
+ } else if c == '}' {
+ cc--
+ if cc == 0 {
+ pe = ps + i
+ break
+ }
+ }
+ }
+ if pe == ps {
+ panic("chi: route param closing delimiter '}' is missing")
+ }
+
+ key := pattern[ps+1 : pe]
+ pe++ // set end to next position
+
+ if pe < len(pattern) {
+ tail = pattern[pe]
+ }
+
+ var rexpat string
+ if idx := strings.Index(key, ":"); idx >= 0 {
+ nt = ntRegexp
+ rexpat = key[idx+1:]
+ key = key[:idx]
+ }
+
+ if len(rexpat) > 0 {
+ if rexpat[0] != '^' {
+ rexpat = "^" + rexpat
+ }
+ if rexpat[len(rexpat)-1] != '$' {
+ rexpat += "$"
+ }
+ }
+
+ return nt, key, rexpat, tail, ps, pe
+ }
+
+ // Wildcard pattern as finale
+ if ws < len(pattern)-1 {
+ panic("chi: wildcard '*' must be the last value in a route. trim trailing text or use a '{param}' instead")
+ }
+ return ntCatchAll, "*", "", 0, ws, len(pattern)
+}
+
+func patParamKeys(pattern string) []string {
+ pat := pattern
+ paramKeys := []string{}
+ for {
+ ptyp, paramKey, _, _, _, e := patNextSegment(pat)
+ if ptyp == ntStatic {
+ return paramKeys
+ }
+ for i := 0; i < len(paramKeys); i++ {
+ if paramKeys[i] == paramKey {
+ panic(fmt.Sprintf("chi: routing pattern '%s' contains duplicate param key, '%s'", pattern, paramKey))
+ }
+ }
+ paramKeys = append(paramKeys, paramKey)
+ pat = pat[e:]
+ }
+}
+
+// longestPrefix finds the length of the shared prefix
+// of two strings
+func longestPrefix(k1, k2 string) int {
+ max := len(k1)
+ if l := len(k2); l < max {
+ max = l
+ }
+ var i int
+ for i = 0; i < max; i++ {
+ if k1[i] != k2[i] {
+ break
+ }
+ }
+ return i
+}
+
+func methodTypString(method methodTyp) string {
+ for s, t := range methodMap {
+ if method == t {
+ return s
+ }
+ }
+ return ""
+}
+
+type nodes []*node
+
+// Sort the list of nodes by label
+func (ns nodes) Sort() { sort.Sort(ns); ns.tailSort() }
+func (ns nodes) Len() int { return len(ns) }
+func (ns nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] }
+func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label }
+
+// tailSort pushes nodes with '/' as the tail to the end of the list for param nodes.
+// The list order determines the traversal order.
+func (ns nodes) tailSort() {
+ for i := len(ns) - 1; i >= 0; i-- {
+ if ns[i].typ > ntStatic && ns[i].tail == '/' {
+ ns.Swap(i, len(ns)-1)
+ return
+ }
+ }
+}
+
+func (ns nodes) findEdge(label byte) *node {
+ num := len(ns)
+ idx := 0
+ i, j := 0, num-1
+ for i <= j {
+ idx = i + (j-i)/2
+ if label > ns[idx].label {
+ i = idx + 1
+ } else if label < ns[idx].label {
+ j = idx - 1
+ } else {
+ i = num // breaks cond
+ }
+ }
+ if ns[idx].label != label {
+ return nil
+ }
+ return ns[idx]
+}
+
+// Route describes the details of a routing handler.
+// Handlers map key is an HTTP method
+type Route struct {
+ SubRoutes Routes
+ Handlers map[string]http.Handler
+ Pattern string
+}
+
+// WalkFunc is the type of the function called for each method and route visited by Walk.
+type WalkFunc func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error
+
+// Walk walks any router tree that implements Routes interface.
+func Walk(r Routes, walkFn WalkFunc) error {
+ return walk(r, walkFn, "")
+}
+
+func walk(r Routes, walkFn WalkFunc, parentRoute string, parentMw ...func(http.Handler) http.Handler) error {
+ for _, route := range r.Routes() {
+ mws := make([]func(http.Handler) http.Handler, len(parentMw))
+ copy(mws, parentMw)
+ mws = append(mws, r.Middlewares()...)
+
+ if route.SubRoutes != nil {
+ if err := walk(route.SubRoutes, walkFn, parentRoute+route.Pattern, mws...); err != nil {
+ return err
+ }
+ continue
+ }
+
+ for method, handler := range route.Handlers {
+ if method == "*" {
+ // Ignore a "catchAll" method, since we pass down all the specific methods for each route.
+ continue
+ }
+
+ fullRoute := parentRoute + route.Pattern
+ fullRoute = strings.Replace(fullRoute, "/*/", "/", -1)
+
+ if chain, ok := handler.(*ChainHandler); ok {
+ if err := walkFn(method, fullRoute, chain.Endpoint, append(mws, chain.Middlewares...)...); err != nil {
+ return err
+ }
+ } else {
+ if err := walkFn(method, fullRoute, handler, mws...); err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/go-chi/cors/LICENSE b/vendor/github.com/go-chi/cors/LICENSE
new file mode 100644
index 000000000..aee6182f9
--- /dev/null
+++ b/vendor/github.com/go-chi/cors/LICENSE
@@ -0,0 +1,21 @@
+Copyright (c) 2014 Olivier Poitrey
+Copyright (c) 2016-Present https://github.com/go-chi authors
+
+MIT License
+
+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.
diff --git a/vendor/github.com/go-chi/cors/README.md b/vendor/github.com/go-chi/cors/README.md
index 6e865be3a..b41686b6a 100644
--- a/vendor/github.com/go-chi/cors/README.md
+++ b/vendor/github.com/go-chi/cors/README.md
@@ -1,6 +1,11 @@
# CORS net/http middleware
-(fork of github.com/rs/cors)
+[go-chi/cors](https://github.com/go-chi/cors) is a fork of [github.com/rs/cors](https://github.com/rs/cors) that
+provides a `net/http` compatible middleware for performing preflight CORS checks on the server side. These headers
+are required for using the browser native [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
+
+This middleware is designed to be used as a top-level middleware on the [chi](https://github.com/go-chi/chi) router.
+Applying with within a `r.Group()` or using `With()` will not work without routes matching `OPTIONS` added.
## Usage
@@ -10,23 +15,25 @@ func main() {
// Basic CORS
// for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing
- cors := cors.New(cors.Options{
- // AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
- AllowedOrigins: []string{"*"},
+ r.Use(cors.Handler(cors.Options{
+ // AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
+ AllowedOrigins: []string{"https://*", "http://*"},
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
- AllowCredentials: true,
+ AllowCredentials: false,
MaxAge: 300, // Maximum value not ignored by any of major browsers
- })
- r.Use(cors.Handler)
-
+ }))
+
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("welcome"))
})
-
+
http.ListenAndServe(":3000", r)
}
```
+## Credits
+
+All credit for the original work of this middleware goes out to [github.com/rs](github.com/rs).
diff --git a/vendor/github.com/go-chi/cors/cors.go b/vendor/github.com/go-chi/cors/cors.go
index d85d86cbc..8df81636e 100644
--- a/vendor/github.com/go-chi/cors/cors.go
+++ b/vendor/github.com/go-chi/cors/cors.go
@@ -31,18 +31,18 @@ type Options struct {
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
// If the special "*" value is present in the list, all origins will be allowed.
// An origin may contain a wildcard (*) to replace 0 or more characters
- // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
+ // (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penalty.
// Only one wildcard can be used per origin.
// Default value is ["*"]
AllowedOrigins []string
- // AllowOriginFunc is a custom function to validate the origin. It take the origin
+ // AllowOriginFunc is a custom function to validate the origin. It takes the origin
// as argument and returns true if allowed or false otherwise. If this option is
// set, the content of AllowedOrigins is ignored.
AllowOriginFunc func(r *http.Request, origin string) bool
// AllowedMethods is a list of methods the client is allowed to use with
- // cross-domain requests. Default value is simple methods (GET and POST)
+ // cross-domain requests. Default value is simple methods (HEAD, GET and POST).
AllowedMethods []string
// AllowedHeaders is list of non simple headers the client is allowed to use with
@@ -71,13 +71,15 @@ type Options struct {
Debug bool
}
+// Logger generic interface for logger
+type Logger interface {
+ Printf(string, ...interface{})
+}
+
// Cors http handler
type Cors struct {
// Debug logger
- log *log.Logger
-
- // Set to true when allowed origins contains a "*"
- allowedOriginsAll bool
+ Log Logger
// Normalized list of plain allowed origins
allowedOrigins []string
@@ -88,9 +90,6 @@ type Cors struct {
// Optional origin validator function
allowOriginFunc func(r *http.Request, origin string) bool
- // Set to true when allowed headers contains a "*"
- allowedHeadersAll bool
-
// Normalized list of allowed headers
allowedHeaders []string
@@ -98,9 +97,16 @@ type Cors struct {
allowedMethods []string
// Normalized list of exposed headers
- exposedHeaders []string
+ exposedHeaders []string
+ maxAge int
+
+ // Set to true when allowed origins contains a "*"
+ allowedOriginsAll bool
+
+ // Set to true when allowed headers contains a "*"
+ allowedHeadersAll bool
+
allowCredentials bool
- maxAge int
optionPassthrough bool
}
@@ -113,8 +119,8 @@ func New(options Options) *Cors {
maxAge: options.MaxAge,
optionPassthrough: options.OptionsPassthrough,
}
- if options.Debug {
- c.log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
+ if options.Debug && c.Log == nil {
+ c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
}
// Normalize options
@@ -123,8 +129,10 @@ func New(options Options) *Cors {
// Allowed Origins
if len(options.AllowedOrigins) == 0 {
- // Default is all origins
- c.allowedOriginsAll = true
+ if options.AllowOriginFunc == nil {
+ // Default is all origins
+ c.allowedOriginsAll = true
+ }
} else {
c.allowedOrigins = []string{}
c.allowedWOrigins = []wildcard{}
@@ -139,7 +147,7 @@ func New(options Options) *Cors {
break
} else if i := strings.IndexByte(origin, '*'); i >= 0 {
// Split the origin in two: start and end string without the *
- w := wildcard{origin[0:i], origin[i+1 : len(origin)]}
+ w := wildcard{origin[0:i], origin[i+1:]}
c.allowedWOrigins = append(c.allowedWOrigins, w)
} else {
c.allowedOrigins = append(c.allowedOrigins, origin)
@@ -166,7 +174,7 @@ func New(options Options) *Cors {
// Allowed Methods
if len(options.AllowedMethods) == 0 {
// Default is spec's "simple" methods
- c.allowedMethods = []string{"GET", "POST"}
+ c.allowedMethods = []string{http.MethodGet, http.MethodPost, http.MethodHead}
} else {
c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
}
@@ -174,16 +182,35 @@ func New(options Options) *Cors {
return c
}
-// Default creates a new Cors handler with default options
-func Default() *Cors {
- return New(Options{})
+// Handler creates a new Cors handler with passed options.
+func Handler(options Options) func(next http.Handler) http.Handler {
+ c := New(options)
+ return c.Handler
+}
+
+// AllowAll create a new Cors handler with permissive configuration allowing all
+// origins with all standard methods with any header and credentials.
+func AllowAll() *Cors {
+ return New(Options{
+ AllowedOrigins: []string{"*"},
+ AllowedMethods: []string{
+ http.MethodHead,
+ http.MethodGet,
+ http.MethodPost,
+ http.MethodPut,
+ http.MethodPatch,
+ http.MethodDelete,
+ },
+ AllowedHeaders: []string{"*"},
+ AllowCredentials: false,
+ })
}
// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func (c *Cors) Handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Method == "OPTIONS" {
+ if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
c.logf("Handler: Preflight request")
c.handlePreflight(w, r)
// Preflight requests are standalone and should stop the chain as some other
@@ -192,6 +219,8 @@ func (c *Cors) Handler(next http.Handler) http.Handler {
// headers (see #1)
if c.optionPassthrough {
next.ServeHTTP(w, r)
+ } else {
+ w.WriteHeader(http.StatusOK)
}
} else {
c.logf("Handler: Actual request")
@@ -206,7 +235,7 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
origin := r.Header.Get("Origin")
- if r.Method != "OPTIONS" {
+ if r.Method != http.MethodOptions {
c.logf("Preflight aborted: %s!=OPTIONS", r.Method)
return
}
@@ -236,7 +265,11 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
c.logf("Preflight aborted: headers '%v' not allowed", reqHeaders)
return
}
- headers.Set("Access-Control-Allow-Origin", origin)
+ if c.allowedOriginsAll {
+ headers.Set("Access-Control-Allow-Origin", "*")
+ } else {
+ headers.Set("Access-Control-Allow-Origin", origin)
+ }
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
// by Access-Control-Request-Method (if supported) can be enough
headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
@@ -260,10 +293,6 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
origin := r.Header.Get("Origin")
- if r.Method == "OPTIONS" {
- c.logf("Actual request no headers added: method == %s", r.Method)
- return
- }
// Always set Vary, see https://github.com/rs/cors/issues/10
headers.Add("Vary", "Origin")
if origin == "" {
@@ -280,14 +309,15 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
// We think it's a nice feature to be able to have control on those methods though.
if !c.isMethodAllowed(r.Method) {
- if c.log != nil {
- c.logf("Actual request no headers added: method '%s' not allowed",
- r.Method)
- }
+ c.logf("Actual request no headers added: method '%s' not allowed", r.Method)
return
}
- headers.Set("Access-Control-Allow-Origin", origin)
+ if c.allowedOriginsAll {
+ headers.Set("Access-Control-Allow-Origin", "*")
+ } else {
+ headers.Set("Access-Control-Allow-Origin", origin)
+ }
if len(c.exposedHeaders) > 0 {
headers.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
}
@@ -297,10 +327,10 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
c.logf("Actual response added headers: %v", headers)
}
-// convenience method. checks if debugging is turned on before printing
+// convenience method. checks if a logger is set.
func (c *Cors) logf(format string, a ...interface{}) {
- if c.log != nil {
- c.log.Printf(format, a...)
+ if c.Log != nil {
+ c.Log.Printf(format, a...)
}
}
@@ -328,14 +358,14 @@ func (c *Cors) isOriginAllowed(r *http.Request, origin string) bool {
}
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
-// on the endpoing
+// on the endpoint
func (c *Cors) isMethodAllowed(method string) bool {
if len(c.allowedMethods) == 0 {
// If no method allowed, always return false, even for preflight request
return false
}
method = strings.ToUpper(method)
- if method == "OPTIONS" {
+ if method == http.MethodOptions {
// Always allow preflight requests
return true
}
@@ -359,6 +389,7 @@ func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
for _, h := range c.allowedHeaders {
if h == header {
found = true
+ break
}
}
if !found {
diff --git a/vendor/github.com/go-chi/cors/go.mod b/vendor/github.com/go-chi/cors/go.mod
new file mode 100644
index 000000000..26ddfc1c2
--- /dev/null
+++ b/vendor/github.com/go-chi/cors/go.mod
@@ -0,0 +1,3 @@
+module github.com/go-chi/cors
+
+go 1.14
diff --git a/vendor/github.com/go-chi/httprate/LICENSE b/vendor/github.com/go-chi/httprate/LICENSE
new file mode 100644
index 000000000..0bb58ba51
--- /dev/null
+++ b/vendor/github.com/go-chi/httprate/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka).
+
+MIT License
+
+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.
diff --git a/vendor/github.com/go-chi/httprate/README.md b/vendor/github.com/go-chi/httprate/README.md
index 5e083ef07..68ae09ffb 100644
--- a/vendor/github.com/go-chi/httprate/README.md
+++ b/vendor/github.com/go-chi/httprate/README.md
@@ -47,6 +47,41 @@ func main() {
}
```
+## Common use cases
+
+### Rate limit by IP and URL path (aka endpoint)
+```go
+ r.Use(httprate.Limit(
+ 10, // requests
+ 10*time.Second, // per duration
+ httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
+ ))
+```
+
+### Rate limit by arbitrary keys
+```go
+ r.Use(httprate.Limit(
+ 100, // requests
+ 1*time.Minute, // per duration
+ // an oversimplified example of rate limiting by a custom header
+ httprate.WithKeyFuncs(func(r *http.Request) (string, error) {
+ return r.Header.Get("X-Access-Token"), nil
+ }),
+ ))
+```
+
+### Send specific response for rate limited requests
+
+```go
+ r.Use(httprate.Limit(
+ 10, // requests
+ 1*time.Second, // per duration
+ httprate.WithLimitHandler(func(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "some specific response here", http.StatusTooManyRequests)
+ }),
+ ))
+```
+
## LICENSE
MIT
diff --git a/vendor/github.com/go-chi/httprate/go.mod b/vendor/github.com/go-chi/httprate/go.mod
index f378f4c02..39936821a 100644
--- a/vendor/github.com/go-chi/httprate/go.mod
+++ b/vendor/github.com/go-chi/httprate/go.mod
@@ -2,4 +2,4 @@ module github.com/go-chi/httprate
go 1.14
-require github.com/cespare/xxhash/v2 v2.1.1
+require github.com/cespare/xxhash/v2 v2.1.2
diff --git a/vendor/github.com/go-chi/httprate/go.sum b/vendor/github.com/go-chi/httprate/go.sum
index 504f6aabe..507c3fcfd 100644
--- a/vendor/github.com/go-chi/httprate/go.sum
+++ b/vendor/github.com/go-chi/httprate/go.sum
@@ -1,2 +1,2 @@
-github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
+github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
diff --git a/vendor/github.com/go-chi/httprate/httprate.go b/vendor/github.com/go-chi/httprate/httprate.go
index 111480424..ec77d953a 100644
--- a/vendor/github.com/go-chi/httprate/httprate.go
+++ b/vendor/github.com/go-chi/httprate/httprate.go
@@ -7,24 +7,27 @@ import (
"time"
)
-func Limit(requestLimit int, windowLength time.Duration, keyFuncs ...KeyFunc) func(next http.Handler) http.Handler {
- return NewRateLimiter(requestLimit, windowLength, nil, keyFuncs...).Handler
+func Limit(requestLimit int, windowLength time.Duration, options ...Option) func(next http.Handler) http.Handler {
+ return NewRateLimiter(requestLimit, windowLength, options...).Handler
}
type KeyFunc func(r *http.Request) (string, error)
+type Option func(rl *rateLimiter)
func LimitAll(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handler {
return Limit(requestLimit, windowLength)
}
func LimitByIP(requestLimit int, windowLength time.Duration) func(next http.Handler) http.Handler {
- return Limit(requestLimit, windowLength, KeyByIP)
+ return Limit(requestLimit, windowLength, WithKeyFuncs(KeyByIP))
}
func KeyByIP(r *http.Request) (string, error) {
var ip string
- if xrip := r.Header.Get("X-Real-IP"); xrip != "" {
+ if tcip := r.Header.Get("True-Client-IP"); tcip != "" {
+ ip = tcip
+ } else if xrip := r.Header.Get("X-Real-IP"); xrip != "" {
ip = xrip
} else if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
i := strings.Index(xff, ", ")
@@ -47,6 +50,26 @@ func KeyByEndpoint(r *http.Request) (string, error) {
return r.URL.Path, nil
}
+func WithKeyFuncs(keyFuncs ...KeyFunc) Option {
+ return func(rl *rateLimiter) {
+ if len(keyFuncs) > 0 {
+ rl.keyFn = composedKeyFunc(keyFuncs...)
+ }
+ }
+}
+
+func WithLimitHandler(h http.HandlerFunc) Option {
+ return func(rl *rateLimiter) {
+ rl.onRequestLimit = h
+ }
+}
+
+func WithLimitCounter(c LimitCounter) Option {
+ return func(rl *rateLimiter) {
+ rl.limitCounter = c
+ }
+}
+
func composedKeyFunc(keyFuncs ...KeyFunc) KeyFunc {
return func(r *http.Request) (string, error) {
var key strings.Builder
diff --git a/vendor/github.com/go-chi/httprate/limiter.go b/vendor/github.com/go-chi/httprate/limiter.go
index db8d4ac71..a040270d1 100644
--- a/vendor/github.com/go-chi/httprate/limiter.go
+++ b/vendor/github.com/go-chi/httprate/limiter.go
@@ -15,29 +15,40 @@ type LimitCounter interface {
Get(key string, previousWindow, currentWindow time.Time) (int, int, error)
}
-func NewRateLimiter(requestLimit int, windowLength time.Duration, counter LimitCounter, keyFuncs ...KeyFunc) *rateLimiter {
- var keyFn KeyFunc
- if len(keyFuncs) == 0 {
- keyFn = func(r *http.Request) (string, error) {
- return "*", nil
- }
- } else {
- keyFn = composedKeyFunc(keyFuncs...)
+func NewRateLimiter(requestLimit int, windowLength time.Duration, options ...Option) *rateLimiter {
+ return newRateLimiter(requestLimit, windowLength, options...)
+}
+
+func newRateLimiter(requestLimit int, windowLength time.Duration, options ...Option) *rateLimiter {
+ rl := &rateLimiter{
+ requestLimit: requestLimit,
+ windowLength: windowLength,
}
- if counter == nil {
- counter = &localCounter{
+ for _, opt := range options {
+ opt(rl)
+ }
+
+ if rl.keyFn == nil {
+ rl.keyFn = func(r *http.Request) (string, error) {
+ return "*", nil
+ }
+ }
+
+ if rl.limitCounter == nil {
+ rl.limitCounter = &localCounter{
counters: make(map[uint64]*count),
windowLength: windowLength,
}
}
- return &rateLimiter{
- requestLimit: requestLimit,
- windowLength: windowLength,
- keyFn: keyFn,
- limitCounter: counter,
+ if rl.onRequestLimit == nil {
+ rl.onRequestLimit = func(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
+ }
}
+
+ return rl
}
func LimitCounterKey(key string, window time.Time) uint64 {
@@ -48,10 +59,11 @@ func LimitCounterKey(key string, window time.Time) uint64 {
}
type rateLimiter struct {
- requestLimit int
- windowLength time.Duration
- keyFn KeyFunc
- limitCounter LimitCounter
+ requestLimit int
+ windowLength time.Duration
+ keyFn KeyFunc
+ limitCounter LimitCounter
+ onRequestLimit http.HandlerFunc
}
func (r *rateLimiter) Counter() LimitCounter {
@@ -103,7 +115,8 @@ func (l *rateLimiter) Handler(next http.Handler) http.Handler {
}
if nrate >= l.requestLimit {
- http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
+ w.Header().Set("Retry-After", fmt.Sprintf("%d", int(l.windowLength.Seconds()))) // RFC 6585
+ l.onRequestLimit(w, r)
return
}
@@ -175,6 +188,7 @@ func (c *localCounter) evict() {
if time.Since(c.lastEvict) < d {
return
}
+ c.lastEvict = time.Now()
for k, v := range c.counters {
if time.Since(v.updatedAt) >= d {
diff --git a/vendor/github.com/go-chi/jwtauth/.travis.yml b/vendor/github.com/go-chi/jwtauth/.travis.yml
deleted file mode 100644
index da1bafdb6..000000000
--- a/vendor/github.com/go-chi/jwtauth/.travis.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-language: go
-
-go:
- - 1.10.x
- - 1.11.x
-
-install:
- - go get -u golang.org/x/tools/cmd/goimports
-
-script:
- - go get -d -t ./...
- - go test ./...
- - >
- goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :
diff --git a/vendor/github.com/go-chi/jwtauth/README.md b/vendor/github.com/go-chi/jwtauth/README.md
index cd42dfe09..f68e46cdb 100644
--- a/vendor/github.com/go-chi/jwtauth/README.md
+++ b/vendor/github.com/go-chi/jwtauth/README.md
@@ -1,4 +1,4 @@
-# jwtauth - JWT authentication middleware for Go HTTP services
+# jwtauth - JWT authentication middleware for HTTP services
[![GoDoc Widget]][godoc]
@@ -7,9 +7,7 @@ from a http request and send the result down the request context (`context.Conte
Please note, `jwtauth` works with any Go http router, but resides under the go-chi group
for maintenance and organization - its only 3rd party dependency is the underlying jwt library
-"github.com/dgrijalva/jwt-go".
-
-This package uses the new `context` package in Go 1.7 stdlib and [net/http#Request.Context](https://golang.org/pkg/net/http/#Request.Context) to pass values between handler chains.
+"github.com/lestrrat-go/jwx".
In a complete JWT-authentication flow, you'll first capture the token from a http
request, decode it, verify it and then validate that its correctly signed and hasn't
@@ -25,12 +23,11 @@ your flow (ie. with a JSON error response body).
By default, the `Verifier` will search for a JWT token in a http request, in the order:
-1. 'jwt' URI query parameter
-2. 'Authorization: BEARER T' request header
-3. 'jwt' Cookie value
+1. 'Authorization: BEARER T' request header
+2. 'jwt' Cookie value
-The first JWT string that is found as a query parameter, authorization header
-or cookie header is then decoded by the `jwt-go` library and a \*jwt.Token
+The first JWT string that is found as an authorization header
+or cookie header is then decoded by the `lestrrat-go/jwx` library and a jwt.Token
object is set on the request context. In the case of a signature decoding error
the Verifier will also set the error on the request context.
@@ -41,7 +38,7 @@ http response.
Note: jwtauth supports custom verification sequences for finding a token
from a request by using the `Verify` middleware instantiator directly. The default
-`Verifier` is instantiated by calling `Verify(ja, TokenFromQuery, TokenFromHeader, TokenFromCookie)`.
+`Verifier` is instantiated by calling `Verify(ja, TokenFromHeader, TokenFromCookie)`.
# Usage
@@ -65,7 +62,7 @@ func init() {
// For debugging/example purposes, we generate and print
// a sample jwt token with claims `user_id:123` here:
- _, tokenString, _ := tokenAuth.Encode(jwt.MapClaims{"user_id": 123})
+ _, tokenString, _ := tokenAuth.Encode(map[string]interface{}{"user_id": 123})
fmt.Printf("DEBUG: a sample jwt is %s\n\n", tokenString)
}
diff --git a/vendor/github.com/go-chi/jwtauth/go.mod b/vendor/github.com/go-chi/jwtauth/go.mod
index 599832730..d12d8a7d1 100644
--- a/vendor/github.com/go-chi/jwtauth/go.mod
+++ b/vendor/github.com/go-chi/jwtauth/go.mod
@@ -1,3 +1,9 @@
module github.com/go-chi/jwtauth
-require github.com/dgrijalva/jwt-go v3.2.0+incompatible
+go 1.15
+
+require (
+ github.com/go-chi/chi v1.5.1
+ github.com/lestrrat-go/jwx v1.1.0
+ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
+)
diff --git a/vendor/github.com/go-chi/jwtauth/go.sum b/vendor/github.com/go-chi/jwtauth/go.sum
index 6a8f14018..563ca752c 100644
--- a/vendor/github.com/go-chi/jwtauth/go.sum
+++ b/vendor/github.com/go-chi/jwtauth/go.sum
@@ -1,2 +1,57 @@
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w=
+github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
+github.com/goccy/go-json v0.3.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/lestrrat-go/backoff/v2 v2.0.7 h1:i2SeK33aOFJlUNJZzf2IpXRBvqBBnaGXfY5Xaop/GsE=
+github.com/lestrrat-go/backoff/v2 v2.0.7/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
+github.com/lestrrat-go/codegen v1.0.0/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
+github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
+github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
+github.com/lestrrat-go/iter v1.0.0 h1:QD+hHQPDSHC4rCJkZYY/yXChYr/vjfBopKekTc+7l4Q=
+github.com/lestrrat-go/iter v1.0.0/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
+github.com/lestrrat-go/jwx v1.1.0 h1:gerfaQK3mEIL8X8oJ5MFvsB/JuxXoGryLtTlNmPi3/k=
+github.com/lestrrat-go/jwx v1.1.0/go.mod h1:vn9FzD6gJtKkgYs7RTKV7CjWtEka8F/voUollhnn4QE=
+github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
+github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCiIqbJRmKeiADU4=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/go-chi/jwtauth/jwtauth.go b/vendor/github.com/go-chi/jwtauth/jwtauth.go
index 1118addef..38104febd 100644
--- a/vendor/github.com/go-chi/jwtauth/jwtauth.go
+++ b/vendor/github.com/go-chi/jwtauth/jwtauth.go
@@ -3,52 +3,45 @@ package jwtauth
import (
"context"
"errors"
- "fmt"
"net/http"
"strings"
"time"
- "github.com/dgrijalva/jwt-go"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwt"
)
-// Context keys
+type JWTAuth struct {
+ alg jwa.SignatureAlgorithm
+ signKey interface{} // private-key
+ verifyKey interface{} // public-key, only used by RSA and ECDSA algorithms
+ verifier jwt.ParseOption
+}
+
var (
TokenCtxKey = &contextKey{"Token"}
ErrorCtxKey = &contextKey{"Error"}
)
-// Library errors
var (
- ErrUnauthorized = errors.New("jwtauth: token is unauthorized")
- ErrExpired = errors.New("jwtauth: token is expired")
- ErrNBFInvalid = errors.New("jwtauth: token nbf validation failed")
- ErrIATInvalid = errors.New("jwtauth: token iat validation failed")
- ErrNoTokenFound = errors.New("jwtauth: no token found")
- ErrAlgoInvalid = errors.New("jwtauth: algorithm mismatch")
+ ErrUnauthorized = errors.New("token is unauthorized")
+ ErrExpired = errors.New("token is expired")
+ ErrNBFInvalid = errors.New("token nbf validation failed")
+ ErrIATInvalid = errors.New("token iat validation failed")
+ ErrNoTokenFound = errors.New("no token found")
+ ErrAlgoInvalid = errors.New("algorithm mismatch")
)
-type JWTAuth struct {
- signKey interface{}
- verifyKey interface{}
- signer jwt.SigningMethod
- parser *jwt.Parser
-}
-
-// New creates a JWTAuth authenticator instance that provides middleware handlers
-// and encoding/decoding functions for JWT signing.
func New(alg string, signKey interface{}, verifyKey interface{}) *JWTAuth {
- return NewWithParser(alg, &jwt.Parser{}, signKey, verifyKey)
-}
+ ja := &JWTAuth{alg: jwa.SignatureAlgorithm(alg), signKey: signKey, verifyKey: verifyKey}
-// NewWithParser is the same as New, except it supports custom parser settings
-// introduced in jwt-go/v2.4.0.
-func NewWithParser(alg string, parser *jwt.Parser, signKey interface{}, verifyKey interface{}) *JWTAuth {
- return &JWTAuth{
- signKey: signKey,
- verifyKey: verifyKey,
- signer: jwt.GetSigningMethod(alg),
- parser: parser,
+ if ja.verifyKey != nil {
+ ja.verifier = jwt.WithVerify(ja.alg, ja.verifyKey)
+ } else {
+ ja.verifier = jwt.WithVerify(ja.alg, ja.signKey)
}
+
+ return ja
}
// Verifier http middleware handler will verify a JWT string from a http request.
@@ -69,7 +62,7 @@ func NewWithParser(alg string, parser *jwt.Parser, signKey interface{}, verifyKe
// http response.
func Verifier(ja *JWTAuth) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
- return Verify(ja, TokenFromQuery, TokenFromHeader, TokenFromCookie)(next)
+ return Verify(ja, TokenFromHeader, TokenFromCookie)(next)
}
}
@@ -85,73 +78,81 @@ func Verify(ja *JWTAuth, findTokenFns ...func(r *http.Request) string) func(http
}
}
-func VerifyRequest(ja *JWTAuth, r *http.Request, findTokenFns ...func(r *http.Request) string) (*jwt.Token, error) {
- var tokenStr string
- var err error
+func VerifyRequest(ja *JWTAuth, r *http.Request, findTokenFns ...func(r *http.Request) string) (jwt.Token, error) {
+ var tokenString string
// Extract token string from the request by calling token find functions in
// the order they where provided. Further extraction stops if a function
// returns a non-empty string.
for _, fn := range findTokenFns {
- tokenStr = fn(r)
- if tokenStr != "" {
+ tokenString = fn(r)
+ if tokenString != "" {
break
}
}
- if tokenStr == "" {
+ if tokenString == "" {
return nil, ErrNoTokenFound
}
- // Verify the token
- token, err := ja.Decode(tokenStr)
+ return VerifyToken(ja, tokenString)
+}
+
+func VerifyToken(ja *JWTAuth, tokenString string) (jwt.Token, error) {
+ // Decode & verify the token
+ token, err := ja.Decode(tokenString)
if err != nil {
- if verr, ok := err.(*jwt.ValidationError); ok {
- if verr.Errors&jwt.ValidationErrorExpired > 0 {
- return token, ErrExpired
- } else if verr.Errors&jwt.ValidationErrorIssuedAt > 0 {
- return token, ErrIATInvalid
- } else if verr.Errors&jwt.ValidationErrorIssuedAt > 0 {
- return token, ErrNBFInvalid
- }
- }
- return token, err
+ return token, ErrorReason(err)
}
- if token == nil || !token.Valid {
- err = ErrUnauthorized
- return token, err
+ if token == nil {
+ return nil, ErrUnauthorized
}
- // Verify signing algorithm
- if token.Method != ja.signer {
- return token, ErrAlgoInvalid
+ if err := jwt.Validate(token); err != nil {
+ return token, ErrorReason(err)
}
// Valid!
return token, nil
}
-func (ja *JWTAuth) Encode(claims jwt.Claims) (t *jwt.Token, tokenString string, err error) {
- t = jwt.New(ja.signer)
- t.Claims = claims
- tokenString, err = t.SignedString(ja.signKey)
- t.Raw = tokenString
- return
-}
-
-func (ja *JWTAuth) Decode(tokenString string) (t *jwt.Token, err error) {
- t, err = ja.parser.Parse(tokenString, ja.keyFunc)
- if err != nil {
- return nil, err
+func (ja *JWTAuth) Encode(claims map[string]interface{}) (t jwt.Token, tokenString string, err error) {
+ t = jwt.New()
+ for k, v := range claims {
+ t.Set(k, v)
}
+ payload, err := ja.sign(t)
+ if err != nil {
+ return nil, "", err
+ }
+ tokenString = string(payload)
return
}
-func (ja *JWTAuth) keyFunc(t *jwt.Token) (interface{}, error) {
- if ja.verifyKey != nil {
- return ja.verifyKey, nil
- } else {
- return ja.signKey, nil
+func (ja *JWTAuth) Decode(tokenString string) (jwt.Token, error) {
+ return ja.parse([]byte(tokenString))
+}
+
+func (ja *JWTAuth) sign(token jwt.Token) ([]byte, error) {
+ return jwt.Sign(token, ja.alg, ja.signKey)
+}
+
+func (ja *JWTAuth) parse(payload []byte) (jwt.Token, error) {
+ return jwt.Parse(payload, ja.verifier)
+}
+
+// ErrorReason will normalize the error message from the underlining
+// jwt library
+func ErrorReason(err error) error {
+ switch err.Error() {
+ case "exp not satisfied", ErrExpired.Error():
+ return ErrExpired
+ case "iat not satisfied", ErrIATInvalid.Error():
+ return ErrIATInvalid
+ case "nbf not satisfied", ErrNBFInvalid.Error():
+ return ErrNBFInvalid
+ default:
+ return ErrUnauthorized
}
}
@@ -164,11 +165,11 @@ func Authenticator(next http.Handler) http.Handler {
token, _, err := FromContext(r.Context())
if err != nil {
- http.Error(w, http.StatusText(401), 401)
+ http.Error(w, err.Error(), 401)
return
}
- if token == nil || !token.Valid {
+ if token == nil || jwt.Validate(token) != nil {
http.Error(w, http.StatusText(401), 401)
return
}
@@ -178,27 +179,28 @@ func Authenticator(next http.Handler) http.Handler {
})
}
-func NewContext(ctx context.Context, t *jwt.Token, err error) context.Context {
+func NewContext(ctx context.Context, t jwt.Token, err error) context.Context {
ctx = context.WithValue(ctx, TokenCtxKey, t)
ctx = context.WithValue(ctx, ErrorCtxKey, err)
return ctx
}
-func FromContext(ctx context.Context) (*jwt.Token, jwt.MapClaims, error) {
- token, _ := ctx.Value(TokenCtxKey).(*jwt.Token)
+func FromContext(ctx context.Context) (jwt.Token, map[string]interface{}, error) {
+ token, _ := ctx.Value(TokenCtxKey).(jwt.Token)
+
+ var err error
+ var claims map[string]interface{}
- var claims jwt.MapClaims
if token != nil {
- if tokenClaims, ok := token.Claims.(jwt.MapClaims); ok {
- claims = tokenClaims
- } else {
- panic(fmt.Sprintf("jwtauth: unknown type of Claims: %T", token.Claims))
+ claims, err = token.AsMap(context.Background())
+ if err != nil {
+ return token, nil, err
}
} else {
- claims = jwt.MapClaims{}
+ claims = map[string]interface{}{}
}
- err, _ := ctx.Value(ErrorCtxKey).(error)
+ err, _ = ctx.Value(ErrorCtxKey).(error)
return token, claims, err
}
@@ -219,22 +221,22 @@ func ExpireIn(tm time.Duration) int64 {
}
// Set issued at ("iat") to specified time in the claims
-func SetIssuedAt(claims jwt.MapClaims, tm time.Time) {
+func SetIssuedAt(claims map[string]interface{}, tm time.Time) {
claims["iat"] = tm.UTC().Unix()
}
// Set issued at ("iat") to present time in the claims
-func SetIssuedNow(claims jwt.MapClaims) {
+func SetIssuedNow(claims map[string]interface{}) {
claims["iat"] = EpochNow()
}
// Set expiry ("exp") in the claims
-func SetExpiry(claims jwt.MapClaims, tm time.Time) {
+func SetExpiry(claims map[string]interface{}, tm time.Time) {
claims["exp"] = tm.UTC().Unix()
}
// Set expiry ("exp") in the claims to some duration from the present time
-func SetExpiryIn(claims jwt.MapClaims, tm time.Duration) {
+func SetExpiryIn(claims map[string]interface{}, tm time.Duration) {
claims["exp"] = ExpireIn(tm)
}
@@ -261,6 +263,14 @@ func TokenFromHeader(r *http.Request) string {
// TokenFromQuery tries to retreive the token string from the "jwt" URI
// query parameter.
+//
+// To use it, build our own middleware handler, such as:
+//
+// func Verifier(ja *JWTAuth) func(http.Handler) http.Handler {
+// return func(next http.Handler) http.Handler {
+// return Verify(ja, TokenFromQuery, TokenFromHeader, TokenFromCookie)(next)
+// }
+// }
func TokenFromQuery(r *http.Request) string {
// Get token from query param named "jwt".
return r.URL.Query().Get("jwt")
diff --git a/vendor/github.com/lestrrat-go/jwx/.gitignore b/vendor/github.com/lestrrat-go/jwx/.gitignore
new file mode 100644
index 000000000..5b2720b66
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/.gitignore
@@ -0,0 +1,35 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+
+# IDE
+.idea
+.vscode
+.DS_Store
+*~
+
+coverage.out
+
+# I redirect my test output to files named "out" way too often
+out
diff --git a/vendor/github.com/lestrrat-go/jwx/.golangci.yml b/vendor/github.com/lestrrat-go/jwx/.golangci.yml
new file mode 100644
index 000000000..b5afb667a
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/.golangci.yml
@@ -0,0 +1,82 @@
+run:
+
+linters-settings:
+ govet:
+ enable-all: true
+ disable:
+ - shadow
+ - fieldalignment
+
+linters:
+ enable-all: true
+ disable:
+ - cyclop
+ - dupl
+ - exhaustive
+ - exhaustivestruct
+ - errorlint
+ - funlen
+ - gci
+ - gochecknoglobals
+ - gochecknoinits
+ - gocognit
+ - gocritic
+ - gocyclo
+ - godot
+ - godox
+ - goerr113
+ - gofumpt
+ - golint #deprecated
+ - gomnd
+ - gosec
+ - govet
+ - interfacer # deprecated
+ - ifshort
+ - lll
+ - maligned # deprecated
+ - makezero
+ - nakedret
+ - nestif
+ - nlreturn
+ - paralleltest
+ - scopelint # deprecated
+ - tagliatelle
+ - testpackage
+ - thelper
+ - wrapcheck
+ - wsl
+
+issues:
+ exclude-rules:
+ # not needed
+ - path: /*.go
+ text: "ST1003: should not use underscores in package names"
+ linters:
+ - stylecheck
+ - path: /*.go
+ text: "don't use an underscore in package name"
+ linters:
+ - revive
+ - path: /main.go
+ linters:
+ - errcheck
+ - path: internal/codegen/codegen.go
+ linters:
+ - errcheck
+ - path: /*_test.go
+ linters:
+ - errcheck
+ - forcetypeassert
+ - path: /*_example_test.go
+ linters:
+ - forbidigo
+ - path: cmd/jwx/jwx.go
+ linters:
+ - forbidigo
+
+ # Maximum issues count per one linter. Set to 0 to disable. Default is 50.
+ max-issues-per-linter: 0
+
+ # Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
+ max-same-issues: 0
+
diff --git a/vendor/github.com/lestrrat-go/jwx/Changes b/vendor/github.com/lestrrat-go/jwx/Changes
new file mode 100644
index 000000000..c924318a4
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/Changes
@@ -0,0 +1,613 @@
+Changes
+=======
+
+v1.2.14 22 Dec 2021
+[New Features]
+ * `jwk.Fetch()` and `(*jwk.AutoRefresh).Configure()` can now take `jwk.Whitelist`
+ object to check for the validity of a url to be fetched
+ * `jws.VerifyAuto()` has been added to verify payloads that can be verified
+ using the JWK set provided in the "jku" field. This function is purposely
+ separated from the `jws.Verify()` function because 1) the required parameters
+ are different, and 2) Users MUST be aware that they are doing a totally
+ different operation than a regular `jws.Verify()`
+ * `(jwk.AutoRefresh).IsRegistered()` has been added.
+
+[Bug fixes]
+ * `jws.SignMulti()` has been fixed to assign the "kid" field of the key used
+ for signing the payload
+ * `jws.SignMulti()` has been fixed to respect the "kid" field of the protected
+ header, not the public header
+
+v1.2.13 07 Dec 2021
+[New Features]
+ * `jwt` package now has a `Builder` that may make it easier to programmatically
+ create a JWT for some users.
+ * `jwt` errors now can be distinguished between validation errors and others.
+ Use `jwt.IsValidationError()` to check if it's a validation error, and then
+ use `errors.Is()` to check if it's one of the known (oft-used) errors
+
+v1.2.12 01 Dec 2021
+[New Features]
+ * `jwk.Set` can now parse private parameters. For example, after parsing
+ a JWKS serialized as `{"foo": "bar", "keys": [...]}`, users can get to
+ the value of `"foo"` by calling `set.Field("foo")`
+ * `jwk.Set` now has `Set()` method to set field values.
+
+v1.2.11 14 Nov 2021
+[Security Fix]
+ * It was reported that since v1.2.6, it was possible to craft
+ a special JSON object to bypass JWT verification via `jwt.Parse`.
+ If you relied on this module to perform all the verification,
+ upgrade is strongly recommended.
+
+v1.2.10 09 Nov 2021
+[Bug fixes]
+ * Parsing OpenID claims were not working for some fields.
+ This was caused by the same problem as the problem fixed in v1.2.9.
+ Proper tests have been added.
+
+v1.2.9 26 Oct 2021
+[Bug fixes]
+ * Parsing `key_ops` for JWKs which was broken in v1.2.8 has been fixed.
+
+v1.2.8 21 Oct 2021
+[Miscellaneous]
+ * `jws.Message`, `jws.Signature`, `jws.Headers` have been reworked
+ to allow JSON messages to be verified correctly. The problem can
+ be caused when protected headers are serialized one way (perhaps
+ `{"c":3","a":1,"b":2}` was used before being base64-encoded) but
+ the Go serialization differed from it (Go serializes in alphabetical
+ order: `{"a":1,"b":2,"c":3}`)
+
+ Messages serialized in compact form do NOT suffer from the
+ same problem.
+
+ This is close to fixes that went in v1.2.2. It boils down to the
+ fact that once deserialized, the JWS messages lose part of its
+ information (namely, the raw, original protected header value),
+ and neither users nor the developers of this library should
+ rely on it.
+
+ * Code generation has be refactored. The main go.mod should now
+ have slightly less dependencies.
+
+v1.2.7 26 Sep 2021
+[New features]
+ * `jwt.InferAlgorithmFromKey()` option is now available to "guess"
+ the algorithm used to verify the JWS signature on a JWT using
+ a JWKS (key set). This allows you to match JWKs that do not have
+ the `alg` field populated.
+
+ We understand that some providers do not provide the `alg` field,
+ which is a nuisance to users. But from a purely security minded PoV,
+ we don't think that this "try until something works" approach is a
+ good one, even if there are no known exploits. This is why the
+ default `jwt.Parse` mechanism is unchanged, and an explicit option
+ has been added.
+
+ * Types `jwt.KeySetProvider` and `jwk.KeySetProviderFunc` have been
+ added. Along with `jwt.WithKeySetProvider()` option, `jwt.Parse`
+ can now choose the `jwk.Set` to use for signature verification
+ dynamically using the UNVERFIEID token as a clue.
+
+ You should NOT trust the token information too much. For example,
+ DO NOT directly use values from the token as verificatin parameters
+ (such as the signature algorithm)
+
+ * `jwt.WithValidator()` has been added to allow users pass in aribtrary
+ validation code to the `jwt.Validate()` method.
+
+ It is also now possible to pass in a `context.Context` object to
+ `jwt.Validate()` using `jwt.WithContext()` option.
+
+[Miscellaneous]
+ * Make the error messages when `jwt.ParseRequest` fails a bit better.
+ * Moved around documentation within the repository
+ * Validation logic for `jwt.Validate()` has been refactored to use the
+ new `jwt.Validator` mechanism
+
+v1.2.6 24 Aug 2021
+[New features]
+ * Support `crypto.Signer` keys for RSA, ECDSA, and EdDSA family
+ of signatures in `jws.Sign`
+[Miscellaneous]
+ * `jwx.GuessFormat()` now requires the presense of both `payload` and
+ `signatures` keys for it to guess that a JSON object is a JWS message.
+ * Slightly enhance `jwt.Parse()` performance.
+
+v1.2.5 04 Aug 2021
+[New features]
+ * Implement RFC7797. The value of the header field `b64` changes
+ how the payload is treated in JWS
+ * Implement detached payloads for JWS
+ * Implement (jwk.AutoRefresh).ErrorSink() to register a channel
+ where you can receive errors from fetches and parses that occur during
+ JWK(s) retrieval.
+
+v1.2.4 15 Jul 2021
+[Bug fixes]
+ * We had the same off-by-one in another place and jumped the gun on
+ releasing a new version. At least we were making mistakes uniformally :/
+ `(jwk.Set).Remove` should finally be fixed.
+
+[New features]
+ * `(jwk.Set).Clone()` has been added.
+
+v1.2.3 15 Jul 2021
+[Bug fixes]
+ * jwk.Set incorrectly removed 2 elements instead of one.
+
+[Miscellaneous]
+ * github.com/goccy/go-json has been upgraded to v0.7.4
+
+v1.2.2 13 Jul 2021
+[Deprecation notice]
+ * `(jwe.Message).Decrypt()` will be removed from the API upon the next
+ major release.
+
+[Bug Fixes]
+ * `jwe.Decrypt` and `(jwe.Message).Decrypt()` failed to decrypt even
+ with the correct message contents when used along with `jwe.RegisterCustomField`
+
+[New features]
+ JWX
+ * Add GuessFormat() function to guess what the payload is.
+
+ JWT
+ * Options `jwt.WithMinDelta()`, `jwt.WithMaxDelta()` have been added.
+ These can be used to compare time-based fields in the JWT object.
+ * Option `jwt.WithRequiredClaim()` has been added. This can be used
+ to check that JWT contains the given claim.
+ * `jwt.Parse` now understands payloads that have been encrypted _and_ signed.
+ This is more in line with the RFC than the previous implementation, but
+ due to the fact that it requires a couple of extra unmarshaling, it may
+ add some amount of overhead.
+ * `jwt.Serializer` has been added as an easy wrapper to perform multiple
+ levels of serializations (e.g. apply JWS, then JWE)
+
+ JWE
+ * Option `jwe.WithMessage()` has been added. This allows the user to
+ obtain both the decrypted payload _and_ the raw `*jwe.Message` in one
+ go when `jwe.Decrypt()` is called
+ * Option `jwe.WithPostParser()`, along with `jwe.PostParser` and `jwe.PostParseFunc`
+ has been added. This allows advanced users to hook into the `jwe.Decrypt()`
+ process. The hook is called right after the JWE message has been parsed,
+ but before the actual decryption has taken place.
+ * `(jwe.Message).Decrypt()` has been marked for deprecation in a next major release.
+
+ JWS
+ * Option `jwe.WithMessage()` has been added. This allows the user to
+ obtain both the verified payload _and_ the raw `*jws.Message` in one
+ go when `jws.Verify()` is called
+ * Options to `jws.Sign()` are not of type `jws.SignOption`. There should be
+ no user-visible effects unless you were storing these somewhere.
+
+v1.2.1 02 Jun 2021
+[New features]
+ * Option `jwt.WithTypedClaim()` and `jwk.WithTypedField()` have been added.
+ They allow a per-object custom conversion from their JSON representation
+ to a Go object, much like `RegisterCustomField`.
+
+ The difference is that whereas `RegisterCustomField` has global effect,
+ these typed fields only take effect in the call where the option was
+ explicitly passed.
+
+ `jws` and `jwe` does not have these options because
+ (1) JWS and JWE messages don't generally carry much in terms of custom data
+ (2) This requires changes in function signatures.
+
+ Only use these options when you absolutely need to. While it is a powerful
+ tool, they do have many caveats, and abusing these features will have
+ negative effects. See the documentation for details
+
+v1.2.0 30 Apr 2021
+
+This is a security fix release with minor incompatibilities from earlier version
+with regards to the behavior of `jwt.Verify()` function
+
+[Security Fix]
+ * `jwt.Verify()` had improperly used the `"alg"` header from the JWS message
+ when `jwt.WithKeySet()` option was used (potentially allowing exploits
+ described in https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/.
+ This has been fixed by ONLY trusting the keys that you provide and
+ using the `"alg"` header from the keys themselves. (#375, #381)
+
+ As a side effect, `jwt.WithKeySet()` requires that all applicable keys
+ to contain a valid `"alg"` header. Without this we cannot safely choose a key to use,
+ and hence verification will fail.
+
+ The requirement for the `"alg"` header on keys is an INCOMPATIBLE behavior.
+ This may break existing code, if the key does not already have an `"alg"` header.
+
+[New features]
+ * `jwt.Settings()` and `jwt.WithFlattenAudience(bool)` has been added
+ to control how the "aud" claim is serialized into JSON. When this
+ is enabled, all JWTs with a single "aud" claim will serialize
+ the field as a single string, instead of an array of strings with
+ a single element, i.e.:
+
+ // jwt.WithFlattenAudience(true)
+ {"aud": "foo"}
+
+ // jwt.WithFlattenAudience(false)
+ {"aud": ["foo"]}
+
+ This setting has a global effect.
+
+[Buf fixes]
+ * jwt.Validate now returns true if the value in `nbf` field is exactly
+ the same as what the clock returns (e.g. token.nbf == time.Now())
+
+v1.1.7 02 Apr 2021
+[New features]
+ * `jwk.New` `jwk.Parse`, `jwk.ParseKey` can now take a Certificate in
+ ASN.1 DER format in PEM encoding to create a JWK.
+
+[Bug fixes]
+ * Protect `jwk.New()` from invalid RSA/ECDSA keys (#360, #361)
+
+[Miscellaneous]
+ * Removed "internal/blackmagic" and separated it to its own repository.
+ * Removed unused "marshal proxy" objects in jwt
+ * Added FAQ in `jwt` package
+
+v1.1.6 28 Mar 2021
+[Bug fixes]
+ * When an object (e.g. JWT) has a null value and `AsMap()` is called,
+ `github.com/lestrrat-go/iter` would panic.
+ This should be fixed in `github.com/lestrrat-go/iter@v1.0.1` and
+ the dependency has been updated accordingly
+
+[Miscellaneous]
+ * Added How-to style docs under `docs/`
+ * github.com/goccy/go-json dependency has been updated to v0.4.8
+
+v1.1.5 12 Mar 2021
+ This is a security fix release. The JWT validation could be skipped
+ for empty values. Upgrade recommended
+
+[Security Fix]
+ * JWT validation could be skipped for empty fields (#352).
+
+[Bug fixes]
+ * Allow setting JWT "typ" fields to any value (#351).
+ * Remove stray replace directive in cmd/jwx/go.mod (#349)
+
+v1.1.4 02 Mar 2021
+[New features]
+ * jwt.ParseRequest, jwt.ParseHeader, jwt.ParseForm have been added.
+ They are convenience functions to parse JWTs out of a HTTP request.
+
+[Miscellaneous]
+ * Fix jwt.Equals() so that comparison between values containing time.Time
+ actually work
+
+ * ES256K has been made non-default. You must enable it using a build tag
+
+ go build -tags jwx_es256k ...
+
+ Your program will still compile without this tag, but it will return
+ an error during runtime, when ES256K is encountered.
+ This feature is still experimental.
+
+v1.1.3 22 Feb 2021
+[New features]
+ * Implemented ES256K signing (#337)
+ This feature should be considered experimental
+
+[Miscellaneous]
+ * Bump minimum required version to go1.15
+ * Fix examples, bench, and cmd/jwx accidentally requiring go1.16
+ * Dependencies for "github.com/goccy/go-json" has been upgraded to
+ v0.4.7
+
+v1.1.2 16 Feb 2021
+[New features]
+ * `RegisterCustomField()` has been added, which allows users to
+ specify a private claim/field/header to decode into a particular
+ object of choice, instead of map[string]interface{} or []interface{} (#332, #333)
+
+[Bug fixes]
+ * Failures for `jwk.Key.MarshalJSON()` were not properly reported (#330, #331)
+
+[Miscellaneous]
+ * `jwe.Encrypt()` now takes options. This should not matter unless you
+ were somehow depending on its method signature.
+ * Dependencies for "github.com/goccy/go-json" has been upgraded to
+ v0.4.2
+
+v1.1.1 05 Feb 2021
+[New features]
+ * Command line tool `jwx` has ben completely reworked, and it is
+ now actually useful.
+
+ * JWKs can now be serialized into PEM files with ASN.1 DER format
+ data, which is useful when you need to work between JSON and PEM
+ data formats.
+
+ * Constants in jwa package now have can be listed via functions
+ in each category.
+
+ * jwe.Encrypt and jwe.Decrypt can now handle jwk.Key objects
+
+v1.1.0 31 Jan 2021
+ v1.1.0 is a release that attempts to fix as many of the quirky APIs
+ that survived the API breaking change of v0.9.x -> v1.0.0. This is
+ hopefully the last releases that change backwards compatibility
+ in a major way, at least for some time to come.
+
+ It is unfortunate that we need to introduce API changes, but we
+ keep learning how the library is being used and the pain points
+ of using this library. Most of the times these pain points are
+ things that we initially did not think about, which in turn
+ requires us to rethink of the API.
+
+ If you do not wish to spend the time fixing your usage, make sure
+ you have your go.mod set up to not automatically track the latest
+ changes.
+
+ However, if you do decide to use the latest version, we believe
+ the API is more uniform across packages, and generally is easier
+ to understand. We hope this library helps some of you out there.
+
+[BREAKING CHANGES]
+ * `jwk.Parse(io.Reader)`, `jws.Parse(io.Reader)`, `jwt.Parse(io.Reader)`,
+ have all been changed to `Parse([]byte)`. To use an `io.Reader`,
+ use `ParseReader(io.Reader)`. `jwe.Parse` already took `[]byte`, so
+ has not been changed.
+
+ With this change, all four package `jwe`, `jwk`, `jws`, and `jwt` follow
+ the same API design, which should make things easier to navigate:
+
+ Parse([]byte)
+ ParseString(string)
+ ParseReader(io.Reader)
+
+ * `jwk.Set` is now an interface, not a struct. `jwk.Set` now has a
+ well-defined API to access and modify the `jwk.Key` objects that it holds.
+
+ Add(jwk.Key) bool
+ Clear()
+ Get(int) (jwk.Key, bool)
+ Index(jwk.Key) int
+ Len() int
+ LookupKeyID() (jwk.Key, bool) // Read the section about it below
+ Remove(jwk.Key) bool
+ Iterate(context.Context) KeyIterator
+
+ * `(jwk.Set).LookupKeyID()` no longer returns an array of `jwk.Key`.
+ Instead, only the first key matching the given key ID will be returned.
+ If you need to work with multiple keys, use `(jwk.Set).Iterate()` or
+ `(jwk.Set).Get()` to look for matching keys.
+
+ * `jwk.PublicKeyOf()` has been renamed to `jwk.PublicRawKeyOf()`,
+ which converts raw keys (e.g. `rsa.PrivateKey`) to their public
+ counter part (e.g. `rsa.PublicKey`)
+
+ `jwk.PublicKeyOf()` is now used to get the public counter part of
+ `jwk.Key` objects (e.g. `jwk.RSAPrivateKey` to `jwk.RSAPublicKey`)
+
+ `jwk.PublicSetOf()` has been added to get a new `jwk.Set` but with
+ all keys transformed to public keys via `jwk.PublicKeyOf()`
+
+ * `jwk.FetchXXXX` functions have been removed. `jwk.Fetch()` remains, but
+ it now takes `context.Context`, and doesn't support retrieving files
+ from the local file system. See `ReadFile()` for that.
+
+ * `jws.VerifyWithJKU()`, `jws.VerifyWithJWK()`, `jwk.VerifyWithJWKSet()`
+ have all been removed, but `jwk.VerifySet(jwk.Set)` has been added.
+
+ * `jws.SplitCompact(io.Reader)` has been changd to `jws.SplitCompact([]byte)`
+ Similar to `Parse()`, `SplitCompactReader(io.Reader)` and `SplitCompactString(string)`
+ have been added
+
+ * `jws.SignLiteral` has been removed.
+
+ * `jws.PayloadSigner` has been removed (but should not matter, because
+ this as internal-use only anyways)
+
+ * `jwe.WithPrettyJSONFormat` has been renamed to `jwe.WithPrettyFormat`
+
+ * `jwt.Verify` has been removed. Use `jwt.Parse()` aloing with the `jwt.WithVerify()`
+ option to perform signature verification. Validation of verified data
+ can be performed via `(jwt.Token).Validate()` method, which has been available
+ since v1.0.6
+
+ * Package `buffer` has been removed. This package should have been an internal
+ package to start with, but it was left because it had been incorporated
+ in the public API in our initial versions.
+
+ * `(jwk.Key).Get(jwk.X509CertChainKey)` no longer returns a `jwk.CertificateChain`.
+ Instead it returns a raw []*x509.Certificate.
+
+ * `(jwt.Token).Size() has been removed.
+
+ * `jwt.WithOpenIDClaims()` has been removed. Use `jwt.WithToken(openid.New())` instead.
+
+[New Features]
+ * `jwe.ReadFile(string)`, `jwk.ReadFile(string)`, `jws.ReadFile(string)`, and
+ `jwt.ReadFile(string)` have been added. In the future, we plan to introduce
+ a `WithFS` option so you can read from an arbitrary file system, but this cannot
+ be added while we keep go < 1.16 compatibility. If you want something like that,
+ you will need to put an adapter over the jwx for the time being.
+
+ * `(jwk.Key).PublicKey()` has been added. This method creates a corresponding
+ public key, with all fields (except those that shouldn't be) copied over.
+ This allows you to easily create a public key of a private key with the
+ same "kid" attribute.
+
+ * Both `jws.Verify` and `jws.Sign` methods can now handle `jwk.Key` objects, on
+ top of raw keys (e.g. rsa.PrivateKey). You no longer need to conver the
+ `jwk.Key` objects that you have in to raw keys before using these functions.
+
+ * `(jws.Header).Remove(string)`, `(jwk.Key).Remove(string)`, and
+ `(jwt.Token).Remove(string)` have been added. `jwe.Header` already had a `Remove()`
+ method, so it has not been changed.
+
+ * `(jwk.Key).Clone() has been added.
+
+[Miscellaneous]
+ * Default branch for the repository is now `main`.
+
+ * Options have been reworked. In most instances, option types should now reflect
+ better the contexts in which they can be used. For example, `jwk` now has
+ `AutoRefreshOption` and `FetchOption` instead of a single `Option`.
+
+ * JSON marshaling should be 10~30% faster by default (though they may take
+ more allocations to achieve this).
+
+ However, if performance is really bogging you down, you can try to enable
+ the optional module github.com/goccy/go-json by enabling the "jwx_goccy" tag
+
+ go build -tags jwx_goccy ...
+
+ In some cases you get an extra 40~50% performance improvement in serailization
+ https://github.com/lestrrat-go/jwx/pull/314#issue-560594020
+ https://github.com/lestrrat-go/jwx/pull/314#issuecomment-766343888
+
+ * Location for examples and benchmarks have changed: Now examples/ and bench/
+ are their respective locations, and they are each a standalone module,
+ so that in case we need extra imports (such as the case in examples)
+ they do not interfere with users who just want to include jwx in their projects.
+
+v1.0.8 15 Jan 2021
+[New features]
+ * Fixed `jws.Message` and `jws.Signature` to be properly formatted when
+ marshaled into JSON. In the same manner, `json.Unmarshal` should also
+ work as expected.
+ * Added API to programatically manipulate `jws.Message` and `jws.Signature`
+[Miscellaneous]
+ * The order of keys are now consistent as when used with `json.Marshal`.
+ Previously some objects used their own ordering, but now the code goes
+ through one extra roundtrip of `json.Unmarshal`/`json.Marshal` to preserve
+ compatible behavior. This *may* lead to slightly slower performance if
+ you are performing `json.Marshal` over and over in very quick succession.
+ Please file an issue if you have real world cases where the change
+ causes problems for you.
+ * Added more examples in various places.
+ * Tests runs have been sped up for the most oft used cases
+
+v1.0.7 11 Jan 2021
+[New features]
+ * Added jwk.AutoRefresh, which is a tool to periodically refresh JWKS. (#265)
+ * Added experimental ed25519 support (#252)
+[Bug fixes]
+ * Fix `Set()` method for jwk Keys to properly accept either `jwk.KeyUsageType`
+ or a simple string.
+[Miscellaneous]
+ * Updated dependencies
+ * Changed options to use github.com/lestrrat-go/option
+ * Various typos, unused annotations, etc, have been fixed by contributors
+ * Nobody except for the author really should care, but the underlying
+ `pdebug` utility, which is used for print debugging, has been
+ upgraded to v3, which should stop parallel test execution from throwing
+ an error when run with -race
+
+v1.0.6 17 Dec 2020
+ * Fix ECDHES ciphers where padding in AAD et al was creating
+ incomptabile values with jose tool
+ * Also fix ECDH-ES cek handling (#248)
+ * Implement direct key encoding (#213, #249)
+ * Allow JWT tokens to use default JWK if only one key is given
+ and the JWT does not necessarily specifies a key (#214)
+ * Deprecate jwt.Verify and introduce jwt.Validate. JWS verification
+ used the term Verify, which was confusing when users wanted to
+ validate the JWT token itself. (#220)
+ * JWT library optins have been explicitly typed as ValidationOption
+ and ParseOption (#220, #223)
+ * Add jwx.DecoderSettings and jwx.WithUseNumber option to globally
+ change how jwx parses JSON objects (#222)
+ * Encode x5c field as base64 with padding (#244)
+ * Add more interoperability tests against jose tool.
+ * Special thanks to anatol and imirkin!
+
+v1.0.5 - 28 Sep 2020
+ * Reinstate PrivateParams() method in jws and jwe packages.
+ These used to be available until v1.0.0, but somehow got lost during the
+ big change.
+ As a workaround for users of versions 1.0.0 to 1.0.4, you could have
+ achieved the same thing using AsMap() methods, albeit with a slight
+ performance penality (#205, #206)
+
+v1.0.4 - 15 Aug 2020
+ * Fix jwt.WithOpenIDClaims(). Looks like something got lost along
+ the way, and it never really worked. (#201 #202)
+
+v1.0.3 - 08 Jul 2020
+ * `jws.Sign`, and therefore `jwt.Sign` now accept `jwk.Key` as the
+ key to use for signature. (#199)
+ * `jwt.Sign` could sometimes return a nil error when setting bad
+ values to the protected header failed (#195)
+ * More golangci-lint cleanup (#193)
+
+v1.0.2 - 07 May 2020
+ * Since 1.0.0, we took some time to play the test coverage game.
+ The coverage is around 30% better, and we _did_ uncover some
+ inconsistencies in the API, which got promptly fixed.
+ But I'm tired of the coverage game for the time being. PR's welcome!
+ * Add jwk.AssignKeyID to automatically assign a `kid` field to a JWK
+ * Fix jwe.Encrypt / jwe.Decrypt to properly look at the `zip` field
+ * Change jwe.Message accessors to return []byte, not buffer.Buffer
+
+v1.0.1 - 04 May 2020
+ * Normalize all JWK serialization to use padding-less base64 encoding (#185)
+ * Fix edge case unmarshaling openid.AddressClaim within a openid.Token
+ * Fix edge case unmarshaling jwe.Message
+ * Export JWK key-specific constants, such as jwk.RSANKey, jwk.SymmetricOctetsKey, etc
+ * Remove some unused code
+
+v1.0.0 - 03 May 2020
+ * All packages (`jws`, `jwe`, `jwk`, `jwt`) have all been reworked from
+ the ground-up.
+ * These packages now hide the actual implementation of the main structs behind an interface.
+ * Header/Token structs must now be instantiated using proper constructors
+ (most notably, json.Unmarshal will miserably fail if you just pass
+ and empty interface via `xxx.Token` or similar)
+ * Token/Header interfaces are now more or less standardized.
+ The following API should be consistent between all relevant packages:
+ * New()
+ * Get()
+ * Set()
+ * Remove()
+ * Iterate()
+ * Walk()
+ * AsMap()
+ * Oft-used fields are no longer directly accessible:
+ e.g. `token.KeyID = v` is no longer valid. You must set using `Set`
+ (and `Remove`, if you are removing it), and use either `Get` or
+ one of the utility methods such as `token.KeyID()`
+ * Many helper functions and structs have been unexported. They were never
+ meant to be anything useful for end-users, and hopefully it does not
+ cause any problems.
+ * Most errors type/instances have been removed from the public API
+ * `jwt` package can now work with different token types, such as OpenID tokens.
+ * `token.Sign` and `token.Verify` have been changed from methods to
+ package functions `jwt.Sign` and `jwt.Verify`, to allow different
+ types of tokens to be passed to the same logic.
+ * Added a custom token type in `openid` sub-package to make it easier to
+ work with OpenID claims
+ * `jwt.Parse` (and its siblings) now accept `jwt.WithOpenIDClaims()`
+ * `jwe` API has been reworked:
+ * `MultiEncrypt` has been removed.
+ * Serializer structs have been removed. Now you just need to call
+ `jwe.Compact` or `jwe.JSON`
+ * `jwk` API has been reworked:
+ * `jwk.ParseKey` has been added
+ * `jwk.Materialize` has been renamed to `Raw()`. A new corresponding
+ method to initialize the key from a raw key (RSA/ECDSA/byte keys)
+ called `FromRaw()` has also been added, which makes a nice pair.
+ * `jws` API has been reworked
+ * CI has been changed from Travis CI to Github Actions, and tests now
+ include linting via `golangci-lint`
+
+v0.9.2 - 15 Apr 2020
+ * Maintenance release to protect users from upcoming breaking changes
+
+v0.9.1 - 27 Feb 2020
+ * Fix error wrapping in certain cases
+ * Add Claims(), Walk(), and AsMap() to iterate claims, as well as
+ getting the entire data out as a single map
+ * Work with alternate base64 encodings when decoding
+
+v0.9.0 - 22 May 2019
+ * Start tagging versions for good measure.
diff --git a/vendor/github.com/lestrrat-go/jwx/Makefile b/vendor/github.com/lestrrat-go/jwx/Makefile
new file mode 100644
index 000000000..2bbfac31b
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/Makefile
@@ -0,0 +1,86 @@
+.PHONY: generate realclean cover viewcover test lint check_diffs imports tidy
+generate:
+ @go generate
+ @$(MAKE) generate-jwa generate-jwe generate-jwk generate-jws generate-jwt
+
+generate-%:
+ @echo "> Generating for $(patsubst generate-%,%,$@)"
+ @go generate $(shell pwd -P)/$(patsubst generate-%,%,$@)
+
+realclean:
+ rm coverage.out
+
+test-cmd:
+ go test -v -race $(TESTOPTS)
+
+test:
+ $(MAKE) TESTOPTS=./... test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C examples test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C bench/performance test-cmd
+
+cover-cmd:
+ $(MAKE) test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C examples TESTOPTS= test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C bench/performance TESTOPTS= test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C cmd/jwx TESTOPTS= test-cmd
+ @# This is NOT cheating. tools to generate code, and tools to
+ @# run tests don't need to be included in the final result.
+ @cat coverage.out.tmp | grep -v "internal/jose" | grep -v "internal/jwxtest" | grep -v "internal/cmd" > coverage.out
+ @rm coverage.out.tmp
+
+cover:
+ $(MAKE) cover-stdlib
+
+cover-stdlib:
+ $(MAKE) cover-cmd TESTOPTS="-coverpkg=./... -coverprofile=coverage.out.tmp ./..."
+
+cover-goccy:
+ $(MAKE) cover-cmd TESTOPTS="-tags jwx_goccy -coverpkg=./... -coverprofile=coverage.out.tmp ./..."
+
+cover-es256k:
+ $(MAKE) cover-cmd TESTOPTS="-tags jwx_es256k -coverpkg=./... -coverprofile=coverage.out.tmp ./..."
+
+cover-all:
+ $(MAKE) cover-cmd TESTOPTS="-tags jwx_goccy,jwx_es256k -coverpkg=./... -coverprofile=coverage.out.tmp ./..."
+
+smoke-cmd:
+ $(MAKE) test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C examples test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C bench/performance test-cmd
+ $(MAKE) -f $(PWD)/Makefile -C cmd/jwx test-cmd
+
+smoke:
+ $(MAKE) smoke-stdlib
+
+smoke-stdlib:
+ $(MAKE) smoke-cmd TESTOPTS="-short ./..."
+
+smoke-goccy:
+ $(MAKE) smoke-cmd TESTOPTS="-short -tags jwx_goccy ./..."
+
+smoke-es256k:
+ $(MAKE) smoke-cmd TESTOPTS="-short -tags jwx_es256k ./..."
+
+smoke-all:
+ $(MAKE) smoke-cmd TESTOPTS="-short -tags jwx_goccy,jwx_es256k ./..."
+
+viewcover:
+ go tool cover -html=coverage.out
+
+lint:
+ golangci-lint run ./...
+
+check_diffs:
+ ./scripts/check-diff.sh
+
+imports:
+ goimports -w ./
+
+tidy:
+ $(MAKE) tidy-cmd
+ $(MAKE) -f $(PWD)/Makefile -C examples tidy-cmd
+ $(MAKE) -f $(PWD)/Makefile -C bench/performance tidy-cmd
+ $(MAKE) -f $(PWD)/Makefile -C cmd/jwx tidy-cmd
+
+tidy-cmd:
+ go mod tidy
diff --git a/vendor/github.com/lestrrat-go/jwx/README.md b/vendor/github.com/lestrrat-go/jwx/README.md
new file mode 100644
index 000000000..bb9c453ca
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/README.md
@@ -0,0 +1,125 @@
+# github.com/lestrrat-go/jwx  [](https://pkg.go.dev/github.com/lestrrat-go/jwx) [](http://codecov.io/github/lestrrat-go/jwx?branch=main)
+
+Various libraries implementing various JWx technologies. Please click on the package names in the table below to find the synopsis/description for each package.
+
+| Package name | Notes |
+|-----------------------------------------------------------|-------------------------------------------------|
+| [jwt](https://github.com/lestrrat-go/jwx/tree/main/jwt) | [RFC 7519](https://tools.ietf.org/html/rfc7519) |
+| [jwk](https://github.com/lestrrat-go/jwx/tree/main/jwk) | [RFC 7517](https://tools.ietf.org/html/rfc7517) + [RFC 7638](https://tools.ietf.org/html/rfc7638) |
+| [jwa](https://github.com/lestrrat-go/jwx/tree/main/jwa) | [RFC 7518](https://tools.ietf.org/html/rfc7518) |
+| [jws](https://github.com/lestrrat-go/jwx/tree/main/jws) | [RFC 7515](https://tools.ietf.org/html/rfc7515) + [RFC 7797](https://tools.ietf.org/html/rfc7797) |
+| [jwe](https://github.com/lestrrat-go/jwx/tree/main/jwe) | [RFC 7516](https://tools.ietf.org/html/rfc7516) |
+
+# How to Use
+
+* [API documentation](https://pkg.go.dev/github.com/lestrrat-go/jwx)
+* [How-to style documentation](./docs)
+* [Runnable Examples](./examples)
+
+# Description
+
+## History
+
+My goal was to write a server that heavily uses JWK and JWT. At first glance
+the libraries that already exist seemed sufficient, but soon I realized that
+
+1. To completely implement the protocols, I needed the entire JWT, JWK, JWS, JWE (and JWA, by necessity).
+2. Most of the libraries that existed only deal with a subset of the various JWx specifications that were necessary to implement their specific needs
+
+For example, a certain library looks like it had most of JWS, JWE, JWK covered, but then it lacked the ability to include private claims in its JWT responses. Another library had support of all the private claims, but completely lacked in its flexibility to generate various different response formats.
+
+Because I was writing the server side (and the client side for testing), I needed the *entire* JOSE toolset to properly implement my server, **and** they needed to be *flexible* enough to fulfill the entire spec that I was writing.
+
+So here's `github.com/lestrrat-go/jwx`. This library is extensible, customizable, and hopefully well organized to the point that it is easy for you to slice and dice it.
+
+## Why would I use this library?
+
+There are several other major Go modules that handle JWT and related data formats,
+so why should you use this library?
+
+From a purely functional perspective, the only major difference is this:
+Whereas most other projects only deal with what they seem necessary to handle
+JWTs, this module handles the entire spectrum of JWS, JWE, JWK, and JWT.
+
+That is, if you need to not only parse JWTs, but also to control JWKs, or
+if you need to handle payloads that are NOT JWTs, you should probably consider
+using this module.
+
+Next, from an implementation perspective, this module differs significantly
+from others in that it tries very hard to expose only the APIs, and not the
+internal data. For example, individual JWT claims are not accessible through
+struct field lookups. You need to use one of the getter methods.
+
+This is because this library takes the stance that the end user is fully capable
+and even willing to shoot themselves on the foot when presented with a lax
+API. By making sure that users do not have access to open structs, we can protect
+users from doing silly things like creating _incomplete_ structs, or access the
+structs concurrently without any protection. This structure also allows
+us to put extra smarts in the structs, such as doing the right thing when
+you want to parse / write custom fields (this module does not require the user
+to specify alternate structs to parse objects with custom fields)
+
+In the end I think it comes down to your usage pattern, and priorities.
+Some general guidelines that come to mind are:
+
+* If you want a single library to handle everything JWx, such as using JWE, JWK, JWS, handling [auto-refreshing JWKs](https://github.com/lestrrat-go/jwx/blob/main/docs/04-jwk.md#auto-refreshing-remote-keys), use this module.
+* If you want to honor all possible custom fields transparently, use this module.
+* If you want a standardized clean API, use this module.
+
+Otherwise, feel free to choose something else.
+
+# Command Line Tool
+
+Since v1.1.1 we have a command line tool `jwx` (*). With `jwx` you can create JWKs (from PEM files, even), sign and verify JWS message, encrypt and decrypt JWE messages, etc.
+
+(*) Okay, it existed since a long time ago, but it was never useful.
+
+## Installation
+
+```
+go install github.com/lestrrat-go/jwx/cmd/jwx
+```
+
+# Caveats
+
+## Backwards Compatibility Notice
+
+### Users of github.com/lestrrat/go-jwx
+
+Uh, why are you using such an ancient version? You know that repository is archived for a reason, yeah? Please use the new version.
+
+### Pre-1.0.0 users
+
+The API has been reworked quite substantially between pre- and post 1.0.0 releases. Please check out the [Changes](./Changes) file (or the [diff](https://github.com/lestrrat-go/jwx/compare/v0.9.2...v1.0.0), if you are into that sort of thing)
+
+### v1.0.x users
+
+The API has gone under some changes for v1.1.0. If you are upgrading, you might want to read the relevant parts in the [Changes](./Changes) file.
+
+# Contributions
+
+## Issues
+
+For bug reports and feature requests, please try to follow the issue templates as much as possible.
+For either bug reports or feature requests, failing tests are even better.
+
+## Pull Requests
+
+Please make sure to include tests that excercise the changes you made.
+
+If you are editing auto-generated files (those files with the `_gen.go` prefix, please make sure that you do the following:
+
+1. Edit the generator, not the generated files (e.g. internal/cmd/genreadfile/main.go)
+2. Run `make generate` (or `go generate`) to generate the new code
+3. Commit _both_ the generator _and_ the generated files
+
+## Discussions / Usage
+
+Please try [discussions](https://github.com/lestrrat-go/jwx/discussions) first.
+
+# Credits
+
+* Work on this library was generously sponsored by HDE Inc (https://www.hde.co.jp)
+* Lots of code, especially JWE was taken from go-jose library (https://github.com/square/go-jose)
+* Lots of individual contributors have helped this project over the years. Thank each and everyone of you very much.
+
diff --git a/vendor/github.com/lestrrat-go/jwx/format.go b/vendor/github.com/lestrrat-go/jwx/format.go
new file mode 100644
index 000000000..9662cea32
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/format.go
@@ -0,0 +1,95 @@
+package jwx
+
+import (
+ "bytes"
+ "encoding/json"
+)
+
+type FormatKind int
+
+const (
+ UnknownFormat FormatKind = iota
+ JWE
+ JWS
+ JWK
+ JWKS
+ JWT
+)
+
+type formatHint struct {
+ Payload json.RawMessage `json:"payload"` // Only in JWS
+ Signatures json.RawMessage `json:"signatures"` // Only in JWS
+ Ciphertext json.RawMessage `json:"ciphertext"` // Only in JWE
+ KeyType json.RawMessage `json:"kty"` // Only in JWK
+ Keys json.RawMessage `json:"keys"` // Only in JWKS
+ Audience json.RawMessage `json:"aud"` // Only in JWT
+}
+
+// GuessFormat is used to guess the format the given payload is in
+// using heuristics. See the type FormatKind for a full list of
+// possible types.
+//
+// This may be useful in determining your next action when you may
+// encounter a payload that could either be a JWE, JWS, or a plain JWT.
+//
+// Because JWTs are almost always JWS signed, you may be thrown off
+// if you pass what you think is a JWT payload to this function.
+// If the function is in the "Compact" format, it means it's a JWS
+// signed message, and its payload is the JWT. Therefore this function
+// will reuturn JWS, not JWT.
+//
+// This function requires an extra parsing of the payload, and therefore
+// may be inefficient if you call it every time before parsing.
+func GuessFormat(payload []byte) FormatKind {
+ // The check against kty, keys, and aud are something this library
+ // made up. for the distinctions between JWE and JWS, we used
+ // https://datatracker.ietf.org/doc/html/rfc7516#section-9.
+ //
+ // The above RFC described several ways to distinguish between
+ // a JWE and JWS JSON, but we're only using one of them
+
+ payload = bytes.TrimSpace(payload)
+ if len(payload) <= 0 {
+ return UnknownFormat
+ }
+
+ if payload[0] != '{' {
+ // Compact format. It's probably a JWS or JWE
+ sep := []byte{'.'} // I want to const this :/
+
+ // Note: this counts the number of occurrences of the
+ // separator, but the RFC talks about the number of segments.
+ // number of '.' == segments - 1, so that's why we have 2 and 4 here
+ switch count := bytes.Count(payload, sep); count {
+ case 2:
+ return JWS
+ case 4:
+ return JWE
+ default:
+ return UnknownFormat
+ }
+ }
+
+ // If we got here, we probably have JSON.
+ var h formatHint
+ if err := json.Unmarshal(payload, &h); err != nil {
+ return UnknownFormat
+ }
+
+ if h.Audience != nil {
+ return JWT
+ }
+ if h.KeyType != nil {
+ return JWK
+ }
+ if h.Keys != nil {
+ return JWKS
+ }
+ if h.Ciphertext != nil {
+ return JWE
+ }
+ if h.Signatures != nil && h.Payload != nil {
+ return JWS
+ }
+ return UnknownFormat
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go b/vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go
new file mode 100644
index 000000000..c2488655f
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/formatkind_string_gen.go
@@ -0,0 +1,28 @@
+// Code generated by "stringer -type=FormatKind"; DO NOT EDIT.
+
+package jwx
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[UnknownFormat-0]
+ _ = x[JWE-1]
+ _ = x[JWS-2]
+ _ = x[JWK-3]
+ _ = x[JWKS-4]
+ _ = x[JWT-5]
+}
+
+const _FormatKind_name = "UnknownFormatJWEJWSJWKJWKSJWT"
+
+var _FormatKind_index = [...]uint8{0, 13, 16, 19, 22, 26, 29}
+
+func (i FormatKind) String() string {
+ if i < 0 || i >= FormatKind(len(_FormatKind_index)-1) {
+ return "FormatKind(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _FormatKind_name[_FormatKind_index[i]:_FormatKind_index[i+1]]
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/gen.sh b/vendor/github.com/lestrrat-go/jwx/gen.sh
new file mode 100644
index 000000000..74c4710c4
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/gen.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Script to perform code generation. This exists to overcome
+# the fact that go:generate doesn't really allow you to change directories
+
+set -e
+
+pushd internal/cmd/genreadfile
+go build -o genreadfile main.go
+popd
+
+./internal/cmd/genreadfile/genreadfile
+
+rm internal/cmd/genreadfile/genreadfile
diff --git a/vendor/github.com/lestrrat-go/jwx/go.mod b/vendor/github.com/lestrrat-go/jwx/go.mod
new file mode 100644
index 000000000..0ca3912dc
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/go.mod
@@ -0,0 +1,16 @@
+module github.com/lestrrat-go/jwx
+
+go 1.15
+
+require (
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d
+ github.com/goccy/go-json v0.8.1
+ github.com/lestrrat-go/backoff/v2 v2.0.8
+ github.com/lestrrat-go/blackmagic v1.0.0
+ github.com/lestrrat-go/httpcc v1.0.0
+ github.com/lestrrat-go/iter v1.0.1
+ github.com/lestrrat-go/option v1.0.0
+ github.com/pkg/errors v0.9.1
+ github.com/stretchr/testify v1.7.0
+ golang.org/x/crypto v0.0.0-20201217014255-9d1352758620
+)
diff --git a/vendor/github.com/lestrrat-go/jwx/go.sum b/vendor/github.com/lestrrat-go/jwx/go.sum
new file mode 100644
index 000000000..989ade874
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/go.sum
@@ -0,0 +1,39 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
+github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
+github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
+github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
+github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
+github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
+github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
+github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
+github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
+github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
+github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
+github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
+golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go b/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go
new file mode 100644
index 000000000..50ff3c83b
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/internal/keyconv/keyconv.go
@@ -0,0 +1,177 @@
+package keyconv
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rsa"
+
+ "github.com/lestrrat-go/blackmagic"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/pkg/errors"
+ "golang.org/x/crypto/ed25519"
+)
+
+// RSAPrivateKey assigns src to dst.
+// `dst` should be a pointer to a rsa.PrivateKey.
+// `src` may be rsa.PrivateKey, *rsa.PrivateKey, or a jwk.Key
+func RSAPrivateKey(dst, src interface{}) error {
+ if jwkKey, ok := src.(jwk.Key); ok {
+ var raw rsa.PrivateKey
+ if err := jwkKey.Raw(&raw); err != nil {
+ return errors.Wrapf(err, `failed to produce rsa.PrvateKey from %T`, src)
+ }
+ src = &raw
+ }
+
+ var ptr *rsa.PrivateKey
+ switch src := src.(type) {
+ case rsa.PrivateKey:
+ ptr = &src
+ case *rsa.PrivateKey:
+ ptr = src
+ default:
+ return errors.Errorf(`expected rsa.PrivateKey or *rsa.PrivateKey, got %T`, src)
+ }
+
+ return blackmagic.AssignIfCompatible(dst, ptr)
+}
+
+// RSAPublicKey assigns src to dst
+// `dst` should be a pointer to a non-zero rsa.PublicKey.
+// `src` may be rsa.PublicKey, *rsa.PublicKey, or a jwk.Key
+func RSAPublicKey(dst, src interface{}) error {
+ if jwkKey, ok := src.(jwk.Key); ok {
+ var raw rsa.PublicKey
+ if err := jwkKey.Raw(&raw); err != nil {
+ return errors.Wrapf(err, `failed to produce rsa.PublicKey from %T`, src)
+ }
+ src = &raw
+ }
+
+ var ptr *rsa.PublicKey
+ switch src := src.(type) {
+ case rsa.PublicKey:
+ ptr = &src
+ case *rsa.PublicKey:
+ ptr = src
+ default:
+ return errors.Errorf(`expected rsa.PublicKey or *rsa.PublicKey, got %T`, src)
+ }
+
+ return blackmagic.AssignIfCompatible(dst, ptr)
+}
+
+// ECDSAPrivateKey assigns src to dst, converting its type from a
+// non-pointer to a pointer
+func ECDSAPrivateKey(dst, src interface{}) error {
+ if jwkKey, ok := src.(jwk.Key); ok {
+ var raw ecdsa.PrivateKey
+ if err := jwkKey.Raw(&raw); err != nil {
+ return errors.Wrapf(err, `failed to produce ecdsa.PrvateKey from %T`, src)
+ }
+ src = &raw
+ }
+
+ var ptr *ecdsa.PrivateKey
+ switch src := src.(type) {
+ case ecdsa.PrivateKey:
+ ptr = &src
+ case *ecdsa.PrivateKey:
+ ptr = src
+ default:
+ return errors.Errorf(`expected ecdsa.PrivateKey or *ecdsa.PrivateKey, got %T`, src)
+ }
+ return blackmagic.AssignIfCompatible(dst, ptr)
+}
+
+// ECDSAPublicKey assigns src to dst, converting its type from a
+// non-pointer to a pointer
+func ECDSAPublicKey(dst, src interface{}) error {
+ if jwkKey, ok := src.(jwk.Key); ok {
+ var raw ecdsa.PublicKey
+ if err := jwkKey.Raw(&raw); err != nil {
+ return errors.Wrapf(err, `failed to produce ecdsa.PublicKey from %T`, src)
+ }
+ src = &raw
+ }
+
+ var ptr *ecdsa.PublicKey
+ switch src := src.(type) {
+ case ecdsa.PublicKey:
+ ptr = &src
+ case *ecdsa.PublicKey:
+ ptr = src
+ default:
+ return errors.Errorf(`expected ecdsa.PublicKey or *ecdsa.PublicKey, got %T`, src)
+ }
+ return blackmagic.AssignIfCompatible(dst, ptr)
+}
+
+func ByteSliceKey(dst, src interface{}) error {
+ if jwkKey, ok := src.(jwk.Key); ok {
+ var raw []byte
+ if err := jwkKey.Raw(&raw); err != nil {
+ return errors.Wrapf(err, `failed to produce []byte from %T`, src)
+ }
+ src = raw
+ }
+
+ if _, ok := src.([]byte); !ok {
+ return errors.Errorf(`expected []byte, got %T`, src)
+ }
+ return blackmagic.AssignIfCompatible(dst, src)
+}
+
+func Ed25519PrivateKey(dst, src interface{}) error {
+ if jwkKey, ok := src.(jwk.Key); ok {
+ var raw ed25519.PrivateKey
+ if err := jwkKey.Raw(&raw); err != nil {
+ return errors.Wrapf(err, `failed to produce ed25519.PrivateKey from %T`, src)
+ }
+ src = &raw
+ }
+
+ var ptr *ed25519.PrivateKey
+ switch src := src.(type) {
+ case ed25519.PrivateKey:
+ ptr = &src
+ case *ed25519.PrivateKey:
+ ptr = src
+ default:
+ return errors.Errorf(`expected ed25519.PrivateKey or *ed25519.PrivateKey, got %T`, src)
+ }
+ return blackmagic.AssignIfCompatible(dst, ptr)
+}
+
+func Ed25519PublicKey(dst, src interface{}) error {
+ if jwkKey, ok := src.(jwk.Key); ok {
+ var raw ed25519.PublicKey
+ if err := jwkKey.Raw(&raw); err != nil {
+ return errors.Wrapf(err, `failed to produce ed25519.PublicKey from %T`, src)
+ }
+ src = &raw
+ }
+
+ var ptr *ed25519.PublicKey
+ switch src := src.(type) {
+ case ed25519.PublicKey:
+ ptr = &src
+ case *ed25519.PublicKey:
+ ptr = src
+ case *crypto.PublicKey:
+ tmp, ok := (*src).(ed25519.PublicKey)
+ if !ok {
+ return errors.New(`failed to retrieve ed25519.PublicKey out of *crypto.PublicKey`)
+ }
+ ptr = &tmp
+ case crypto.PublicKey:
+ tmp, ok := src.(ed25519.PublicKey)
+ if !ok {
+ return errors.New(`failed to retrieve ed25519.PublicKey out of crypto.PublicKey`)
+ }
+ ptr = &tmp
+ default:
+ return errors.Errorf(`expected ed25519.PublicKey or *ed25519.PublicKey, got %T`, src)
+ }
+ return blackmagic.AssignIfCompatible(dst, ptr)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/README.md b/vendor/github.com/lestrrat-go/jwx/jwe/README.md
new file mode 100644
index 000000000..216c53362
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/README.md
@@ -0,0 +1,94 @@
+# JWE [](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwe)
+
+Package jwe implements JWE as described in [RFC7516](https://tools.ietf.org/html/rfc7516)
+
+* Encrypt and Decrypt arbitrary data
+* Content compression and decompression
+* Add arbitrary fields in the JWE header object
+
+How-to style documentation can be found in the [docs directory](../docs).
+
+Examples are located in the examples directory ([jwe_example_test.go](../examples/jwe_example_test.go))
+
+Supported key encryption algorithm:
+
+| Algorithm | Supported? | Constant in [jwa](../jwa) |
+|:-----------------------------------------|:-----------|:-------------------------|
+| RSA-PKCS1v1.5 | YES | jwa.RSA1_5 |
+| RSA-OAEP-SHA1 | YES | jwa.RSA_OAEP |
+| RSA-OAEP-SHA256 | YES | jwa.RSA_OAEP_256 |
+| AES key wrap (128) | YES | jwa.A128KW |
+| AES key wrap (192) | YES | jwa.A192KW |
+| AES key wrap (256) | YES | jwa.A256KW |
+| Direct encryption | YES (1) | jwa.DIRECT |
+| ECDH-ES | YES (1) | jwa.ECDH_ES |
+| ECDH-ES + AES key wrap (128) | YES | jwa.ECDH_ES_A128KW |
+| ECDH-ES + AES key wrap (192) | YES | jwa.ECDH_ES_A192KW |
+| ECDH-ES + AES key wrap (256) | YES | jwa.ECDH_ES_A256KW |
+| AES-GCM key wrap (128) | YES | jwa.A128GCMKW |
+| AES-GCM key wrap (192) | YES | jwa.A192GCMKW |
+| AES-GCM key wrap (256) | YES | jwa.A256GCMKW |
+| PBES2 + HMAC-SHA256 + AES key wrap (128) | YES | jwa.PBES2_HS256_A128KW |
+| PBES2 + HMAC-SHA384 + AES key wrap (192) | YES | jwa.PBES2_HS384_A192KW |
+| PBES2 + HMAC-SHA512 + AES key wrap (256) | YES | jwa.PBES2_HS512_A256KW |
+
+* Note 1: Single-recipient only
+
+Supported content encryption algorithm:
+
+| Algorithm | Supported? | Constant in [jwa](../jwa) |
+|:----------------------------|:-----------|:--------------------------|
+| AES-CBC + HMAC-SHA256 (128) | YES | jwa.A128CBC_HS256 |
+| AES-CBC + HMAC-SHA384 (192) | YES | jwa.A192CBC_HS384 |
+| AES-CBC + HMAC-SHA512 (256) | YES | jwa.A256CBC_HS512 |
+| AES-GCM (128) | YES | jwa.A128GCM |
+| AES-GCM (192) | YES | jwa.A192GCM |
+| AES-GCM (256) | YES | jwa.A256GCM |
+
+# SYNOPSIS
+
+## Encrypt data
+
+```go
+func ExampleEncrypt() {
+ privkey, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ log.Printf("failed to generate private key: %s", err)
+ return
+ }
+
+ payload := []byte("Lorem Ipsum")
+
+ encrypted, err := jwe.Encrypt(payload, jwa.RSA1_5, &privkey.PublicKey, jwa.A128CBC_HS256, jwa.NoCompress)
+ if err != nil {
+ log.Printf("failed to encrypt payload: %s", err)
+ return
+ }
+ _ = encrypted
+ // OUTPUT:
+}
+```
+
+## Decrypt data
+
+```go
+func ExampleDecrypt() {
+ privkey, encrypted, err := exampleGenPayload()
+ if err != nil {
+ log.Printf("failed to generate encrypted payload: %s", err)
+ return
+ }
+
+ decrypted, err := jwe.Decrypt(encrypted, jwa.RSA1_5, privkey)
+ if err != nil {
+ log.Printf("failed to decrypt: %s", err)
+ return
+ }
+
+ if string(decrypted) != "Lorem Ipsum" {
+ log.Printf("WHAT?!")
+ return
+ }
+ // OUTPUT:
+}
+```
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/compress.go b/vendor/github.com/lestrrat-go/jwx/jwe/compress.go
new file mode 100644
index 000000000..e3836a693
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/compress.go
@@ -0,0 +1,41 @@
+package jwe
+
+import (
+ "bytes"
+ "compress/flate"
+ "io/ioutil"
+
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+func uncompress(plaintext []byte) ([]byte, error) {
+ return ioutil.ReadAll(flate.NewReader(bytes.NewReader(plaintext)))
+}
+
+func compress(plaintext []byte, alg jwa.CompressionAlgorithm) ([]byte, error) {
+ if alg == jwa.NoCompress {
+ return plaintext, nil
+ }
+
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+
+ w, _ := flate.NewWriter(buf, 1)
+ in := plaintext
+ for len(in) > 0 {
+ n, err := w.Write(in)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to write to compression writer`)
+ }
+ in = in[n:]
+ }
+ if err := w.Close(); err != nil {
+ return nil, errors.Wrap(err, "failed to close compression writer")
+ }
+
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go b/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go
new file mode 100644
index 000000000..4fc2ef30d
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/decrypt.go
@@ -0,0 +1,300 @@
+package jwe
+
+import (
+ "crypto/aes"
+ cryptocipher "crypto/cipher"
+ "crypto/ecdsa"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/sha512"
+ "hash"
+
+ "golang.org/x/crypto/pbkdf2"
+
+ "github.com/lestrrat-go/jwx/internal/keyconv"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/cipher"
+ "github.com/lestrrat-go/jwx/jwe/internal/content_crypt"
+ "github.com/lestrrat-go/jwx/jwe/internal/keyenc"
+ "github.com/lestrrat-go/jwx/x25519"
+ "github.com/pkg/errors"
+)
+
+// Decrypter is responsible for taking various components to decrypt a message.
+// its operation is not concurrency safe. You must provide locking yourself
+//nolint:govet
+type Decrypter struct {
+ aad []byte
+ apu []byte
+ apv []byte
+ computedAad []byte
+ iv []byte
+ keyiv []byte
+ keysalt []byte
+ keytag []byte
+ tag []byte
+ privkey interface{}
+ pubkey interface{}
+ ctalg jwa.ContentEncryptionAlgorithm
+ keyalg jwa.KeyEncryptionAlgorithm
+ cipher content_crypt.Cipher
+ keycount int
+}
+
+// NewDecrypter Creates a new Decrypter instance. You must supply the
+// rest of parameters via their respective setter methods before
+// calling Decrypt().
+//
+// privkey must be a private key in its "raw" format (i.e. something like
+// *rsa.PrivateKey, instead of jwk.Key)
+//
+// You should consider this object immutable once you assign values to it.
+func NewDecrypter(keyalg jwa.KeyEncryptionAlgorithm, ctalg jwa.ContentEncryptionAlgorithm, privkey interface{}) *Decrypter {
+ return &Decrypter{
+ ctalg: ctalg,
+ keyalg: keyalg,
+ privkey: privkey,
+ }
+}
+
+func (d *Decrypter) AgreementPartyUInfo(apu []byte) *Decrypter {
+ d.apu = apu
+ return d
+}
+
+func (d *Decrypter) AgreementPartyVInfo(apv []byte) *Decrypter {
+ d.apv = apv
+ return d
+}
+
+func (d *Decrypter) AuthenticatedData(aad []byte) *Decrypter {
+ d.aad = aad
+ return d
+}
+
+func (d *Decrypter) ComputedAuthenticatedData(aad []byte) *Decrypter {
+ d.computedAad = aad
+ return d
+}
+
+func (d *Decrypter) ContentEncryptionAlgorithm(ctalg jwa.ContentEncryptionAlgorithm) *Decrypter {
+ d.ctalg = ctalg
+ return d
+}
+
+func (d *Decrypter) InitializationVector(iv []byte) *Decrypter {
+ d.iv = iv
+ return d
+}
+
+func (d *Decrypter) KeyCount(keycount int) *Decrypter {
+ d.keycount = keycount
+ return d
+}
+
+func (d *Decrypter) KeyInitializationVector(keyiv []byte) *Decrypter {
+ d.keyiv = keyiv
+ return d
+}
+
+func (d *Decrypter) KeySalt(keysalt []byte) *Decrypter {
+ d.keysalt = keysalt
+ return d
+}
+
+func (d *Decrypter) KeyTag(keytag []byte) *Decrypter {
+ d.keytag = keytag
+ return d
+}
+
+// PublicKey sets the public key to be used in decoding EC based encryptions.
+// The key must be in its "raw" format (i.e. *ecdsa.PublicKey, instead of jwk.Key)
+func (d *Decrypter) PublicKey(pubkey interface{}) *Decrypter {
+ d.pubkey = pubkey
+ return d
+}
+
+func (d *Decrypter) Tag(tag []byte) *Decrypter {
+ d.tag = tag
+ return d
+}
+
+func (d *Decrypter) ContentCipher() (content_crypt.Cipher, error) {
+ if d.cipher == nil {
+ switch d.ctalg {
+ case jwa.A128GCM, jwa.A192GCM, jwa.A256GCM, jwa.A128CBC_HS256, jwa.A192CBC_HS384, jwa.A256CBC_HS512:
+ cipher, err := cipher.NewAES(d.ctalg)
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to build content cipher for %s`, d.ctalg)
+ }
+ d.cipher = cipher
+ default:
+ return nil, errors.Errorf(`invalid content cipher algorithm (%s)`, d.ctalg)
+ }
+ }
+
+ return d.cipher, nil
+}
+
+func (d *Decrypter) Decrypt(recipientKey, ciphertext []byte) (plaintext []byte, err error) {
+ cek, keyerr := d.DecryptKey(recipientKey)
+ if keyerr != nil {
+ err = errors.Wrap(keyerr, `failed to decrypt key`)
+ return
+ }
+
+ cipher, ciphererr := d.ContentCipher()
+ if ciphererr != nil {
+ err = errors.Wrap(ciphererr, `failed to fetch content crypt cipher`)
+ return
+ }
+
+ computedAad := d.computedAad
+ if d.aad != nil {
+ computedAad = append(append(computedAad, '.'), d.aad...)
+ }
+
+ plaintext, err = cipher.Decrypt(cek, d.iv, ciphertext, d.tag, computedAad)
+ if err != nil {
+ err = errors.Wrap(err, `failed to decrypt payload`)
+ return
+ }
+
+ return plaintext, nil
+}
+
+func (d *Decrypter) decryptSymmetricKey(recipientKey, cek []byte) ([]byte, error) {
+ switch d.keyalg {
+ case jwa.DIRECT:
+ return cek, nil
+ case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
+ var hashFunc func() hash.Hash
+ var keylen int
+ switch d.keyalg {
+ case jwa.PBES2_HS256_A128KW:
+ hashFunc = sha256.New
+ keylen = 16
+ case jwa.PBES2_HS384_A192KW:
+ hashFunc = sha512.New384
+ keylen = 24
+ case jwa.PBES2_HS512_A256KW:
+ hashFunc = sha512.New
+ keylen = 32
+ }
+ salt := []byte(d.keyalg)
+ salt = append(salt, byte(0))
+ salt = append(salt, d.keysalt...)
+ cek = pbkdf2.Key(cek, salt, d.keycount, keylen, hashFunc)
+ fallthrough
+ case jwa.A128KW, jwa.A192KW, jwa.A256KW:
+ block, err := aes.NewCipher(cek)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to create new AES cipher`)
+ }
+
+ jek, err := keyenc.Unwrap(block, recipientKey)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to unwrap key`)
+ }
+
+ return jek, nil
+ case jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW:
+ if len(d.keyiv) != 12 {
+ return nil, errors.Errorf("GCM requires 96-bit iv, got %d", len(d.keyiv)*8)
+ }
+ if len(d.keytag) != 16 {
+ return nil, errors.Errorf("GCM requires 128-bit tag, got %d", len(d.keytag)*8)
+ }
+ block, err := aes.NewCipher(cek)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to create new AES cipher`)
+ }
+ aesgcm, err := cryptocipher.NewGCM(block)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to create new GCM wrap`)
+ }
+ ciphertext := recipientKey[:]
+ ciphertext = append(ciphertext, d.keytag...)
+ jek, err := aesgcm.Open(nil, d.keyiv, ciphertext, nil)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decode key`)
+ }
+ return jek, nil
+ default:
+ return nil, errors.Errorf("decrypt key: unsupported algorithm %s", d.keyalg)
+ }
+}
+
+func (d *Decrypter) DecryptKey(recipientKey []byte) (cek []byte, err error) {
+ if d.keyalg.IsSymmetric() {
+ var ok bool
+ cek, ok = d.privkey.([]byte)
+ if !ok {
+ return nil, errors.Errorf("decrypt key: []byte is required as the key to build %s key decrypter (got %T)", d.keyalg, d.privkey)
+ }
+
+ return d.decryptSymmetricKey(recipientKey, cek)
+ }
+
+ k, err := d.BuildKeyDecrypter()
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to build key decrypter`)
+ }
+
+ cek, err = k.Decrypt(recipientKey)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decrypt key`)
+ }
+
+ return cek, nil
+}
+
+func (d *Decrypter) BuildKeyDecrypter() (keyenc.Decrypter, error) {
+ cipher, err := d.ContentCipher()
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to fetch content crypt cipher`)
+ }
+
+ switch alg := d.keyalg; alg {
+ case jwa.RSA1_5:
+ var privkey rsa.PrivateKey
+ if err := keyconv.RSAPrivateKey(&privkey, d.privkey); err != nil {
+ return nil, errors.Wrapf(err, "*rsa.PrivateKey is required as the key to build %s key decrypter", alg)
+ }
+
+ return keyenc.NewRSAPKCS15Decrypt(alg, &privkey, cipher.KeySize()/2), nil
+ case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
+ var privkey rsa.PrivateKey
+ if err := keyconv.RSAPrivateKey(&privkey, d.privkey); err != nil {
+ return nil, errors.Wrapf(err, "*rsa.PrivateKey is required as the key to build %s key decrypter", alg)
+ }
+
+ return keyenc.NewRSAOAEPDecrypt(alg, &privkey)
+ case jwa.A128KW, jwa.A192KW, jwa.A256KW:
+ sharedkey, ok := d.privkey.([]byte)
+ if !ok {
+ return nil, errors.Errorf("[]byte is required as the key to build %s key decrypter", alg)
+ }
+
+ return keyenc.NewAES(alg, sharedkey)
+ case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
+ switch d.pubkey.(type) {
+ case x25519.PublicKey:
+ return keyenc.NewECDHESDecrypt(alg, d.ctalg, d.pubkey, d.apu, d.apv, d.privkey), nil
+ default:
+ var pubkey ecdsa.PublicKey
+ if err := keyconv.ECDSAPublicKey(&pubkey, d.pubkey); err != nil {
+ return nil, errors.Wrapf(err, "*ecdsa.PublicKey is required as the key to build %s key decrypter", alg)
+ }
+
+ var privkey ecdsa.PrivateKey
+ if err := keyconv.ECDSAPrivateKey(&privkey, d.privkey); err != nil {
+ return nil, errors.Wrapf(err, "*ecdsa.PrivateKey is required as the key to build %s key decrypter", alg)
+ }
+
+ return keyenc.NewECDHESDecrypt(alg, d.ctalg, &pubkey, d.apu, d.apv, &privkey), nil
+ }
+ default:
+ return nil, errors.Errorf(`unsupported algorithm for key decryption (%s)`, alg)
+ }
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go b/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go
new file mode 100644
index 000000000..0b1b40bf4
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/encrypt.go
@@ -0,0 +1,144 @@
+package jwe
+
+import (
+ "context"
+ "sync"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+var encryptCtxPool = sync.Pool{
+ New: func() interface{} {
+ return &encryptCtx{}
+ },
+}
+
+func getEncryptCtx() *encryptCtx {
+ return encryptCtxPool.Get().(*encryptCtx)
+}
+
+func releaseEncryptCtx(ctx *encryptCtx) {
+ ctx.protected = nil
+ ctx.contentEncrypter = nil
+ ctx.generator = nil
+ ctx.keyEncrypters = nil
+ ctx.compress = jwa.NoCompress
+ encryptCtxPool.Put(ctx)
+}
+
+// Encrypt takes the plaintext and encrypts into a JWE message.
+func (e encryptCtx) Encrypt(plaintext []byte) (*Message, error) {
+ bk, err := e.generator.Generate()
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate key")
+ }
+ cek := bk.Bytes()
+
+ if e.protected == nil {
+ // shouldn't happen, but...
+ e.protected = NewHeaders()
+ }
+
+ if err := e.protected.Set(ContentEncryptionKey, e.contentEncrypter.Algorithm()); err != nil {
+ return nil, errors.Wrap(err, `failed to set "enc" in protected header`)
+ }
+
+ compression := e.compress
+ if compression != jwa.NoCompress {
+ if err := e.protected.Set(CompressionKey, compression); err != nil {
+ return nil, errors.Wrap(err, `failed to set "zip" in protected header`)
+ }
+ }
+
+ // In JWE, multiple recipients may exist -- they receive an
+ // encrypted version of the CEK, using their key encryption
+ // algorithm of choice.
+ recipients := make([]Recipient, len(e.keyEncrypters))
+ for i, enc := range e.keyEncrypters {
+ r := NewRecipient()
+ if err := r.Headers().Set(AlgorithmKey, enc.Algorithm()); err != nil {
+ return nil, errors.Wrap(err, "failed to set header")
+ }
+ if v := enc.KeyID(); v != "" {
+ if err := r.Headers().Set(KeyIDKey, v); err != nil {
+ return nil, errors.Wrap(err, "failed to set header")
+ }
+ }
+
+ enckey, err := enc.Encrypt(cek)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to encrypt key`)
+ }
+ if enc.Algorithm() == jwa.ECDH_ES || enc.Algorithm() == jwa.DIRECT {
+ if len(e.keyEncrypters) > 1 {
+ return nil, errors.Errorf("unable to support multiple recipients for ECDH-ES")
+ }
+ cek = enckey.Bytes()
+ } else {
+ if err := r.SetEncryptedKey(enckey.Bytes()); err != nil {
+ return nil, errors.Wrap(err, "failed to set encrypted key")
+ }
+ }
+ if hp, ok := enckey.(populater); ok {
+ if err := hp.Populate(r.Headers()); err != nil {
+ return nil, errors.Wrap(err, "failed to populate")
+ }
+ }
+ recipients[i] = r
+ }
+
+ // If there's only one recipient, you want to include that in the
+ // protected header
+ if len(recipients) == 1 {
+ h, err := e.protected.Merge(context.TODO(), recipients[0].Headers())
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to merge protected headers")
+ }
+ e.protected = h
+ }
+
+ aad, err := e.protected.Encode()
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to base64 encode protected headers")
+ }
+
+ plaintext, err = compress(plaintext, compression)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to compress payload before encryption`)
+ }
+
+ // ...on the other hand, there's only one content cipher.
+ iv, ciphertext, tag, err := e.contentEncrypter.Encrypt(cek, plaintext, aad)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to encrypt payload")
+ }
+
+ msg := NewMessage()
+
+ decodedAad, err := base64.Decode(aad)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to decode base64")
+ }
+ if err := msg.Set(AuthenticatedDataKey, decodedAad); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, AuthenticatedDataKey)
+ }
+ if err := msg.Set(CipherTextKey, ciphertext); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, CipherTextKey)
+ }
+ if err := msg.Set(InitializationVectorKey, iv); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, InitializationVectorKey)
+ }
+ if err := msg.Set(ProtectedHeadersKey, e.protected); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, ProtectedHeadersKey)
+ }
+ if err := msg.Set(RecipientsKey, recipients); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, RecipientsKey)
+ }
+ if err := msg.Set(TagKey, tag); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, TagKey)
+ }
+
+ return msg, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh b/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh
new file mode 100644
index 000000000..dde877e1a
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/gen.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Script to perform code generation. This exists to overcome
+# the fact that go:generate doesn't really allow you to change directories
+
+set -e
+
+pushd internal/cmd/genheader
+go build -o genheader main.go
+popd
+
+./internal/cmd/genheader/genheader -objects=internal/cmd/genheader/objects.yml
+
+rm internal/cmd/genheader/genheader
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/headers.go b/vendor/github.com/lestrrat-go/jwx/jwe/headers.go
new file mode 100644
index 000000000..7d8efd634
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/headers.go
@@ -0,0 +1,120 @@
+package jwe
+
+import (
+ "context"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/json"
+
+ "github.com/lestrrat-go/iter/mapiter"
+ "github.com/lestrrat-go/jwx/internal/iter"
+ "github.com/pkg/errors"
+)
+
+type isZeroer interface {
+ isZero() bool
+}
+
+func (h *stdHeaders) isZero() bool {
+ return h.agreementPartyUInfo == nil &&
+ h.agreementPartyVInfo == nil &&
+ h.algorithm == nil &&
+ h.compression == nil &&
+ h.contentEncryption == nil &&
+ h.contentType == nil &&
+ h.critical == nil &&
+ h.ephemeralPublicKey == nil &&
+ h.jwk == nil &&
+ h.jwkSetURL == nil &&
+ h.keyID == nil &&
+ h.typ == nil &&
+ h.x509CertChain == nil &&
+ h.x509CertThumbprint == nil &&
+ h.x509CertThumbprintS256 == nil &&
+ h.x509URL == nil &&
+ len(h.privateParams) == 0
+}
+
+// Iterate returns a channel that successively returns all the
+// header name and values.
+func (h *stdHeaders) Iterate(ctx context.Context) Iterator {
+ pairs := h.makePairs()
+ ch := make(chan *HeaderPair, len(pairs))
+ go func(ctx context.Context, ch chan *HeaderPair, pairs []*HeaderPair) {
+ defer close(ch)
+ for _, pair := range pairs {
+ select {
+ case <-ctx.Done():
+ return
+ case ch <- pair:
+ }
+ }
+ }(ctx, ch, pairs)
+ return mapiter.New(ch)
+}
+
+func (h *stdHeaders) Walk(ctx context.Context, visitor Visitor) error {
+ return iter.WalkMap(ctx, h, visitor)
+}
+
+func (h *stdHeaders) AsMap(ctx context.Context) (map[string]interface{}, error) {
+ return iter.AsMap(ctx, h)
+}
+
+func (h *stdHeaders) Clone(ctx context.Context) (Headers, error) {
+ dst := NewHeaders()
+ if err := h.Copy(ctx, dst); err != nil {
+ return nil, errors.Wrap(err, `failed to copy header contents to new object`)
+ }
+ return dst, nil
+}
+
+func (h *stdHeaders) Copy(ctx context.Context, dst Headers) error {
+ for _, pair := range h.makePairs() {
+ if err := dst.Set(pair.Key.(string), pair.Value); err != nil {
+ return errors.Wrapf(err, `failed to set header`)
+ }
+ }
+ return nil
+}
+
+func (h *stdHeaders) Merge(ctx context.Context, h2 Headers) (Headers, error) {
+ h3 := NewHeaders()
+
+ if h != nil {
+ if err := h.Copy(ctx, h3); err != nil {
+ return nil, errors.Wrap(err, `failed to copy headers from receiver`)
+ }
+ }
+
+ if h2 != nil {
+ if err := h2.Copy(ctx, h3); err != nil {
+ return nil, errors.Wrap(err, `failed to copy headers from argument`)
+ }
+ }
+
+ return h3, nil
+}
+
+func (h *stdHeaders) Encode() ([]byte, error) {
+ buf, err := json.Marshal(h)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to marshal headers to JSON prior to encoding`)
+ }
+
+ return base64.Encode(buf), nil
+}
+
+func (h *stdHeaders) Decode(buf []byte) error {
+ // base64 json string -> json object representation of header
+ decoded, err := base64.Decode(buf)
+ if err != nil {
+ return errors.Wrap(err, "failed to unmarshal base64 encoded buffer")
+ }
+
+ if err := json.Unmarshal(decoded, h); err != nil {
+ return errors.Wrap(err, "failed to unmarshal buffer")
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go b/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go
new file mode 100644
index 000000000..d486502b7
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/headers_gen.go
@@ -0,0 +1,714 @@
+// This file is auto-generated by jwe/internal/cmd/genheaders/main.go. DO NOT EDIT
+
+package jwe
+
+import (
+ "bytes"
+ "context"
+ "sort"
+ "sync"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/pkg/errors"
+)
+
+const (
+ AgreementPartyUInfoKey = "apu"
+ AgreementPartyVInfoKey = "apv"
+ AlgorithmKey = "alg"
+ CompressionKey = "zip"
+ ContentEncryptionKey = "enc"
+ ContentTypeKey = "cty"
+ CriticalKey = "crit"
+ EphemeralPublicKeyKey = "epk"
+ JWKKey = "jwk"
+ JWKSetURLKey = "jku"
+ KeyIDKey = "kid"
+ TypeKey = "typ"
+ X509CertChainKey = "x5c"
+ X509CertThumbprintKey = "x5t"
+ X509CertThumbprintS256Key = "x5t#S256"
+ X509URLKey = "x5u"
+)
+
+// Headers describe a standard Header set.
+type Headers interface {
+ json.Marshaler
+ json.Unmarshaler
+ AgreementPartyUInfo() []byte
+ AgreementPartyVInfo() []byte
+ Algorithm() jwa.KeyEncryptionAlgorithm
+ Compression() jwa.CompressionAlgorithm
+ ContentEncryption() jwa.ContentEncryptionAlgorithm
+ ContentType() string
+ Critical() []string
+ EphemeralPublicKey() jwk.Key
+ JWK() jwk.Key
+ JWKSetURL() string
+ KeyID() string
+ Type() string
+ X509CertChain() []string
+ X509CertThumbprint() string
+ X509CertThumbprintS256() string
+ X509URL() string
+ Iterate(ctx context.Context) Iterator
+ Walk(ctx context.Context, v Visitor) error
+ AsMap(ctx context.Context) (map[string]interface{}, error)
+ Get(string) (interface{}, bool)
+ Set(string, interface{}) error
+ Remove(string) error
+ Encode() ([]byte, error)
+ Decode([]byte) error
+ // PrivateParams returns the map containing the non-standard ('private') parameters
+ // in the associated header. WARNING: DO NOT USE PrivateParams()
+ // IF YOU HAVE CONCURRENT CODE ACCESSING THEM. Use AsMap() to
+ // get a copy of the entire header instead
+ PrivateParams() map[string]interface{}
+ Clone(context.Context) (Headers, error)
+ Copy(context.Context, Headers) error
+ Merge(context.Context, Headers) (Headers, error)
+}
+
+type stdHeaders struct {
+ agreementPartyUInfo []byte
+ agreementPartyVInfo []byte
+ algorithm *jwa.KeyEncryptionAlgorithm
+ compression *jwa.CompressionAlgorithm
+ contentEncryption *jwa.ContentEncryptionAlgorithm
+ contentType *string
+ critical []string
+ ephemeralPublicKey jwk.Key
+ jwk jwk.Key
+ jwkSetURL *string
+ keyID *string
+ typ *string
+ x509CertChain []string
+ x509CertThumbprint *string
+ x509CertThumbprintS256 *string
+ x509URL *string
+ privateParams map[string]interface{}
+ mu *sync.RWMutex
+}
+
+func NewHeaders() Headers {
+ return &stdHeaders{
+ mu: &sync.RWMutex{},
+ privateParams: map[string]interface{}{},
+ }
+}
+
+func (h *stdHeaders) AgreementPartyUInfo() []byte {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.agreementPartyUInfo
+}
+
+func (h *stdHeaders) AgreementPartyVInfo() []byte {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.agreementPartyVInfo
+}
+
+func (h *stdHeaders) Algorithm() jwa.KeyEncryptionAlgorithm {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.algorithm == nil {
+ return ""
+ }
+ return *(h.algorithm)
+}
+
+func (h *stdHeaders) Compression() jwa.CompressionAlgorithm {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.compression == nil {
+ return jwa.NoCompress
+ }
+ return *(h.compression)
+}
+
+func (h *stdHeaders) ContentEncryption() jwa.ContentEncryptionAlgorithm {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.contentEncryption == nil {
+ return ""
+ }
+ return *(h.contentEncryption)
+}
+
+func (h *stdHeaders) ContentType() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.contentType == nil {
+ return ""
+ }
+ return *(h.contentType)
+}
+
+func (h *stdHeaders) Critical() []string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.critical
+}
+
+func (h *stdHeaders) EphemeralPublicKey() jwk.Key {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.ephemeralPublicKey
+}
+
+func (h *stdHeaders) JWK() jwk.Key {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.jwk
+}
+
+func (h *stdHeaders) JWKSetURL() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.jwkSetURL == nil {
+ return ""
+ }
+ return *(h.jwkSetURL)
+}
+
+func (h *stdHeaders) KeyID() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.keyID == nil {
+ return ""
+ }
+ return *(h.keyID)
+}
+
+func (h *stdHeaders) Type() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.typ == nil {
+ return ""
+ }
+ return *(h.typ)
+}
+
+func (h *stdHeaders) X509CertChain() []string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.x509CertChain
+}
+
+func (h *stdHeaders) X509CertThumbprint() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.x509CertThumbprint == nil {
+ return ""
+ }
+ return *(h.x509CertThumbprint)
+}
+
+func (h *stdHeaders) X509CertThumbprintS256() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.x509CertThumbprintS256 == nil {
+ return ""
+ }
+ return *(h.x509CertThumbprintS256)
+}
+
+func (h *stdHeaders) X509URL() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.x509URL == nil {
+ return ""
+ }
+ return *(h.x509URL)
+}
+
+func (h *stdHeaders) makePairs() []*HeaderPair {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ var pairs []*HeaderPair
+ if h.agreementPartyUInfo != nil {
+ pairs = append(pairs, &HeaderPair{Key: AgreementPartyUInfoKey, Value: h.agreementPartyUInfo})
+ }
+ if h.agreementPartyVInfo != nil {
+ pairs = append(pairs, &HeaderPair{Key: AgreementPartyVInfoKey, Value: h.agreementPartyVInfo})
+ }
+ if h.algorithm != nil {
+ pairs = append(pairs, &HeaderPair{Key: AlgorithmKey, Value: *(h.algorithm)})
+ }
+ if h.compression != nil {
+ pairs = append(pairs, &HeaderPair{Key: CompressionKey, Value: *(h.compression)})
+ }
+ if h.contentEncryption != nil {
+ pairs = append(pairs, &HeaderPair{Key: ContentEncryptionKey, Value: *(h.contentEncryption)})
+ }
+ if h.contentType != nil {
+ pairs = append(pairs, &HeaderPair{Key: ContentTypeKey, Value: *(h.contentType)})
+ }
+ if h.critical != nil {
+ pairs = append(pairs, &HeaderPair{Key: CriticalKey, Value: h.critical})
+ }
+ if h.ephemeralPublicKey != nil {
+ pairs = append(pairs, &HeaderPair{Key: EphemeralPublicKeyKey, Value: h.ephemeralPublicKey})
+ }
+ if h.jwk != nil {
+ pairs = append(pairs, &HeaderPair{Key: JWKKey, Value: h.jwk})
+ }
+ if h.jwkSetURL != nil {
+ pairs = append(pairs, &HeaderPair{Key: JWKSetURLKey, Value: *(h.jwkSetURL)})
+ }
+ if h.keyID != nil {
+ pairs = append(pairs, &HeaderPair{Key: KeyIDKey, Value: *(h.keyID)})
+ }
+ if h.typ != nil {
+ pairs = append(pairs, &HeaderPair{Key: TypeKey, Value: *(h.typ)})
+ }
+ if h.x509CertChain != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain})
+ }
+ if h.x509CertThumbprint != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)})
+ }
+ if h.x509CertThumbprintS256 != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintS256Key, Value: *(h.x509CertThumbprintS256)})
+ }
+ if h.x509URL != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509URLKey, Value: *(h.x509URL)})
+ }
+ for k, v := range h.privateParams {
+ pairs = append(pairs, &HeaderPair{Key: k, Value: v})
+ }
+ return pairs
+}
+
+func (h *stdHeaders) PrivateParams() map[string]interface{} {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.privateParams
+}
+
+func (h *stdHeaders) Get(name string) (interface{}, bool) {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ switch name {
+ case AgreementPartyUInfoKey:
+ if h.agreementPartyUInfo == nil {
+ return nil, false
+ }
+ return h.agreementPartyUInfo, true
+ case AgreementPartyVInfoKey:
+ if h.agreementPartyVInfo == nil {
+ return nil, false
+ }
+ return h.agreementPartyVInfo, true
+ case AlgorithmKey:
+ if h.algorithm == nil {
+ return nil, false
+ }
+ return *(h.algorithm), true
+ case CompressionKey:
+ if h.compression == nil {
+ return nil, false
+ }
+ return *(h.compression), true
+ case ContentEncryptionKey:
+ if h.contentEncryption == nil {
+ return nil, false
+ }
+ return *(h.contentEncryption), true
+ case ContentTypeKey:
+ if h.contentType == nil {
+ return nil, false
+ }
+ return *(h.contentType), true
+ case CriticalKey:
+ if h.critical == nil {
+ return nil, false
+ }
+ return h.critical, true
+ case EphemeralPublicKeyKey:
+ if h.ephemeralPublicKey == nil {
+ return nil, false
+ }
+ return h.ephemeralPublicKey, true
+ case JWKKey:
+ if h.jwk == nil {
+ return nil, false
+ }
+ return h.jwk, true
+ case JWKSetURLKey:
+ if h.jwkSetURL == nil {
+ return nil, false
+ }
+ return *(h.jwkSetURL), true
+ case KeyIDKey:
+ if h.keyID == nil {
+ return nil, false
+ }
+ return *(h.keyID), true
+ case TypeKey:
+ if h.typ == nil {
+ return nil, false
+ }
+ return *(h.typ), true
+ case X509CertChainKey:
+ if h.x509CertChain == nil {
+ return nil, false
+ }
+ return h.x509CertChain, true
+ case X509CertThumbprintKey:
+ if h.x509CertThumbprint == nil {
+ return nil, false
+ }
+ return *(h.x509CertThumbprint), true
+ case X509CertThumbprintS256Key:
+ if h.x509CertThumbprintS256 == nil {
+ return nil, false
+ }
+ return *(h.x509CertThumbprintS256), true
+ case X509URLKey:
+ if h.x509URL == nil {
+ return nil, false
+ }
+ return *(h.x509URL), true
+ default:
+ v, ok := h.privateParams[name]
+ return v, ok
+ }
+}
+
+func (h *stdHeaders) Set(name string, value interface{}) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ return h.setNoLock(name, value)
+}
+
+func (h *stdHeaders) setNoLock(name string, value interface{}) error {
+ switch name {
+ case AgreementPartyUInfoKey:
+ if v, ok := value.([]byte); ok {
+ h.agreementPartyUInfo = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, AgreementPartyUInfoKey, value)
+ case AgreementPartyVInfoKey:
+ if v, ok := value.([]byte); ok {
+ h.agreementPartyVInfo = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, AgreementPartyVInfoKey, value)
+ case AlgorithmKey:
+ if v, ok := value.(jwa.KeyEncryptionAlgorithm); ok {
+ h.algorithm = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, AlgorithmKey, value)
+ case CompressionKey:
+ if v, ok := value.(jwa.CompressionAlgorithm); ok {
+ h.compression = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, CompressionKey, value)
+ case ContentEncryptionKey:
+ if v, ok := value.(jwa.ContentEncryptionAlgorithm); ok {
+ if v == "" {
+ return errors.New(`"enc" field cannot be an empty string`)
+ }
+ h.contentEncryption = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, ContentEncryptionKey, value)
+ case ContentTypeKey:
+ if v, ok := value.(string); ok {
+ h.contentType = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value)
+ case CriticalKey:
+ if v, ok := value.([]string); ok {
+ h.critical = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, CriticalKey, value)
+ case EphemeralPublicKeyKey:
+ if v, ok := value.(jwk.Key); ok {
+ h.ephemeralPublicKey = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, EphemeralPublicKeyKey, value)
+ case JWKKey:
+ if v, ok := value.(jwk.Key); ok {
+ h.jwk = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, JWKKey, value)
+ case JWKSetURLKey:
+ if v, ok := value.(string); ok {
+ h.jwkSetURL = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, JWKSetURLKey, value)
+ case KeyIDKey:
+ if v, ok := value.(string); ok {
+ h.keyID = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value)
+ case TypeKey:
+ if v, ok := value.(string); ok {
+ h.typ = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, TypeKey, value)
+ case X509CertChainKey:
+ if v, ok := value.([]string); ok {
+ h.x509CertChain = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value)
+ case X509CertThumbprintKey:
+ if v, ok := value.(string); ok {
+ h.x509CertThumbprint = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value)
+ case X509CertThumbprintS256Key:
+ if v, ok := value.(string); ok {
+ h.x509CertThumbprintS256 = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value)
+ case X509URLKey:
+ if v, ok := value.(string); ok {
+ h.x509URL = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value)
+ default:
+ if h.privateParams == nil {
+ h.privateParams = map[string]interface{}{}
+ }
+ h.privateParams[name] = value
+ }
+ return nil
+}
+
+func (h *stdHeaders) Remove(key string) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ switch key {
+ case AgreementPartyUInfoKey:
+ h.agreementPartyUInfo = nil
+ case AgreementPartyVInfoKey:
+ h.agreementPartyVInfo = nil
+ case AlgorithmKey:
+ h.algorithm = nil
+ case CompressionKey:
+ h.compression = nil
+ case ContentEncryptionKey:
+ h.contentEncryption = nil
+ case ContentTypeKey:
+ h.contentType = nil
+ case CriticalKey:
+ h.critical = nil
+ case EphemeralPublicKeyKey:
+ h.ephemeralPublicKey = nil
+ case JWKKey:
+ h.jwk = nil
+ case JWKSetURLKey:
+ h.jwkSetURL = nil
+ case KeyIDKey:
+ h.keyID = nil
+ case TypeKey:
+ h.typ = nil
+ case X509CertChainKey:
+ h.x509CertChain = nil
+ case X509CertThumbprintKey:
+ h.x509CertThumbprint = nil
+ case X509CertThumbprintS256Key:
+ h.x509CertThumbprintS256 = nil
+ case X509URLKey:
+ h.x509URL = nil
+ default:
+ delete(h.privateParams, key)
+ }
+ return nil
+}
+
+func (h *stdHeaders) UnmarshalJSON(buf []byte) error {
+ h.agreementPartyUInfo = nil
+ h.agreementPartyVInfo = nil
+ h.algorithm = nil
+ h.compression = nil
+ h.contentEncryption = nil
+ h.contentType = nil
+ h.critical = nil
+ h.ephemeralPublicKey = nil
+ h.jwk = nil
+ h.jwkSetURL = nil
+ h.keyID = nil
+ h.typ = nil
+ h.x509CertChain = nil
+ h.x509CertThumbprint = nil
+ h.x509CertThumbprintS256 = nil
+ h.x509URL = nil
+ dec := json.NewDecoder(bytes.NewReader(buf))
+LOOP:
+ for {
+ tok, err := dec.Token()
+ if err != nil {
+ return errors.Wrap(err, `error reading token`)
+ }
+ switch tok := tok.(type) {
+ case json.Delim:
+ // Assuming we're doing everything correctly, we should ONLY
+ // get either '{' or '}' here.
+ if tok == '}' { // End of object
+ break LOOP
+ } else if tok != '{' {
+ return errors.Errorf(`expected '{', but got '%c'`, tok)
+ }
+ case string: // Objects can only have string keys
+ switch tok {
+ case AgreementPartyUInfoKey:
+ if err := json.AssignNextBytesToken(&h.agreementPartyUInfo, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, AgreementPartyUInfoKey)
+ }
+ case AgreementPartyVInfoKey:
+ if err := json.AssignNextBytesToken(&h.agreementPartyVInfo, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, AgreementPartyVInfoKey)
+ }
+ case AlgorithmKey:
+ var decoded jwa.KeyEncryptionAlgorithm
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey)
+ }
+ h.algorithm = &decoded
+ case CompressionKey:
+ var decoded jwa.CompressionAlgorithm
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, CompressionKey)
+ }
+ h.compression = &decoded
+ case ContentEncryptionKey:
+ var decoded jwa.ContentEncryptionAlgorithm
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, ContentEncryptionKey)
+ }
+ h.contentEncryption = &decoded
+ case ContentTypeKey:
+ if err := json.AssignNextStringToken(&h.contentType, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, ContentTypeKey)
+ }
+ case CriticalKey:
+ var decoded []string
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, CriticalKey)
+ }
+ h.critical = decoded
+ case EphemeralPublicKeyKey:
+ var buf json.RawMessage
+ if err := dec.Decode(&buf); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, EphemeralPublicKeyKey)
+ }
+ key, err := jwk.ParseKey(buf)
+ if err != nil {
+ return errors.Wrapf(err, `failed to parse JWK for key %s`, EphemeralPublicKeyKey)
+ }
+ h.ephemeralPublicKey = key
+ case JWKKey:
+ var buf json.RawMessage
+ if err := dec.Decode(&buf); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, JWKKey)
+ }
+ key, err := jwk.ParseKey(buf)
+ if err != nil {
+ return errors.Wrapf(err, `failed to parse JWK for key %s`, JWKKey)
+ }
+ h.jwk = key
+ case JWKSetURLKey:
+ if err := json.AssignNextStringToken(&h.jwkSetURL, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, JWKSetURLKey)
+ }
+ case KeyIDKey:
+ if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey)
+ }
+ case TypeKey:
+ if err := json.AssignNextStringToken(&h.typ, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, TypeKey)
+ }
+ case X509CertChainKey:
+ var decoded []string
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey)
+ }
+ h.x509CertChain = decoded
+ case X509CertThumbprintKey:
+ if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey)
+ }
+ case X509CertThumbprintS256Key:
+ if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key)
+ }
+ case X509URLKey:
+ if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey)
+ }
+ default:
+ decoded, err := registry.Decode(dec, tok)
+ if err != nil {
+ return err
+ }
+ h.setNoLock(tok, decoded)
+ }
+ default:
+ return errors.Errorf(`invalid token %T`, tok)
+ }
+ }
+ return nil
+}
+
+func (h stdHeaders) MarshalJSON() ([]byte, error) {
+ data := make(map[string]interface{})
+ fields := make([]string, 0, 16)
+ for _, pair := range h.makePairs() {
+ fields = append(fields, pair.Key.(string))
+ data[pair.Key.(string)] = pair.Value
+ }
+
+ sort.Strings(fields)
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+ buf.WriteByte('{')
+ enc := json.NewEncoder(buf)
+ for i, f := range fields {
+ if i > 0 {
+ buf.WriteRune(',')
+ }
+ buf.WriteRune('"')
+ buf.WriteString(f)
+ buf.WriteString(`":`)
+ v := data[f]
+ switch v := v.(type) {
+ case []byte:
+ buf.WriteRune('"')
+ buf.WriteString(base64.EncodeToString(v))
+ buf.WriteRune('"')
+ default:
+ if err := enc.Encode(v); err != nil {
+ errors.Errorf(`failed to encode value for field %s`, f)
+ }
+ buf.Truncate(buf.Len() - 1)
+ }
+ }
+ buf.WriteByte('}')
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/interface.go b/vendor/github.com/lestrrat-go/jwx/jwe/interface.go
new file mode 100644
index 000000000..c23120883
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/interface.go
@@ -0,0 +1,101 @@
+package jwe
+
+import (
+ "github.com/lestrrat-go/iter/mapiter"
+ "github.com/lestrrat-go/jwx/internal/iter"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/keyenc"
+ "github.com/lestrrat-go/jwx/jwe/internal/keygen"
+)
+
+// Recipient holds the encrypted key and hints to decrypt the key
+type Recipient interface {
+ Headers() Headers
+ EncryptedKey() []byte
+ SetHeaders(Headers) error
+ SetEncryptedKey([]byte) error
+}
+
+type stdRecipient struct {
+ headers Headers
+ encryptedKey []byte
+}
+
+// Message contains the entire encrypted JWE message. You should not
+// expect to use Message for anything other than inspecting the
+// state of an encrypted message. This is because encryption is
+// highly context sensitive, and once we parse the original payload
+// into an object, we may not always be able to recreate the exact
+// context in which the encryption happened.
+//
+// For example, it is totally valid for if the protected header's
+// integrity was calculated using a non-standard line breaks:
+//
+// {"a dummy":
+// "protected header"}
+//
+// Once parsed, though, we can only serialize the protected header as:
+//
+// {"a dummy":"protected header"}
+//
+// which would obviously result in a contradicting integrity value
+// if we tried to re-calculate it from a parsed message.
+//nolint:govet
+type Message struct {
+ authenticatedData []byte
+ cipherText []byte
+ initializationVector []byte
+ tag []byte
+ recipients []Recipient
+ protectedHeaders Headers
+ unprotectedHeaders Headers
+
+ // These two fields below are not available for the public consumers of this object.
+ // rawProtectedHeaders stores the original protected header buffer
+ rawProtectedHeaders []byte
+ // storeProtectedHeaders is a hint to be used in UnmarshalJSON().
+ // When this flag is true, UnmarshalJSON() will populate the
+ // rawProtectedHeaders field
+ storeProtectedHeaders bool
+}
+
+// contentEncrypter encrypts the content using the content using the
+// encrypted key
+type contentEncrypter interface {
+ Algorithm() jwa.ContentEncryptionAlgorithm
+ Encrypt([]byte, []byte, []byte) ([]byte, []byte, []byte, error)
+}
+
+//nolint:govet
+type encryptCtx struct {
+ keyEncrypters []keyenc.Encrypter
+ protected Headers
+ contentEncrypter contentEncrypter
+ generator keygen.Generator
+ compress jwa.CompressionAlgorithm
+}
+
+// populater is an interface for things that may modify the
+// JWE header. e.g. ByteWithECPrivateKey
+type populater interface {
+ Populate(keygen.Setter) error
+}
+
+type Visitor = iter.MapVisitor
+type VisitorFunc = iter.MapVisitorFunc
+type HeaderPair = mapiter.Pair
+type Iterator = mapiter.Iterator
+
+// PostParser is used in conjunction with jwe.WithPostParser().
+// This hook is called right after the JWE message has been parsed
+// but before the actual decryption takes place during `jwe.Decrypt()`.
+type PostParser interface {
+ PostParse(DecryptCtx) error
+}
+
+// PostParseFunc is a PostParser that is represented by a single function
+type PostParseFunc func(DecryptCtx) error
+
+func (fn PostParseFunc) PostParse(ctx DecryptCtx) error {
+ return fn(ctx)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go
new file mode 100644
index 000000000..f08f2cbec
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/aescbc/aescbc.go
@@ -0,0 +1,207 @@
+package aescbc
+
+import (
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/sha512"
+ "crypto/subtle"
+ "encoding/binary"
+ "fmt"
+ "hash"
+
+ "github.com/pkg/errors"
+)
+
+const (
+ NonceSize = 16
+)
+
+func pad(buf []byte, n int) []byte {
+ rem := n - len(buf)%n
+ if rem == 0 {
+ return buf
+ }
+
+ newbuf := make([]byte, len(buf)+rem)
+ copy(newbuf, buf)
+
+ for i := len(buf); i < len(newbuf); i++ {
+ newbuf[i] = byte(rem)
+ }
+ return newbuf
+}
+
+func unpad(buf []byte, n int) ([]byte, error) {
+ lbuf := len(buf)
+ rem := lbuf % n
+ if rem != 0 {
+ return nil, errors.Errorf("input buffer must be multiple of block size %d", n)
+ }
+
+ count := 0
+ last := buf[lbuf-1]
+ for i := lbuf - 1; i >= 0; i-- {
+ if buf[i] != last {
+ break
+ }
+ count++
+ }
+ if count != int(last) {
+ return nil, errors.New("invalid padding")
+ }
+
+ return buf[:lbuf-int(last)], nil
+}
+
+type Hmac struct {
+ blockCipher cipher.Block
+ hash func() hash.Hash
+ keysize int
+ tagsize int
+ integrityKey []byte
+}
+
+type BlockCipherFunc func([]byte) (cipher.Block, error)
+
+func New(key []byte, f BlockCipherFunc) (hmac *Hmac, err error) {
+ keysize := len(key) / 2
+ ikey := key[:keysize]
+ ekey := key[keysize:]
+
+ bc, ciphererr := f(ekey)
+ if ciphererr != nil {
+ err = errors.Wrap(ciphererr, `failed to execute block cipher function`)
+ return
+ }
+
+ var hfunc func() hash.Hash
+ switch keysize {
+ case 16:
+ hfunc = sha256.New
+ case 24:
+ hfunc = sha512.New384
+ case 32:
+ hfunc = sha512.New
+ default:
+ return nil, errors.Errorf("unsupported key size %d", keysize)
+ }
+
+ return &Hmac{
+ blockCipher: bc,
+ hash: hfunc,
+ integrityKey: ikey,
+ keysize: keysize,
+ tagsize: keysize, // NonceSize,
+ // While investigating GH #207, I stumbled upon another problem where
+ // the computed tags don't match on decrypt. After poking through the
+ // code using a bunch of debug statements, I've finally found out that
+ // tagsize = keysize makes the whole thing work.
+ }, nil
+}
+
+// NonceSize fulfills the crypto.AEAD interface
+func (c Hmac) NonceSize() int {
+ return NonceSize
+}
+
+// Overhead fulfills the crypto.AEAD interface
+func (c Hmac) Overhead() int {
+ return c.blockCipher.BlockSize() + c.tagsize
+}
+
+func (c Hmac) ComputeAuthTag(aad, nonce, ciphertext []byte) ([]byte, error) {
+ buf := make([]byte, len(aad)+len(nonce)+len(ciphertext)+8)
+ n := 0
+ n += copy(buf, aad)
+ n += copy(buf[n:], nonce)
+ n += copy(buf[n:], ciphertext)
+ binary.BigEndian.PutUint64(buf[n:], uint64(len(aad)*8))
+
+ h := hmac.New(c.hash, c.integrityKey)
+ if _, err := h.Write(buf); err != nil {
+ return nil, errors.Wrap(err, "failed to write ComputeAuthTag using Hmac")
+ }
+ s := h.Sum(nil)
+ return s[:c.tagsize], nil
+}
+
+func ensureSize(dst []byte, n int) []byte {
+ // if the dst buffer has enough length just copy the relevant parts to it.
+ // Otherwise create a new slice that's big enough, and operate on that
+ // Note: I think go-jose has a bug in that it checks for cap(), but not len().
+ ret := dst
+ if diff := n - len(dst); diff > 0 {
+ // dst is not big enough
+ ret = make([]byte, n)
+ copy(ret, dst)
+ }
+ return ret
+}
+
+// Seal fulfills the crypto.AEAD interface
+func (c Hmac) Seal(dst, nonce, plaintext, data []byte) []byte {
+ ctlen := len(plaintext)
+ ciphertext := make([]byte, ctlen+c.Overhead())[:ctlen]
+ copy(ciphertext, plaintext)
+ ciphertext = pad(ciphertext, c.blockCipher.BlockSize())
+
+ cbc := cipher.NewCBCEncrypter(c.blockCipher, nonce)
+ cbc.CryptBlocks(ciphertext, ciphertext)
+
+ authtag, err := c.ComputeAuthTag(data, nonce, ciphertext)
+ if err != nil {
+ // Hmac implements cipher.AEAD interface. Seal can't return error.
+ // But currently it never reach here because of Hmac.ComputeAuthTag doesn't return error.
+ panic(fmt.Errorf("failed to seal on hmac: %v", err))
+ }
+
+ retlen := len(dst) + len(ciphertext) + len(authtag)
+
+ ret := ensureSize(dst, retlen)
+ out := ret[len(dst):]
+ n := copy(out, ciphertext)
+ copy(out[n:], authtag)
+
+ return ret
+}
+
+// Open fulfills the crypto.AEAD interface
+func (c Hmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
+ if len(ciphertext) < c.keysize {
+ return nil, errors.New("invalid ciphertext (too short)")
+ }
+
+ tagOffset := len(ciphertext) - c.tagsize
+ if tagOffset%c.blockCipher.BlockSize() != 0 {
+ return nil, fmt.Errorf(
+ "invalid ciphertext (invalid length: %d %% %d != 0)",
+ tagOffset,
+ c.blockCipher.BlockSize(),
+ )
+ }
+ tag := ciphertext[tagOffset:]
+ ciphertext = ciphertext[:tagOffset]
+
+ expectedTag, err := c.ComputeAuthTag(data, nonce, ciphertext[:tagOffset])
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to compute auth tag`)
+ }
+
+ if subtle.ConstantTimeCompare(expectedTag, tag) != 1 {
+ return nil, errors.New("invalid ciphertext (tag mismatch)")
+ }
+
+ cbc := cipher.NewCBCDecrypter(c.blockCipher, nonce)
+ buf := make([]byte, tagOffset)
+ cbc.CryptBlocks(buf, ciphertext)
+
+ plaintext, err := unpad(buf, c.blockCipher.BlockSize())
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to generate plaintext from decrypted blocks`)
+ }
+ ret := ensureSize(dst, len(plaintext))
+ out := ret[len(dst):]
+ copy(out, plaintext)
+ return ret, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go
new file mode 100644
index 000000000..586feff89
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/cipher.go
@@ -0,0 +1,166 @@
+package cipher
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "fmt"
+
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/aescbc"
+ "github.com/lestrrat-go/jwx/jwe/internal/keygen"
+ "github.com/pkg/errors"
+)
+
+var gcm = &gcmFetcher{}
+var cbc = &cbcFetcher{}
+
+func (f gcmFetcher) Fetch(key []byte) (cipher.AEAD, error) {
+ aescipher, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, errors.Wrap(err, "cipher: failed to create AES cipher for GCM")
+ }
+
+ aead, err := cipher.NewGCM(aescipher)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to create GCM for cipher`)
+ }
+ return aead, nil
+}
+
+func (f cbcFetcher) Fetch(key []byte) (cipher.AEAD, error) {
+ aead, err := aescbc.New(key, aes.NewCipher)
+ if err != nil {
+ return nil, errors.Wrap(err, "cipher: failed to create AES cipher for CBC")
+ }
+ return aead, nil
+}
+
+func (c AesContentCipher) KeySize() int {
+ return c.keysize
+}
+
+func (c AesContentCipher) TagSize() int {
+ return c.tagsize
+}
+
+func NewAES(alg jwa.ContentEncryptionAlgorithm) (*AesContentCipher, error) {
+ var keysize int
+ var tagsize int
+ var fetcher Fetcher
+ switch alg {
+ case jwa.A128GCM:
+ keysize = 16
+ tagsize = 16
+ fetcher = gcm
+ case jwa.A192GCM:
+ keysize = 24
+ tagsize = 16
+ fetcher = gcm
+ case jwa.A256GCM:
+ keysize = 32
+ tagsize = 16
+ fetcher = gcm
+ case jwa.A128CBC_HS256:
+ tagsize = 16
+ keysize = tagsize * 2
+ fetcher = cbc
+ case jwa.A192CBC_HS384:
+ tagsize = 24
+ keysize = tagsize * 2
+ fetcher = cbc
+ case jwa.A256CBC_HS512:
+ tagsize = 32
+ keysize = tagsize * 2
+ fetcher = cbc
+ default:
+ return nil, errors.Errorf("failed to create AES content cipher: invalid algorithm (%s)", alg)
+ }
+
+ return &AesContentCipher{
+ keysize: keysize,
+ tagsize: tagsize,
+ fetch: fetcher,
+ }, nil
+}
+
+func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertext, tag []byte, err error) {
+ var aead cipher.AEAD
+ aead, err = c.fetch.Fetch(cek)
+ if err != nil {
+ return nil, nil, nil, errors.Wrap(err, "failed to fetch AEAD")
+ }
+
+ // Seal may panic (argh!), so protect ourselves from that
+ defer func() {
+ if e := recover(); e != nil {
+ switch e := e.(type) {
+ case error:
+ err = e
+ case string:
+ err = errors.New(e)
+ default:
+ err = fmt.Errorf("%s", e)
+ }
+ err = errors.Wrap(err, "failed to encrypt")
+ }
+ }()
+
+ var bs keygen.ByteSource
+ if c.NonceGenerator == nil {
+ bs, err = keygen.NewRandom(aead.NonceSize()).Generate()
+ } else {
+ bs, err = c.NonceGenerator.Generate()
+ }
+ if err != nil {
+ return nil, nil, nil, errors.Wrap(err, "failed to generate nonce")
+ }
+ iv = bs.Bytes()
+
+ combined := aead.Seal(nil, iv, plaintext, aad)
+ tagoffset := len(combined) - c.TagSize()
+
+ if tagoffset < 0 {
+ panic(fmt.Sprintf("tag offset is less than 0 (combined len = %d, tagsize = %d)", len(combined), c.TagSize()))
+ }
+
+ tag = combined[tagoffset:]
+ ciphertext = make([]byte, tagoffset)
+ copy(ciphertext, combined[:tagoffset])
+
+ return
+}
+
+func (c AesContentCipher) Decrypt(cek, iv, ciphertxt, tag, aad []byte) (plaintext []byte, err error) {
+ aead, err := c.fetch.Fetch(cek)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to fetch AEAD data")
+ }
+
+ // Open may panic (argh!), so protect ourselves from that
+ defer func() {
+ if e := recover(); e != nil {
+ switch e := e.(type) {
+ case error:
+ err = e
+ case string:
+ err = errors.New(e)
+ default:
+ err = fmt.Errorf("%s", e)
+ }
+ err = errors.Wrap(err, "failed to decrypt")
+ return
+ }
+ }()
+
+ combined := make([]byte, len(ciphertxt)+len(tag))
+ copy(combined, ciphertxt)
+ copy(combined[len(ciphertxt):], tag)
+
+ buf, aeaderr := aead.Open(nil, iv, combined, aad)
+ if aeaderr != nil {
+ err = errors.Wrap(aeaderr, `aead.Open failed`)
+ return
+ }
+ plaintext = buf
+ return
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go
new file mode 100644
index 000000000..8af8202d4
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/cipher/interface.go
@@ -0,0 +1,34 @@
+package cipher
+
+import (
+ "crypto/cipher"
+
+ "github.com/lestrrat-go/jwx/jwe/internal/keygen"
+)
+
+const (
+ TagSize = 16
+)
+
+// ContentCipher knows how to encrypt/decrypt the content given a content
+// encryption key and other data
+type ContentCipher interface {
+ KeySize() int
+ Encrypt(cek, aad, plaintext []byte) ([]byte, []byte, []byte, error)
+ Decrypt(cek, iv, aad, ciphertext, tag []byte) ([]byte, error)
+}
+
+type Fetcher interface {
+ Fetch([]byte) (cipher.AEAD, error)
+}
+
+type gcmFetcher struct{}
+type cbcFetcher struct{}
+
+// AesContentCipher represents a cipher based on AES
+type AesContentCipher struct {
+ NonceGenerator keygen.Generator
+ fetch Fetcher
+ keysize int
+ tagsize int
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go
new file mode 100644
index 000000000..777c1a5ba
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/concatkdf/concatkdf.go
@@ -0,0 +1,67 @@
+package concatkdf
+
+import (
+ "crypto"
+ "encoding/binary"
+
+ "github.com/pkg/errors"
+)
+
+type KDF struct {
+ buf []byte
+ otherinfo []byte
+ z []byte
+ hash crypto.Hash
+}
+
+func ndata(src []byte) []byte {
+ buf := make([]byte, 4+len(src))
+ binary.BigEndian.PutUint32(buf, uint32(len(src)))
+ copy(buf[4:], src)
+ return buf
+}
+
+func New(hash crypto.Hash, alg, Z, apu, apv, pubinfo, privinfo []byte) *KDF {
+ algbuf := ndata(alg)
+ apubuf := ndata(apu)
+ apvbuf := ndata(apv)
+
+ concat := make([]byte, len(algbuf)+len(apubuf)+len(apvbuf)+len(pubinfo)+len(privinfo))
+ n := copy(concat, algbuf)
+ n += copy(concat[n:], apubuf)
+ n += copy(concat[n:], apvbuf)
+ n += copy(concat[n:], pubinfo)
+ copy(concat[n:], privinfo)
+
+ return &KDF{
+ hash: hash,
+ otherinfo: concat,
+ z: Z,
+ }
+}
+
+func (k *KDF) Read(out []byte) (int, error) {
+ var round uint32 = 1
+ h := k.hash.New()
+
+ for len(out) > len(k.buf) {
+ h.Reset()
+
+ if err := binary.Write(h, binary.BigEndian, round); err != nil {
+ return 0, errors.Wrap(err, "failed to write round using kdf")
+ }
+ if _, err := h.Write(k.z); err != nil {
+ return 0, errors.Wrap(err, "failed to write z using kdf")
+ }
+ if _, err := h.Write(k.otherinfo); err != nil {
+ return 0, errors.Wrap(err, "failed to write other info using kdf")
+ }
+
+ k.buf = append(k.buf, h.Sum(nil)...)
+ round++
+ }
+
+ n := copy(out, k.buf[:len(out)])
+ k.buf = k.buf[len(out):]
+ return n, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go
new file mode 100644
index 000000000..b83af42dc
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/content_crypt.go
@@ -0,0 +1,42 @@
+package content_crypt //nolint:golint
+
+import (
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/cipher"
+ "github.com/pkg/errors"
+)
+
+func (c Generic) Algorithm() jwa.ContentEncryptionAlgorithm {
+ return c.alg
+}
+
+func (c Generic) Encrypt(cek, plaintext, aad []byte) ([]byte, []byte, []byte, error) {
+ iv, encrypted, tag, err := c.cipher.Encrypt(cek, plaintext, aad)
+ if err != nil {
+ return nil, nil, nil, errors.Wrap(err, `failed to crypt content`)
+ }
+
+ return iv, encrypted, tag, nil
+}
+
+func (c Generic) Decrypt(cek, iv, ciphertext, tag, aad []byte) ([]byte, error) {
+ return c.cipher.Decrypt(cek, iv, ciphertext, tag, aad)
+}
+
+func NewGeneric(alg jwa.ContentEncryptionAlgorithm) (*Generic, error) {
+ c, err := cipher.NewAES(alg)
+ if err != nil {
+ return nil, errors.Wrap(err, `aes crypt: failed to create content cipher`)
+ }
+
+ return &Generic{
+ alg: alg,
+ cipher: c,
+ keysize: c.KeySize(),
+ tagsize: 16,
+ }, nil
+}
+
+func (c Generic) KeySize() int {
+ return c.keysize
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go
new file mode 100644
index 000000000..b12fd31a1
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/content_crypt/interface.go
@@ -0,0 +1,20 @@
+package content_crypt //nolint:golint
+
+import (
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/cipher"
+)
+
+// Generic encrypts a message by applying all the necessary
+// modifications to the keys and the contents
+type Generic struct {
+ alg jwa.ContentEncryptionAlgorithm
+ keysize int
+ tagsize int
+ cipher cipher.ContentCipher
+}
+
+type Cipher interface {
+ Decrypt([]byte, []byte, []byte, []byte, []byte) ([]byte, error)
+ KeySize() int
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go
new file mode 100644
index 000000000..4abad322b
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/interface.go
@@ -0,0 +1,104 @@
+package keyenc
+
+import (
+ "crypto/rsa"
+ "hash"
+
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/keygen"
+)
+
+// Encrypter is an interface for things that can encrypt keys
+type Encrypter interface {
+ Algorithm() jwa.KeyEncryptionAlgorithm
+ Encrypt([]byte) (keygen.ByteSource, error)
+ // KeyID returns the key id for this Encrypter. This exists so that
+ // you can pass in a Encrypter to MultiEncrypt, you can rest assured
+ // that the generated key will have the proper key ID.
+ KeyID() string
+}
+
+// Decrypter is an interface for things that can decrypt keys
+type Decrypter interface {
+ Algorithm() jwa.KeyEncryptionAlgorithm
+ Decrypt([]byte) ([]byte, error)
+}
+
+type Noop struct {
+ alg jwa.KeyEncryptionAlgorithm
+ keyID string
+ sharedkey []byte
+}
+
+// AES encrypts content encryption keys using AES key wrap.
+// Contrary to what the name implies, it also decrypt encrypted keys
+type AES struct {
+ alg jwa.KeyEncryptionAlgorithm
+ keyID string
+ sharedkey []byte
+}
+
+// AESGCM encrypts content encryption keys using AES-GCM key wrap.
+type AESGCMEncrypt struct {
+ algorithm jwa.KeyEncryptionAlgorithm
+ keyID string
+ sharedkey []byte
+}
+
+// ECDHESEncrypt encrypts content encryption keys using ECDH-ES.
+type ECDHESEncrypt struct {
+ algorithm jwa.KeyEncryptionAlgorithm
+ keyID string
+ generator keygen.Generator
+}
+
+// ECDHESDecrypt decrypts keys using ECDH-ES.
+type ECDHESDecrypt struct {
+ keyalg jwa.KeyEncryptionAlgorithm
+ contentalg jwa.ContentEncryptionAlgorithm
+ apu []byte
+ apv []byte
+ privkey interface{}
+ pubkey interface{}
+}
+
+// RSAOAEPEncrypt encrypts keys using RSA OAEP algorithm
+type RSAOAEPEncrypt struct {
+ alg jwa.KeyEncryptionAlgorithm
+ pubkey *rsa.PublicKey
+ keyID string
+}
+
+// RSAOAEPDecrypt decrypts keys using RSA OAEP algorithm
+type RSAOAEPDecrypt struct {
+ alg jwa.KeyEncryptionAlgorithm
+ privkey *rsa.PrivateKey
+}
+
+// RSAPKCS15Decrypt decrypts keys using RSA PKCS1v15 algorithm
+type RSAPKCS15Decrypt struct {
+ alg jwa.KeyEncryptionAlgorithm
+ privkey *rsa.PrivateKey
+ generator keygen.Generator
+}
+
+// RSAPKCSEncrypt encrypts keys using RSA PKCS1v15 algorithm
+type RSAPKCSEncrypt struct {
+ alg jwa.KeyEncryptionAlgorithm
+ pubkey *rsa.PublicKey
+ keyID string
+}
+
+// DirectDecrypt does no encryption (Note: Unimplemented)
+type DirectDecrypt struct {
+ Key []byte
+}
+
+// PBES2Encrypt encrypts keys with PBES2 / PBKDF2 password
+type PBES2Encrypt struct {
+ algorithm jwa.KeyEncryptionAlgorithm
+ hashFunc func() hash.Hash
+ keylen int
+ keyID string
+ password []byte
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go
new file mode 100644
index 000000000..ffa0033ba
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keyenc/keyenc.go
@@ -0,0 +1,633 @@
+package keyenc
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha1"
+ "crypto/sha256"
+ "crypto/sha512"
+ "crypto/subtle"
+ "encoding/binary"
+ "fmt"
+ "hash"
+ "io"
+
+ "golang.org/x/crypto/curve25519"
+ "golang.org/x/crypto/pbkdf2"
+
+ "github.com/lestrrat-go/jwx/internal/ecutil"
+ "github.com/lestrrat-go/jwx/jwa"
+ contentcipher "github.com/lestrrat-go/jwx/jwe/internal/cipher"
+ "github.com/lestrrat-go/jwx/jwe/internal/concatkdf"
+ "github.com/lestrrat-go/jwx/jwe/internal/keygen"
+ "github.com/lestrrat-go/jwx/x25519"
+ "github.com/pkg/errors"
+)
+
+func NewNoop(alg jwa.KeyEncryptionAlgorithm, sharedkey []byte) (*Noop, error) {
+ return &Noop{
+ alg: alg,
+ sharedkey: sharedkey,
+ }, nil
+}
+
+func (kw *Noop) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return kw.alg
+}
+
+func (kw *Noop) KeyID() string {
+ return kw.keyID
+}
+
+func (kw *Noop) Encrypt(cek []byte) (keygen.ByteSource, error) {
+ return keygen.ByteKey(kw.sharedkey), nil
+}
+
+// NewAES creates a key-wrap encrypter using AES.
+// Although the name suggests otherwise, this does the decryption as well.
+func NewAES(alg jwa.KeyEncryptionAlgorithm, sharedkey []byte) (*AES, error) {
+ return &AES{
+ alg: alg,
+ sharedkey: sharedkey,
+ }, nil
+}
+
+// Algorithm returns the key encryption algorithm being used
+func (kw *AES) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return kw.alg
+}
+
+// KeyID returns the key ID associated with this encrypter
+func (kw *AES) KeyID() string {
+ return kw.keyID
+}
+
+// Decrypt decrypts the encrypted key using AES key unwrap
+func (kw *AES) Decrypt(enckey []byte) ([]byte, error) {
+ block, err := aes.NewCipher(kw.sharedkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create cipher from shared key")
+ }
+
+ cek, err := Unwrap(block, enckey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to unwrap data")
+ }
+ return cek, nil
+}
+
+// KeyEncrypt encrypts the given content encryption key
+func (kw *AES) Encrypt(cek []byte) (keygen.ByteSource, error) {
+ block, err := aes.NewCipher(kw.sharedkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create cipher from shared key")
+ }
+ encrypted, err := Wrap(block, cek)
+ if err != nil {
+ return nil, errors.Wrap(err, `keywrap: failed to wrap key`)
+ }
+ return keygen.ByteKey(encrypted), nil
+}
+
+func NewAESGCMEncrypt(alg jwa.KeyEncryptionAlgorithm, sharedkey []byte) (*AESGCMEncrypt, error) {
+ return &AESGCMEncrypt{
+ algorithm: alg,
+ sharedkey: sharedkey,
+ }, nil
+}
+
+func (kw AESGCMEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return kw.algorithm
+}
+
+func (kw AESGCMEncrypt) KeyID() string {
+ return kw.keyID
+}
+
+func (kw AESGCMEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
+ block, err := aes.NewCipher(kw.sharedkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create cipher from shared key")
+ }
+ aesgcm, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create gcm from cipher")
+ }
+
+ iv := make([]byte, aesgcm.NonceSize())
+ _, err = io.ReadFull(rand.Reader, iv)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get random iv")
+ }
+
+ encrypted := aesgcm.Seal(nil, iv, cek, nil)
+ tag := encrypted[len(encrypted)-aesgcm.Overhead():]
+ ciphertext := encrypted[:len(encrypted)-aesgcm.Overhead()]
+ return keygen.ByteWithIVAndTag{
+ ByteKey: ciphertext,
+ IV: iv,
+ Tag: tag,
+ }, nil
+}
+
+func NewPBES2Encrypt(alg jwa.KeyEncryptionAlgorithm, password []byte) (*PBES2Encrypt, error) {
+ var hashFunc func() hash.Hash
+ var keylen int
+ switch alg {
+ case jwa.PBES2_HS256_A128KW:
+ hashFunc = sha256.New
+ keylen = 16
+ case jwa.PBES2_HS384_A192KW:
+ hashFunc = sha512.New384
+ keylen = 24
+ case jwa.PBES2_HS512_A256KW:
+ hashFunc = sha512.New
+ keylen = 32
+ default:
+ return nil, errors.Errorf("unexpected key encryption algorithm %s", alg)
+ }
+ return &PBES2Encrypt{
+ algorithm: alg,
+ password: password,
+ hashFunc: hashFunc,
+ keylen: keylen,
+ }, nil
+}
+
+func (kw PBES2Encrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return kw.algorithm
+}
+
+func (kw PBES2Encrypt) KeyID() string {
+ return kw.keyID
+}
+
+func (kw PBES2Encrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
+ count := 10000
+ salt := make([]byte, kw.keylen)
+ _, err := io.ReadFull(rand.Reader, salt)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get random salt")
+ }
+
+ fullsalt := []byte(kw.algorithm)
+ fullsalt = append(fullsalt, byte(0))
+ fullsalt = append(fullsalt, salt...)
+ sharedkey := pbkdf2.Key(kw.password, fullsalt, count, kw.keylen, kw.hashFunc)
+
+ block, err := aes.NewCipher(sharedkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create cipher from shared key")
+ }
+ encrypted, err := Wrap(block, cek)
+ if err != nil {
+ return nil, errors.Wrap(err, `keywrap: failed to wrap key`)
+ }
+ return keygen.ByteWithSaltAndCount{
+ ByteKey: encrypted,
+ Salt: salt,
+ Count: count,
+ }, nil
+}
+
+// NewECDHESEncrypt creates a new key encrypter based on ECDH-ES
+func NewECDHESEncrypt(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, keyif interface{}) (*ECDHESEncrypt, error) {
+ var generator keygen.Generator
+ var err error
+ switch key := keyif.(type) {
+ case *ecdsa.PublicKey:
+ generator, err = keygen.NewEcdhes(alg, enc, keysize, key)
+ case x25519.PublicKey:
+ generator, err = keygen.NewX25519(alg, enc, keysize, key)
+ default:
+ return nil, errors.Errorf("unexpected key type %T", keyif)
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create key generator")
+ }
+ return &ECDHESEncrypt{
+ algorithm: alg,
+ generator: generator,
+ }, nil
+}
+
+// Algorithm returns the key encryption algorithm being used
+func (kw ECDHESEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return kw.algorithm
+}
+
+// KeyID returns the key ID associated with this encrypter
+func (kw ECDHESEncrypt) KeyID() string {
+ return kw.keyID
+}
+
+// KeyEncrypt encrypts the content encryption key using ECDH-ES
+func (kw ECDHESEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
+ kg, err := kw.generator.Generate()
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create key generator")
+ }
+
+ bwpk, ok := kg.(keygen.ByteWithECPublicKey)
+ if !ok {
+ return nil, errors.New("key generator generated invalid key (expected ByteWithECPrivateKey)")
+ }
+
+ if kw.algorithm == jwa.ECDH_ES {
+ return bwpk, nil
+ }
+
+ block, err := aes.NewCipher(bwpk.Bytes())
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate cipher from generated key")
+ }
+
+ jek, err := Wrap(block, cek)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to wrap data")
+ }
+
+ bwpk.ByteKey = keygen.ByteKey(jek)
+
+ return bwpk, nil
+}
+
+// NewECDHESDecrypt creates a new key decrypter using ECDH-ES
+func NewECDHESDecrypt(keyalg jwa.KeyEncryptionAlgorithm, contentalg jwa.ContentEncryptionAlgorithm, pubkey interface{}, apu, apv []byte, privkey interface{}) *ECDHESDecrypt {
+ return &ECDHESDecrypt{
+ keyalg: keyalg,
+ contentalg: contentalg,
+ apu: apu,
+ apv: apv,
+ privkey: privkey,
+ pubkey: pubkey,
+ }
+}
+
+// Algorithm returns the key encryption algorithm being used
+func (kw ECDHESDecrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return kw.keyalg
+}
+
+func DeriveZ(privkeyif interface{}, pubkeyif interface{}) ([]byte, error) {
+ switch privkeyif.(type) {
+ case x25519.PrivateKey:
+ privkey, ok := privkeyif.(x25519.PrivateKey)
+ if !ok {
+ return nil, errors.Errorf(`private key must be x25519.PrivateKey, was: %T`, privkeyif)
+ }
+ pubkey, ok := pubkeyif.(x25519.PublicKey)
+ if !ok {
+ return nil, errors.Errorf(`public key must be x25519.PublicKey, was: %T`, pubkeyif)
+ }
+ return curve25519.X25519(privkey.Seed(), pubkey)
+ default:
+ privkey, ok := privkeyif.(*ecdsa.PrivateKey)
+ if !ok {
+ return nil, errors.Errorf(`private key must be *ecdsa.PrivateKey, was: %T`, privkeyif)
+ }
+ pubkey, ok := pubkeyif.(*ecdsa.PublicKey)
+ if !ok {
+ return nil, errors.Errorf(`public key must be *ecdsa.PublicKey, was: %T`, pubkeyif)
+ }
+ if !privkey.PublicKey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
+ return nil, errors.New(`public key must be on the same curve as private key`)
+ }
+
+ z, _ := privkey.PublicKey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes())
+ zBytes := ecutil.AllocECPointBuffer(z, privkey.Curve)
+ defer ecutil.ReleaseECPointBuffer(zBytes)
+ zCopy := make([]byte, len(zBytes))
+ copy(zCopy, zBytes)
+ return zCopy, nil
+ }
+}
+
+func DeriveECDHES(alg, apu, apv []byte, privkey interface{}, pubkey interface{}, keysize uint32) ([]byte, error) {
+ pubinfo := make([]byte, 4)
+ binary.BigEndian.PutUint32(pubinfo, keysize*8)
+ zBytes, err := DeriveZ(privkey, pubkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "unable to determine Z")
+ }
+ kdf := concatkdf.New(crypto.SHA256, alg, zBytes, apu, apv, pubinfo, []byte{})
+ key := make([]byte, keysize)
+ if _, err := kdf.Read(key); err != nil {
+ return nil, errors.Wrap(err, "failed to read kdf")
+ }
+
+ return key, nil
+}
+
+// Decrypt decrypts the encrypted key using ECDH-ES
+func (kw ECDHESDecrypt) Decrypt(enckey []byte) ([]byte, error) {
+ var algBytes []byte
+ var keysize uint32
+
+ // Use keyalg except for when jwa.ECDH_ES
+ algBytes = []byte(kw.keyalg.String())
+
+ switch kw.keyalg {
+ case jwa.ECDH_ES:
+ // Create a content cipher from the content encryption algorithm
+ c, err := contentcipher.NewAES(kw.contentalg)
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to create content cipher for %s`, kw.contentalg)
+ }
+ keysize = uint32(c.KeySize())
+ algBytes = []byte(kw.contentalg.String())
+ case jwa.ECDH_ES_A128KW:
+ keysize = 16
+ case jwa.ECDH_ES_A192KW:
+ keysize = 24
+ case jwa.ECDH_ES_A256KW:
+ keysize = 32
+ default:
+ return nil, errors.Errorf("invalid ECDH-ES key wrap algorithm (%s)", kw.keyalg)
+ }
+
+ key, err := DeriveECDHES(algBytes, kw.apu, kw.apv, kw.privkey, kw.pubkey, keysize)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to derive ECDHES encryption key`)
+ }
+
+ // ECDH-ES does not wrap keys
+ if kw.keyalg == jwa.ECDH_ES {
+ return key, nil
+ }
+
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create cipher for ECDH-ES key wrap")
+ }
+
+ return Unwrap(block, enckey)
+}
+
+// NewRSAOAEPEncrypt creates a new key encrypter using RSA OAEP
+func NewRSAOAEPEncrypt(alg jwa.KeyEncryptionAlgorithm, pubkey *rsa.PublicKey) (*RSAOAEPEncrypt, error) {
+ switch alg {
+ case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
+ default:
+ return nil, errors.Errorf("invalid RSA OAEP encrypt algorithm (%s)", alg)
+ }
+ return &RSAOAEPEncrypt{
+ alg: alg,
+ pubkey: pubkey,
+ }, nil
+}
+
+// NewRSAPKCSEncrypt creates a new key encrypter using PKCS1v15
+func NewRSAPKCSEncrypt(alg jwa.KeyEncryptionAlgorithm, pubkey *rsa.PublicKey) (*RSAPKCSEncrypt, error) {
+ switch alg {
+ case jwa.RSA1_5:
+ default:
+ return nil, errors.Errorf("invalid RSA PKCS encrypt algorithm (%s)", alg)
+ }
+
+ return &RSAPKCSEncrypt{
+ alg: alg,
+ pubkey: pubkey,
+ }, nil
+}
+
+// Algorithm returns the key encryption algorithm being used
+func (e RSAPKCSEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return e.alg
+}
+
+// KeyID returns the key ID associated with this encrypter
+func (e RSAPKCSEncrypt) KeyID() string {
+ return e.keyID
+}
+
+// Algorithm returns the key encryption algorithm being used
+func (e RSAOAEPEncrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return e.alg
+}
+
+// KeyID returns the key ID associated with this encrypter
+func (e RSAOAEPEncrypt) KeyID() string {
+ return e.keyID
+}
+
+// KeyEncrypt encrypts the content encryption key using RSA PKCS1v15
+func (e RSAPKCSEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
+ if e.alg != jwa.RSA1_5 {
+ return nil, errors.Errorf("invalid RSA PKCS encrypt algorithm (%s)", e.alg)
+ }
+ encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, e.pubkey, cek)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to encrypt using PKCS1v15")
+ }
+ return keygen.ByteKey(encrypted), nil
+}
+
+// KeyEncrypt encrypts the content encryption key using RSA OAEP
+func (e RSAOAEPEncrypt) Encrypt(cek []byte) (keygen.ByteSource, error) {
+ var hash hash.Hash
+ switch e.alg {
+ case jwa.RSA_OAEP:
+ hash = sha1.New()
+ case jwa.RSA_OAEP_256:
+ hash = sha256.New()
+ default:
+ return nil, errors.New("failed to generate key encrypter for RSA-OAEP: RSA_OAEP/RSA_OAEP_256 required")
+ }
+ encrypted, err := rsa.EncryptOAEP(hash, rand.Reader, e.pubkey, cek, []byte{})
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to OAEP encrypt`)
+ }
+ return keygen.ByteKey(encrypted), nil
+}
+
+// NewRSAPKCS15Decrypt creates a new decrypter using RSA PKCS1v15
+func NewRSAPKCS15Decrypt(alg jwa.KeyEncryptionAlgorithm, privkey *rsa.PrivateKey, keysize int) *RSAPKCS15Decrypt {
+ generator := keygen.NewRandom(keysize * 2)
+ return &RSAPKCS15Decrypt{
+ alg: alg,
+ privkey: privkey,
+ generator: generator,
+ }
+}
+
+// Algorithm returns the key encryption algorithm being used
+func (d RSAPKCS15Decrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return d.alg
+}
+
+// Decrypt decrypts the encrypted key using RSA PKCS1v1.5
+func (d RSAPKCS15Decrypt) Decrypt(enckey []byte) ([]byte, error) {
+ // Hey, these notes and workarounds were stolen from go-jose
+ defer func() {
+ // DecryptPKCS1v15SessionKey sometimes panics on an invalid payload
+ // because of an index out of bounds error, which we want to ignore.
+ // This has been fixed in Go 1.3.1 (released 2014/08/13), the recover()
+ // only exists for preventing crashes with unpatched versions.
+ // See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k
+ // See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33
+ _ = recover()
+ }()
+
+ // Perform some input validation.
+ expectedlen := d.privkey.PublicKey.N.BitLen() / 8
+ if expectedlen != len(enckey) {
+ // Input size is incorrect, the encrypted payload should always match
+ // the size of the public modulus (e.g. using a 2048 bit key will
+ // produce 256 bytes of output). Reject this since it's invalid input.
+ return nil, fmt.Errorf(
+ "input size for key decrypt is incorrect (expected %d, got %d)",
+ expectedlen,
+ len(enckey),
+ )
+ }
+
+ var err error
+
+ bk, err := d.generator.Generate()
+ if err != nil {
+ return nil, errors.New("failed to generate key")
+ }
+ cek := bk.Bytes()
+
+ // When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to
+ // prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing
+ // the Million Message Attack on Cryptographic Message Syntax". We are
+ // therefore deliberately ignoring errors here.
+ err = rsa.DecryptPKCS1v15SessionKey(rand.Reader, d.privkey, enckey, cek)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to decrypt via PKCS1v15")
+ }
+
+ return cek, nil
+}
+
+// NewRSAOAEPDecrypt creates a new key decrypter using RSA OAEP
+func NewRSAOAEPDecrypt(alg jwa.KeyEncryptionAlgorithm, privkey *rsa.PrivateKey) (*RSAOAEPDecrypt, error) {
+ switch alg {
+ case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
+ default:
+ return nil, errors.Errorf("invalid RSA OAEP decrypt algorithm (%s)", alg)
+ }
+
+ return &RSAOAEPDecrypt{
+ alg: alg,
+ privkey: privkey,
+ }, nil
+}
+
+// Algorithm returns the key encryption algorithm being used
+func (d RSAOAEPDecrypt) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return d.alg
+}
+
+// Decrypt decrypts the encrypted key using RSA OAEP
+func (d RSAOAEPDecrypt) Decrypt(enckey []byte) ([]byte, error) {
+ var hash hash.Hash
+ switch d.alg {
+ case jwa.RSA_OAEP:
+ hash = sha1.New()
+ case jwa.RSA_OAEP_256:
+ hash = sha256.New()
+ default:
+ return nil, errors.New("failed to generate key encrypter for RSA-OAEP: RSA_OAEP/RSA_OAEP_256 required")
+ }
+ return rsa.DecryptOAEP(hash, rand.Reader, d.privkey, enckey, []byte{})
+}
+
+// Decrypt for DirectDecrypt does not do anything other than
+// return a copy of the embedded key
+func (d DirectDecrypt) Decrypt() ([]byte, error) {
+ cek := make([]byte, len(d.Key))
+ copy(cek, d.Key)
+ return cek, nil
+}
+
+var keywrapDefaultIV = []byte{0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}
+
+const keywrapChunkLen = 8
+
+func Wrap(kek cipher.Block, cek []byte) ([]byte, error) {
+ if len(cek)%8 != 0 {
+ return nil, errors.New(`keywrap input must be 8 byte blocks`)
+ }
+
+ n := len(cek) / keywrapChunkLen
+ r := make([][]byte, n)
+
+ for i := 0; i < n; i++ {
+ r[i] = make([]byte, keywrapChunkLen)
+ copy(r[i], cek[i*keywrapChunkLen:])
+ }
+
+ buffer := make([]byte, keywrapChunkLen*2)
+ tBytes := make([]byte, keywrapChunkLen)
+ copy(buffer, keywrapDefaultIV)
+
+ for t := 0; t < 6*n; t++ {
+ copy(buffer[keywrapChunkLen:], r[t%n])
+
+ kek.Encrypt(buffer, buffer)
+
+ binary.BigEndian.PutUint64(tBytes, uint64(t+1))
+
+ for i := 0; i < keywrapChunkLen; i++ {
+ buffer[i] = buffer[i] ^ tBytes[i]
+ }
+ copy(r[t%n], buffer[keywrapChunkLen:])
+ }
+
+ out := make([]byte, (n+1)*keywrapChunkLen)
+ copy(out, buffer[:keywrapChunkLen])
+ for i := range r {
+ copy(out[(i+1)*8:], r[i])
+ }
+
+ return out, nil
+}
+
+func Unwrap(block cipher.Block, ciphertxt []byte) ([]byte, error) {
+ if len(ciphertxt)%keywrapChunkLen != 0 {
+ return nil, errors.Errorf(`keyunwrap input must be %d byte blocks`, keywrapChunkLen)
+ }
+
+ n := (len(ciphertxt) / keywrapChunkLen) - 1
+ r := make([][]byte, n)
+
+ for i := range r {
+ r[i] = make([]byte, keywrapChunkLen)
+ copy(r[i], ciphertxt[(i+1)*keywrapChunkLen:])
+ }
+
+ buffer := make([]byte, keywrapChunkLen*2)
+ tBytes := make([]byte, keywrapChunkLen)
+ copy(buffer[:keywrapChunkLen], ciphertxt[:keywrapChunkLen])
+
+ for t := 6*n - 1; t >= 0; t-- {
+ binary.BigEndian.PutUint64(tBytes, uint64(t+1))
+
+ for i := 0; i < keywrapChunkLen; i++ {
+ buffer[i] = buffer[i] ^ tBytes[i]
+ }
+ copy(buffer[keywrapChunkLen:], r[t%n])
+
+ block.Decrypt(buffer, buffer)
+
+ copy(r[t%n], buffer[keywrapChunkLen:])
+ }
+
+ if subtle.ConstantTimeCompare(buffer[:keywrapChunkLen], keywrapDefaultIV) == 0 {
+ return nil, errors.New("key unwrap: failed to unwrap key")
+ }
+
+ out := make([]byte, n*keywrapChunkLen)
+ for i := range r {
+ copy(out[i*keywrapChunkLen:], r[i])
+ }
+
+ return out, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go
new file mode 100644
index 000000000..0114abe53
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/interface.go
@@ -0,0 +1,73 @@
+package keygen
+
+import (
+ "crypto/ecdsa"
+
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/x25519"
+)
+
+type Generator interface {
+ Size() int
+ Generate() (ByteSource, error)
+}
+
+// StaticKeyGenerate uses a static byte buffer to provide keys.
+type Static []byte
+
+// RandomKeyGenerate generates random keys
+type Random struct {
+ keysize int
+}
+
+// EcdhesKeyGenerate generates keys using ECDH-ES algorithm / EC-DSA curve
+type Ecdhes struct {
+ pubkey *ecdsa.PublicKey
+ keysize int
+ algorithm jwa.KeyEncryptionAlgorithm
+ enc jwa.ContentEncryptionAlgorithm
+}
+
+// X25519KeyGenerate generates keys using ECDH-ES algorithm / X25519 curve
+type X25519 struct {
+ algorithm jwa.KeyEncryptionAlgorithm
+ enc jwa.ContentEncryptionAlgorithm
+ keysize int
+ pubkey x25519.PublicKey
+}
+
+// ByteKey is a generated key that only has the key's byte buffer
+// as its instance data. If a key needs to do more, such as providing
+// values to be set in a JWE header, that key type wraps a ByteKey
+type ByteKey []byte
+
+// ByteWithECPublicKey holds the EC private key that generated
+// the key along with the key itself. This is required to set the
+// proper values in the JWE headers
+type ByteWithECPublicKey struct {
+ ByteKey
+ PublicKey interface{}
+}
+
+type ByteWithIVAndTag struct {
+ ByteKey
+ IV []byte
+ Tag []byte
+}
+
+type ByteWithSaltAndCount struct {
+ ByteKey
+ Salt []byte
+ Count int
+}
+
+// ByteSource is an interface for things that return a byte sequence.
+// This is used for KeyGenerator so that the result of computations can
+// carry more than just the generate byte sequence.
+type ByteSource interface {
+ Bytes() []byte
+}
+
+type Setter interface {
+ Set(string, interface{}) error
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go
new file mode 100644
index 000000000..f15e2a139
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/internal/keygen/keygen.go
@@ -0,0 +1,192 @@
+package keygen
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "encoding/binary"
+ "io"
+
+ "golang.org/x/crypto/curve25519"
+
+ "github.com/lestrrat-go/jwx/internal/ecutil"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/concatkdf"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/lestrrat-go/jwx/x25519"
+ "github.com/pkg/errors"
+)
+
+// Bytes returns the byte from this ByteKey
+func (k ByteKey) Bytes() []byte {
+ return []byte(k)
+}
+
+// Size returns the size of the key
+func (g Static) Size() int {
+ return len(g)
+}
+
+// Generate returns the key
+func (g Static) Generate() (ByteSource, error) {
+ buf := make([]byte, g.Size())
+ copy(buf, g)
+ return ByteKey(buf), nil
+}
+
+// NewRandom creates a new Generator that returns
+// random bytes
+func NewRandom(n int) Random {
+ return Random{keysize: n}
+}
+
+// Size returns the key size
+func (g Random) Size() int {
+ return g.keysize
+}
+
+// Generate generates a random new key
+func (g Random) Generate() (ByteSource, error) {
+ buf := make([]byte, g.keysize)
+ if _, err := io.ReadFull(rand.Reader, buf); err != nil {
+ return nil, errors.Wrap(err, "failed to read from rand.Reader")
+ }
+ return ByteKey(buf), nil
+}
+
+// NewEcdhes creates a new key generator using ECDH-ES
+func NewEcdhes(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey *ecdsa.PublicKey) (*Ecdhes, error) {
+ return &Ecdhes{
+ algorithm: alg,
+ enc: enc,
+ keysize: keysize,
+ pubkey: pubkey,
+ }, nil
+}
+
+// Size returns the key size associated with this generator
+func (g Ecdhes) Size() int {
+ return g.keysize
+}
+
+// Generate generates new keys using ECDH-ES
+func (g Ecdhes) Generate() (ByteSource, error) {
+ priv, err := ecdsa.GenerateKey(g.pubkey.Curve, rand.Reader)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate key for ECDH-ES")
+ }
+
+ var algorithm string
+ if g.algorithm == jwa.ECDH_ES {
+ algorithm = g.enc.String()
+ } else {
+ algorithm = g.algorithm.String()
+ }
+
+ pubinfo := make([]byte, 4)
+ binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
+
+ z, _ := priv.PublicKey.Curve.ScalarMult(g.pubkey.X, g.pubkey.Y, priv.D.Bytes())
+ zBytes := ecutil.AllocECPointBuffer(z, priv.PublicKey.Curve)
+ defer ecutil.ReleaseECPointBuffer(zBytes)
+ kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{})
+ kek := make([]byte, g.keysize)
+ if _, err := kdf.Read(kek); err != nil {
+ return nil, errors.Wrap(err, "failed to read kdf")
+ }
+
+ return ByteWithECPublicKey{
+ PublicKey: &priv.PublicKey,
+ ByteKey: ByteKey(kek),
+ }, nil
+}
+
+// NewX25519 creates a new key generator using ECDH-ES
+func NewX25519(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey x25519.PublicKey) (*X25519, error) {
+ return &X25519{
+ algorithm: alg,
+ enc: enc,
+ keysize: keysize,
+ pubkey: pubkey,
+ }, nil
+}
+
+// Size returns the key size associated with this generator
+func (g X25519) Size() int {
+ return g.keysize
+}
+
+// Generate generates new keys using ECDH-ES
+func (g X25519) Generate() (ByteSource, error) {
+ pub, priv, err := x25519.GenerateKey(rand.Reader)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to generate key for X25519")
+ }
+
+ var algorithm string
+ if g.algorithm == jwa.ECDH_ES {
+ algorithm = g.enc.String()
+ } else {
+ algorithm = g.algorithm.String()
+ }
+
+ pubinfo := make([]byte, 4)
+ binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
+
+ zBytes, err := curve25519.X25519(priv.Seed(), g.pubkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to compute Z")
+ }
+ kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{})
+ kek := make([]byte, g.keysize)
+ if _, err := kdf.Read(kek); err != nil {
+ return nil, errors.Wrap(err, "failed to read kdf")
+ }
+
+ return ByteWithECPublicKey{
+ PublicKey: pub,
+ ByteKey: ByteKey(kek),
+ }, nil
+}
+
+// HeaderPopulate populates the header with the required EC-DSA public key
+// information ('epk' key)
+func (k ByteWithECPublicKey) Populate(h Setter) error {
+ key, err := jwk.New(k.PublicKey)
+ if err != nil {
+ return errors.Wrap(err, "failed to create JWK")
+ }
+
+ if err := h.Set("epk", key); err != nil {
+ return errors.Wrap(err, "failed to write header")
+ }
+ return nil
+}
+
+// HeaderPopulate populates the header with the required AES GCM
+// parameters ('iv' and 'tag')
+func (k ByteWithIVAndTag) Populate(h Setter) error {
+ if err := h.Set("iv", k.IV); err != nil {
+ return errors.Wrap(err, "failed to write header")
+ }
+
+ if err := h.Set("tag", k.Tag); err != nil {
+ return errors.Wrap(err, "failed to write header")
+ }
+
+ return nil
+}
+
+// HeaderPopulate populates the header with the required PBES2
+// parameters ('p2s' and 'p2c')
+func (k ByteWithSaltAndCount) Populate(h Setter) error {
+ if err := h.Set("p2c", k.Count); err != nil {
+ return errors.Wrap(err, "failed to write header")
+ }
+
+ if err := h.Set("p2s", k.Salt); err != nil {
+ return errors.Wrap(err, "failed to write header")
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/io.go b/vendor/github.com/lestrrat-go/jwx/jwe/io.go
new file mode 100644
index 000000000..031f6106d
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/io.go
@@ -0,0 +1,23 @@
+// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT
+
+package jwe
+
+import "os"
+
+// ReadFileOption describes options that can be passed to ReadFile.
+// Currently there are no options available that can be passed to ReadFile, but
+// it is provided here for anticipated future additions
+type ReadFileOption interface {
+ Option
+ readFileOption()
+}
+
+func ReadFile(path string, _ ...ReadFileOption) (*Message, error) {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+
+ defer f.Close()
+ return ParseReader(f)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go b/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go
new file mode 100644
index 000000000..18d9f47ea
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/jwe.go
@@ -0,0 +1,370 @@
+//go:generate ./gen.sh
+
+// Package jwe implements JWE as described in https://tools.ietf.org/html/rfc7516
+package jwe
+
+import (
+ "bytes"
+ "crypto/ecdsa"
+ "crypto/rsa"
+ "io"
+ "io/ioutil"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/internal/keyconv"
+ "github.com/lestrrat-go/jwx/jwk"
+
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe/internal/content_crypt"
+ "github.com/lestrrat-go/jwx/jwe/internal/keyenc"
+ "github.com/lestrrat-go/jwx/jwe/internal/keygen"
+ "github.com/lestrrat-go/jwx/x25519"
+ "github.com/pkg/errors"
+)
+
+var registry = json.NewRegistry()
+
+// Encrypt takes the plaintext payload and encrypts it in JWE compact format.
+// `key` should be a public key, and it may be a raw key (e.g. rsa.PublicKey) or a jwk.Key
+//
+// Encrypt currently does not support multi-recipient messages.
+func Encrypt(payload []byte, keyalg jwa.KeyEncryptionAlgorithm, key interface{}, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm, options ...EncryptOption) ([]byte, error) {
+ var protected Headers
+ for _, option := range options {
+ //nolint:forcetypeassert
+ switch option.Ident() {
+ case identProtectedHeader{}:
+ protected = option.Value().(Headers)
+ }
+ }
+ if protected == nil {
+ protected = NewHeaders()
+ }
+
+ contentcrypt, err := content_crypt.NewGeneric(contentalg)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to create AES encrypter`)
+ }
+
+ if jwkKey, ok := key.(jwk.Key); ok {
+ var raw interface{}
+ if err := jwkKey.Raw(&raw); err != nil {
+ return nil, errors.Wrapf(err, `failed to retrieve raw key out of %T`, key)
+ }
+
+ key = raw
+ }
+
+ var enc keyenc.Encrypter
+ switch keyalg {
+ case jwa.RSA1_5:
+ var pubkey rsa.PublicKey
+ if err := keyconv.RSAPublicKey(&pubkey, key); err != nil {
+ return nil, errors.Wrapf(err, "failed to generate public key from key (%T)", key)
+ }
+
+ enc, err = keyenc.NewRSAPKCSEncrypt(keyalg, &pubkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create RSA PKCS encrypter")
+ }
+ case jwa.RSA_OAEP, jwa.RSA_OAEP_256:
+ var pubkey rsa.PublicKey
+ if err := keyconv.RSAPublicKey(&pubkey, key); err != nil {
+ return nil, errors.Wrapf(err, "failed to generate public key from key (%T)", key)
+ }
+
+ enc, err = keyenc.NewRSAOAEPEncrypt(keyalg, &pubkey)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create RSA OAEP encrypter")
+ }
+ case jwa.A128KW, jwa.A192KW, jwa.A256KW,
+ jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW,
+ jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
+ sharedkey, ok := key.([]byte)
+ if !ok {
+ return nil, errors.New("invalid key: []byte required")
+ }
+ switch keyalg {
+ case jwa.A128KW, jwa.A192KW, jwa.A256KW:
+ enc, err = keyenc.NewAES(keyalg, sharedkey)
+ case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
+ enc, err = keyenc.NewPBES2Encrypt(keyalg, sharedkey)
+ default:
+ enc, err = keyenc.NewAESGCMEncrypt(keyalg, sharedkey)
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create key wrap encrypter")
+ }
+ // NOTE: there was formerly a restriction, introduced
+ // in PR #26, which disallowed certain key/content
+ // algorithm combinations. This seemed bogus, and
+ // interop with the jose tool demonstrates it.
+ case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
+ var keysize int
+ switch keyalg {
+ case jwa.ECDH_ES:
+ // https://tools.ietf.org/html/rfc7518#page-15
+ // In Direct Key Agreement mode, the output of the Concat KDF MUST be a
+ // key of the same length as that used by the "enc" algorithm.
+ keysize = contentcrypt.KeySize()
+ case jwa.ECDH_ES_A128KW:
+ keysize = 16
+ case jwa.ECDH_ES_A192KW:
+ keysize = 24
+ case jwa.ECDH_ES_A256KW:
+ keysize = 32
+ }
+
+ switch key := key.(type) {
+ case x25519.PublicKey:
+ enc, err = keyenc.NewECDHESEncrypt(keyalg, contentalg, keysize, key)
+ default:
+ var pubkey ecdsa.PublicKey
+ if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil {
+ return nil, errors.Wrapf(err, "failed to generate public key from key (%T)", key)
+ }
+ enc, err = keyenc.NewECDHESEncrypt(keyalg, contentalg, keysize, &pubkey)
+ }
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create ECDHS key wrap encrypter")
+ }
+ case jwa.DIRECT:
+ sharedkey, ok := key.([]byte)
+ if !ok {
+ return nil, errors.New("invalid key: []byte required")
+ }
+ enc, _ = keyenc.NewNoop(keyalg, sharedkey)
+ default:
+ return nil, errors.Errorf(`invalid key encryption algorithm (%s)`, keyalg)
+ }
+
+ keysize := contentcrypt.KeySize()
+ encctx := getEncryptCtx()
+ defer releaseEncryptCtx(encctx)
+
+ encctx.protected = protected
+ encctx.contentEncrypter = contentcrypt
+ encctx.generator = keygen.NewRandom(keysize)
+ encctx.keyEncrypters = []keyenc.Encrypter{enc}
+ encctx.compress = compressalg
+ msg, err := encctx.Encrypt(payload)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to encrypt payload")
+ }
+
+ return Compact(msg)
+}
+
+// DecryptCtx is used internally when jwe.Decrypt is called, and is
+// passed for hooks that you may pass into it.
+//
+// Regular users should not have to touch this object, but if you need advanced handling
+// of messages, you might have to use it. Only use it when you really
+// understand how JWE processing works in this library.
+type DecryptCtx interface {
+ Algorithm() jwa.KeyEncryptionAlgorithm
+ SetAlgorithm(jwa.KeyEncryptionAlgorithm)
+ Key() interface{}
+ SetKey(interface{})
+ Message() *Message
+ SetMessage(*Message)
+}
+
+type decryptCtx struct {
+ alg jwa.KeyEncryptionAlgorithm
+ key interface{}
+ msg *Message
+}
+
+func (ctx *decryptCtx) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return ctx.alg
+}
+
+func (ctx *decryptCtx) SetAlgorithm(v jwa.KeyEncryptionAlgorithm) {
+ ctx.alg = v
+}
+
+func (ctx *decryptCtx) Key() interface{} {
+ return ctx.key
+}
+
+func (ctx *decryptCtx) SetKey(v interface{}) {
+ ctx.key = v
+}
+
+func (ctx *decryptCtx) Message() *Message {
+ return ctx.msg
+}
+
+func (ctx *decryptCtx) SetMessage(m *Message) {
+ ctx.msg = m
+}
+
+// Decrypt takes the key encryption algorithm and the corresponding
+// key to decrypt the JWE message, and returns the decrypted payload.
+// The JWE message can be either compact or full JSON format.
+//
+// `key` must be a private key. It can be either in its raw format (e.g. *rsa.PrivateKey) or a jwk.Key
+func Decrypt(buf []byte, alg jwa.KeyEncryptionAlgorithm, key interface{}, options ...DecryptOption) ([]byte, error) {
+ var ctx decryptCtx
+ ctx.key = key
+ ctx.alg = alg
+
+ var dst *Message
+ var postParse PostParser
+ //nolint:forcetypeassert
+ for _, option := range options {
+ switch option.Ident() {
+ case identMessage{}:
+ dst = option.Value().(*Message)
+ case identPostParser{}:
+ postParse = option.Value().(PostParser)
+ }
+ }
+
+ msg, err := parseJSONOrCompact(buf, true)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to parse buffer for Decrypt")
+ }
+
+ ctx.msg = msg
+ if postParse != nil {
+ if err := postParse.PostParse(&ctx); err != nil {
+ return nil, errors.Wrap(err, `failed to execute PostParser hook`)
+ }
+ }
+
+ payload, err := doDecryptCtx(&ctx)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decrypt message`)
+ }
+
+ if dst != nil {
+ *dst = *msg
+ dst.rawProtectedHeaders = nil
+ dst.storeProtectedHeaders = false
+ }
+
+ return payload, nil
+}
+
+// Parse parses the JWE message into a Message object. The JWE message
+// can be either compact or full JSON format.
+func Parse(buf []byte) (*Message, error) {
+ return parseJSONOrCompact(buf, false)
+}
+
+func parseJSONOrCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) {
+ buf = bytes.TrimSpace(buf)
+ if len(buf) == 0 {
+ return nil, errors.New("empty buffer")
+ }
+
+ if buf[0] == '{' {
+ return parseJSON(buf, storeProtectedHeaders)
+ }
+ return parseCompact(buf, storeProtectedHeaders)
+}
+
+// ParseString is the same as Parse, but takes a string.
+func ParseString(s string) (*Message, error) {
+ return Parse([]byte(s))
+}
+
+// ParseReader is the same as Parse, but takes an io.Reader.
+func ParseReader(src io.Reader) (*Message, error) {
+ buf, err := ioutil.ReadAll(src)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to read from io.Reader`)
+ }
+ return Parse(buf)
+}
+
+func parseJSON(buf []byte, storeProtectedHeaders bool) (*Message, error) {
+ m := NewMessage()
+ m.storeProtectedHeaders = storeProtectedHeaders
+ if err := json.Unmarshal(buf, &m); err != nil {
+ return nil, errors.Wrap(err, "failed to parse JSON")
+ }
+ return m, nil
+}
+
+func parseCompact(buf []byte, storeProtectedHeaders bool) (*Message, error) {
+ parts := bytes.Split(buf, []byte{'.'})
+ if len(parts) != 5 {
+ return nil, errors.Errorf(`compact JWE format must have five parts (%d)`, len(parts))
+ }
+
+ hdrbuf, err := base64.Decode(parts[0])
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to parse first part of compact form`)
+ }
+
+ protected := NewHeaders()
+ if err := json.Unmarshal(hdrbuf, protected); err != nil {
+ return nil, errors.Wrap(err, "failed to parse header JSON")
+ }
+
+ ivbuf, err := base64.Decode(parts[2])
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to base64 decode iv")
+ }
+
+ ctbuf, err := base64.Decode(parts[3])
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to base64 decode content")
+ }
+
+ tagbuf, err := base64.Decode(parts[4])
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to base64 decode tag")
+ }
+
+ m := NewMessage()
+ if err := m.Set(CipherTextKey, ctbuf); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, CipherTextKey)
+ }
+ if err := m.Set(InitializationVectorKey, ivbuf); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, InitializationVectorKey)
+ }
+ if err := m.Set(ProtectedHeadersKey, protected); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, ProtectedHeadersKey)
+ }
+
+ if err := m.makeDummyRecipient(string(parts[1]), protected); err != nil {
+ return nil, errors.Wrap(err, `failed to setup recipient`)
+ }
+
+ if err := m.Set(TagKey, tagbuf); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, TagKey)
+ }
+
+ if storeProtectedHeaders {
+ // This is later used for decryption.
+ m.rawProtectedHeaders = parts[0]
+ }
+
+ return m, nil
+}
+
+// RegisterCustomField allows users to specify that a private field
+// be decoded as an instance of the specified type. This option has
+// a global effect.
+//
+// For example, suppose you have a custom field `x-birthday`, which
+// you want to represent as a string formatted in RFC3339 in JSON,
+// but want it back as `time.Time`.
+//
+// In that case you would register a custom field as follows
+//
+// jwe.RegisterCustomField(`x-birthday`, timeT)
+//
+// Then `hdr.Get("x-birthday")` will still return an `interface{}`,
+// but you can convert its type to `time.Time`
+//
+// bdayif, _ := hdr.Get(`x-birthday`)
+// bday := bdayif.(time.Time)
+func RegisterCustomField(name string, object interface{}) {
+ registry.Register(name, object)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/message.go b/vendor/github.com/lestrrat-go/jwx/jwe/message.go
new file mode 100644
index 000000000..d3dc2535b
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/message.go
@@ -0,0 +1,646 @@
+package jwe
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "fmt"
+
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwk"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+// NewRecipient creates a Recipient object
+func NewRecipient() Recipient {
+ return &stdRecipient{
+ headers: NewHeaders(),
+ }
+}
+
+func (r *stdRecipient) SetHeaders(h Headers) error {
+ r.headers = h
+ return nil
+}
+
+func (r *stdRecipient) SetEncryptedKey(v []byte) error {
+ r.encryptedKey = v
+ return nil
+}
+
+func (r *stdRecipient) Headers() Headers {
+ return r.headers
+}
+
+func (r *stdRecipient) EncryptedKey() []byte {
+ return r.encryptedKey
+}
+
+type recipientMarshalProxy struct {
+ Headers Headers `json:"header"`
+ EncryptedKey string `json:"encrypted_key"`
+}
+
+func (r *stdRecipient) UnmarshalJSON(buf []byte) error {
+ var proxy recipientMarshalProxy
+ proxy.Headers = NewHeaders()
+ if err := json.Unmarshal(buf, &proxy); err != nil {
+ return errors.Wrap(err, `failed to unmarshal json into recipient`)
+ }
+
+ r.headers = proxy.Headers
+ decoded, err := base64.DecodeString(proxy.EncryptedKey)
+ if err != nil {
+ return errors.Wrap(err, `failed to decode "encrypted_key"`)
+ }
+ r.encryptedKey = decoded
+ return nil
+}
+
+func (r *stdRecipient) MarshalJSON() ([]byte, error) {
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+
+ buf.WriteString(`{"header":`)
+ hdrbuf, err := r.headers.MarshalJSON()
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to marshal recipient header`)
+ }
+ buf.Write(hdrbuf)
+ buf.WriteString(`,"encrypted_key":"`)
+ buf.WriteString(base64.EncodeToString(r.encryptedKey))
+ buf.WriteString(`"}`)
+
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
+
+// NewMessage creates a new message
+func NewMessage() *Message {
+ return &Message{}
+}
+
+func (m *Message) AuthenticatedData() []byte {
+ return m.authenticatedData
+}
+
+func (m *Message) CipherText() []byte {
+ return m.cipherText
+}
+
+func (m *Message) InitializationVector() []byte {
+ return m.initializationVector
+}
+
+func (m *Message) Tag() []byte {
+ return m.tag
+}
+
+func (m *Message) ProtectedHeaders() Headers {
+ return m.protectedHeaders
+}
+
+func (m *Message) Recipients() []Recipient {
+ return m.recipients
+}
+
+func (m *Message) UnprotectedHeaders() Headers {
+ return m.unprotectedHeaders
+}
+
+const (
+ AuthenticatedDataKey = "aad"
+ CipherTextKey = "ciphertext"
+ CountKey = "p2c"
+ InitializationVectorKey = "iv"
+ ProtectedHeadersKey = "protected"
+ RecipientsKey = "recipients"
+ SaltKey = "p2s"
+ TagKey = "tag"
+ UnprotectedHeadersKey = "unprotected"
+ HeadersKey = "header"
+ EncryptedKeyKey = "encrypted_key"
+)
+
+func (m *Message) Set(k string, v interface{}) error {
+ switch k {
+ case AuthenticatedDataKey:
+ buf, ok := v.([]byte)
+ if !ok {
+ return errors.Errorf(`invalid value %T for %s key`, v, AuthenticatedDataKey)
+ }
+ m.authenticatedData = buf
+ case CipherTextKey:
+ buf, ok := v.([]byte)
+ if !ok {
+ return errors.Errorf(`invalid value %T for %s key`, v, CipherTextKey)
+ }
+ m.cipherText = buf
+ case InitializationVectorKey:
+ buf, ok := v.([]byte)
+ if !ok {
+ return errors.Errorf(`invalid value %T for %s key`, v, InitializationVectorKey)
+ }
+ m.initializationVector = buf
+ case ProtectedHeadersKey:
+ cv, ok := v.(Headers)
+ if !ok {
+ return errors.Errorf(`invalid value %T for %s key`, v, ProtectedHeadersKey)
+ }
+ m.protectedHeaders = cv
+ case RecipientsKey:
+ cv, ok := v.([]Recipient)
+ if !ok {
+ return errors.Errorf(`invalid value %T for %s key`, v, RecipientsKey)
+ }
+ m.recipients = cv
+ case TagKey:
+ buf, ok := v.([]byte)
+ if !ok {
+ return errors.Errorf(`invalid value %T for %s key`, v, TagKey)
+ }
+ m.tag = buf
+ case UnprotectedHeadersKey:
+ cv, ok := v.(Headers)
+ if !ok {
+ return errors.Errorf(`invalid value %T for %s key`, v, UnprotectedHeadersKey)
+ }
+ m.unprotectedHeaders = cv
+ default:
+ if m.unprotectedHeaders == nil {
+ m.unprotectedHeaders = NewHeaders()
+ }
+ return m.unprotectedHeaders.Set(k, v)
+ }
+ return nil
+}
+
+type messageMarshalProxy struct {
+ AuthenticatedData string `json:"aad,omitempty"`
+ CipherText string `json:"ciphertext"`
+ InitializationVector string `json:"iv,omitempty"`
+ ProtectedHeaders json.RawMessage `json:"protected"`
+ Recipients []json.RawMessage `json:"recipients,omitempty"`
+ Tag string `json:"tag,omitempty"`
+ UnprotectedHeaders Headers `json:"unprotected,omitempty"`
+
+ // For flattened structure. Headers is NOT a Headers type,
+ // so that we can detect its presence by checking proxy.Headers != nil
+ Headers json.RawMessage `json:"header,omitempty"`
+ EncryptedKey string `json:"encrypted_key,omitempty"`
+}
+
+func (m *Message) MarshalJSON() ([]byte, error) {
+ // This is slightly convoluted, but we need to encode the
+ // protected headers, so we do it by hand
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+ enc := json.NewEncoder(buf)
+ fmt.Fprintf(buf, `{`)
+
+ var wrote bool
+ if aad := m.AuthenticatedData(); len(aad) > 0 {
+ wrote = true
+ fmt.Fprintf(buf, `%#v:`, AuthenticatedDataKey)
+ if err := enc.Encode(base64.EncodeToString(aad)); err != nil {
+ return nil, errors.Wrapf(err, `failed to encode %s field`, AuthenticatedDataKey)
+ }
+ }
+ if cipherText := m.CipherText(); len(cipherText) > 0 {
+ if wrote {
+ fmt.Fprintf(buf, `,`)
+ }
+ wrote = true
+ fmt.Fprintf(buf, `%#v:`, CipherTextKey)
+ if err := enc.Encode(base64.EncodeToString(cipherText)); err != nil {
+ return nil, errors.Wrapf(err, `failed to encode %s field`, CipherTextKey)
+ }
+ }
+
+ if iv := m.InitializationVector(); len(iv) > 0 {
+ if wrote {
+ fmt.Fprintf(buf, `,`)
+ }
+ wrote = true
+ fmt.Fprintf(buf, `%#v:`, InitializationVectorKey)
+ if err := enc.Encode(base64.EncodeToString(iv)); err != nil {
+ return nil, errors.Wrapf(err, `failed to encode %s field`, InitializationVectorKey)
+ }
+ }
+
+ if h := m.ProtectedHeaders(); h != nil {
+ encodedHeaders, err := h.Encode()
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to encode protected headers`)
+ }
+
+ if len(encodedHeaders) > 2 {
+ if wrote {
+ fmt.Fprintf(buf, `,`)
+ }
+ wrote = true
+ fmt.Fprintf(buf, `%#v:%#v`, ProtectedHeadersKey, string(encodedHeaders))
+ }
+ }
+
+ if recipients := m.Recipients(); len(recipients) > 0 {
+ if wrote {
+ fmt.Fprintf(buf, `,`)
+ }
+ if len(recipients) == 1 { // Use flattened format
+ fmt.Fprintf(buf, `%#v:`, HeadersKey)
+ if err := enc.Encode(recipients[0].Headers()); err != nil {
+ return nil, errors.Wrapf(err, `failed to encode %s field`, HeadersKey)
+ }
+ if ek := recipients[0].EncryptedKey(); len(ek) > 0 {
+ fmt.Fprintf(buf, `,%#v:`, EncryptedKeyKey)
+ if err := enc.Encode(base64.EncodeToString(ek)); err != nil {
+ return nil, errors.Wrapf(err, `failed to encode %s field`, EncryptedKeyKey)
+ }
+ }
+ } else {
+ fmt.Fprintf(buf, `%#v:`, RecipientsKey)
+ if err := enc.Encode(recipients); err != nil {
+ return nil, errors.Wrapf(err, `failed to encode %s field`, RecipientsKey)
+ }
+ }
+ }
+
+ if tag := m.Tag(); len(tag) > 0 {
+ if wrote {
+ fmt.Fprintf(buf, `,`)
+ }
+ fmt.Fprintf(buf, `%#v:`, TagKey)
+ if err := enc.Encode(base64.EncodeToString(tag)); err != nil {
+ return nil, errors.Wrapf(err, `failed to encode %s field`, TagKey)
+ }
+ }
+
+ if h := m.UnprotectedHeaders(); h != nil {
+ unprotected, err := json.Marshal(h)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to encode unprotected headers`)
+ }
+
+ if len(unprotected) > 2 {
+ fmt.Fprintf(buf, `,%#v:%#v`, UnprotectedHeadersKey, string(unprotected))
+ }
+ }
+ fmt.Fprintf(buf, `}`)
+
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
+
+func (m *Message) UnmarshalJSON(buf []byte) error {
+ var proxy messageMarshalProxy
+ proxy.UnprotectedHeaders = NewHeaders()
+
+ if err := json.Unmarshal(buf, &proxy); err != nil {
+ return errors.Wrap(err, `failed to unmashal JSON into message`)
+ }
+
+ // Get the string value
+ var protectedHeadersStr string
+ if err := json.Unmarshal(proxy.ProtectedHeaders, &protectedHeadersStr); err != nil {
+ return errors.Wrap(err, `failed to decode protected headers (1)`)
+ }
+
+ // It's now in _quoted_ base64 string. Decode it
+ protectedHeadersRaw, err := base64.DecodeString(protectedHeadersStr)
+ if err != nil {
+ return errors.Wrap(err, "failed to base64 decoded protected headers buffer")
+ }
+
+ h := NewHeaders()
+ if err := json.Unmarshal(protectedHeadersRaw, h); err != nil {
+ return errors.Wrap(err, `failed to decode protected headers (2)`)
+ }
+
+ // if this were a flattened message, we would see a "header" and "ciphertext"
+ // field. TODO: do both of these conditions need to meet, or just one?
+ if proxy.Headers != nil || len(proxy.EncryptedKey) > 0 {
+ recipient := NewRecipient()
+ hdrs := NewHeaders()
+ if err := json.Unmarshal(proxy.Headers, hdrs); err != nil {
+ return errors.Wrap(err, `failed to decode headers field`)
+ }
+
+ if err := recipient.SetHeaders(hdrs); err != nil {
+ return errors.Wrap(err, `failed to set new headers`)
+ }
+
+ if v := proxy.EncryptedKey; len(v) > 0 {
+ buf, err := base64.DecodeString(v)
+ if err != nil {
+ return errors.Wrap(err, `failed to decode encrypted key`)
+ }
+ if err := recipient.SetEncryptedKey(buf); err != nil {
+ return errors.Wrap(err, `failed to set encrypted key`)
+ }
+ }
+
+ m.recipients = append(m.recipients, recipient)
+ } else {
+ for i, recipientbuf := range proxy.Recipients {
+ recipient := NewRecipient()
+ if err := json.Unmarshal(recipientbuf, recipient); err != nil {
+ return errors.Wrapf(err, `failed to decode recipient at index %d`, i)
+ }
+
+ m.recipients = append(m.recipients, recipient)
+ }
+ }
+
+ if src := proxy.AuthenticatedData; len(src) > 0 {
+ v, err := base64.DecodeString(src)
+ if err != nil {
+ return errors.Wrap(err, `failed to decode "aad"`)
+ }
+ m.authenticatedData = v
+ }
+
+ if src := proxy.CipherText; len(src) > 0 {
+ v, err := base64.DecodeString(src)
+ if err != nil {
+ return errors.Wrap(err, `failed to decode "ciphertext"`)
+ }
+ m.cipherText = v
+ }
+
+ if src := proxy.InitializationVector; len(src) > 0 {
+ v, err := base64.DecodeString(src)
+ if err != nil {
+ return errors.Wrap(err, `failed to decode "iv"`)
+ }
+ m.initializationVector = v
+ }
+
+ if src := proxy.Tag; len(src) > 0 {
+ v, err := base64.DecodeString(src)
+ if err != nil {
+ return errors.Wrap(err, `failed to decode "tag"`)
+ }
+ m.tag = v
+ }
+
+ m.protectedHeaders = h
+ if m.storeProtectedHeaders {
+ // this is later used for decryption
+ m.rawProtectedHeaders = base64.Encode(protectedHeadersRaw)
+ }
+
+ if !proxy.UnprotectedHeaders.(isZeroer).isZero() {
+ m.unprotectedHeaders = proxy.UnprotectedHeaders
+ }
+
+ if len(m.recipients) == 0 {
+ if err := m.makeDummyRecipient(proxy.EncryptedKey, m.protectedHeaders); err != nil {
+ return errors.Wrap(err, `failed to setup recipient`)
+ }
+ }
+
+ return nil
+}
+
+func (m *Message) makeDummyRecipient(enckeybuf string, protected Headers) error {
+ // Recipients in this case should not contain the content encryption key,
+ // so move that out
+ hdrs, err := protected.Clone(context.TODO())
+ if err != nil {
+ return errors.Wrap(err, `failed to clone headers`)
+ }
+
+ if err := hdrs.Remove(ContentEncryptionKey); err != nil {
+ return errors.Wrapf(err, "failed to remove %#v from public header", ContentEncryptionKey)
+ }
+
+ enckey, err := base64.DecodeString(enckeybuf)
+ if err != nil {
+ return errors.Wrap(err, `failed to decode encrypted key`)
+ }
+
+ if err := m.Set(RecipientsKey, []Recipient{
+ &stdRecipient{
+ headers: hdrs,
+ encryptedKey: enckey,
+ },
+ }); err != nil {
+ return errors.Wrapf(err, `failed to set %s`, RecipientsKey)
+ }
+ return nil
+}
+
+// Decrypt decrypts the message using the specified algorithm and key.
+//
+// `key` must be a private key in its "raw" format (i.e. something like
+// *rsa.PrivateKey, instead of jwk.Key)
+//
+// This method is marked for deprecation. It will be removed from the API
+// in the next major release. You should not rely on this method
+// to work 100% of the time, especially when it was obtained via jwe.Parse
+// instead of being constructed from scratch by this library.
+func (m *Message) Decrypt(alg jwa.KeyEncryptionAlgorithm, key interface{}) ([]byte, error) {
+ var ctx decryptCtx
+ ctx.alg = alg
+ ctx.key = key
+ ctx.msg = m
+
+ return doDecryptCtx(&ctx)
+}
+
+func doDecryptCtx(dctx *decryptCtx) ([]byte, error) {
+ m := dctx.msg
+ alg := dctx.alg
+ key := dctx.key
+
+ if jwkKey, ok := key.(jwk.Key); ok {
+ var raw interface{}
+ if err := jwkKey.Raw(&raw); err != nil {
+ return nil, errors.Wrapf(err, `failed to retrieve raw key from %T`, key)
+ }
+ key = raw
+ }
+
+ var err error
+ ctx := context.TODO()
+ h, err := m.protectedHeaders.Clone(ctx)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to copy protected headers`)
+ }
+ h, err = h.Merge(ctx, m.unprotectedHeaders)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to merge headers for message decryption")
+ }
+
+ enc := m.protectedHeaders.ContentEncryption()
+ var aad []byte
+ if aadContainer := m.authenticatedData; aadContainer != nil {
+ aad = base64.Encode(aadContainer)
+ }
+
+ var computedAad []byte
+ if len(m.rawProtectedHeaders) > 0 {
+ computedAad = m.rawProtectedHeaders
+ } else {
+ // this is probably not required once msg.Decrypt is deprecated
+ var err error
+ computedAad, err = m.protectedHeaders.Encode()
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to encode protected headers")
+ }
+ }
+
+ dec := NewDecrypter(alg, enc, key).
+ AuthenticatedData(aad).
+ ComputedAuthenticatedData(computedAad).
+ InitializationVector(m.initializationVector).
+ Tag(m.tag)
+
+ var plaintext []byte
+ var lastError error
+
+ // if we have no recipients, pretend like we only have one
+ recipients := m.recipients
+ if len(recipients) == 0 {
+ r := NewRecipient()
+ if err := r.SetHeaders(m.protectedHeaders); err != nil {
+ return nil, errors.Wrap(err, `failed to set headers to recipient`)
+ }
+ recipients = append(recipients, r)
+ }
+
+ for _, recipient := range recipients {
+ // strategy: try each recipient. If we fail in one of the steps,
+ // keep looping because there might be another key with the same algo
+ if recipient.Headers().Algorithm() != alg {
+ // algorithms don't match
+ continue
+ }
+
+ h2, err := h.Clone(ctx)
+ if err != nil {
+ lastError = errors.Wrap(err, `failed to copy headers (1)`)
+ continue
+ }
+
+ h2, err = h2.Merge(ctx, recipient.Headers())
+ if err != nil {
+ lastError = errors.Wrap(err, `failed to copy headers (2)`)
+ continue
+ }
+
+ switch alg {
+ case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
+ epkif, ok := h2.Get(EphemeralPublicKeyKey)
+ if !ok {
+ return nil, errors.New("failed to get 'epk' field")
+ }
+ switch epk := epkif.(type) {
+ case jwk.ECDSAPublicKey:
+ var pubkey ecdsa.PublicKey
+ if err := epk.Raw(&pubkey); err != nil {
+ return nil, errors.Wrap(err, "failed to get public key")
+ }
+ dec.PublicKey(&pubkey)
+ case jwk.OKPPublicKey:
+ var pubkey interface{}
+ if err := epk.Raw(&pubkey); err != nil {
+ return nil, errors.Wrap(err, "failed to get public key")
+ }
+ dec.PublicKey(pubkey)
+ default:
+ return nil, errors.Errorf("unexpected 'epk' type %T for alg %s", epkif, alg)
+ }
+
+ if apu := h2.AgreementPartyUInfo(); len(apu) > 0 {
+ dec.AgreementPartyUInfo(apu)
+ }
+
+ if apv := h2.AgreementPartyVInfo(); len(apv) > 0 {
+ dec.AgreementPartyVInfo(apv)
+ }
+ case jwa.A128GCMKW, jwa.A192GCMKW, jwa.A256GCMKW:
+ ivB64, ok := h2.Get(InitializationVectorKey)
+ if !ok {
+ return nil, errors.New("failed to get 'iv' field")
+ }
+ ivB64Str, ok := ivB64.(string)
+ if !ok {
+ return nil, errors.Errorf("unexpected type for 'iv': %T", ivB64)
+ }
+ tagB64, ok := h2.Get(TagKey)
+ if !ok {
+ return nil, errors.New("failed to get 'tag' field")
+ }
+ tagB64Str, ok := tagB64.(string)
+ if !ok {
+ return nil, errors.Errorf("unexpected type for 'tag': %T", tagB64)
+ }
+ iv, err := base64.DecodeString(ivB64Str)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to b64-decode 'iv'")
+ }
+ tag, err := base64.DecodeString(tagB64Str)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to b64-decode 'tag'")
+ }
+ dec.KeyInitializationVector(iv)
+ dec.KeyTag(tag)
+ case jwa.PBES2_HS256_A128KW, jwa.PBES2_HS384_A192KW, jwa.PBES2_HS512_A256KW:
+ saltB64, ok := h2.Get(SaltKey)
+ if !ok {
+ return nil, errors.New("failed to get 'p2s' field")
+ }
+ saltB64Str, ok := saltB64.(string)
+ if !ok {
+ return nil, errors.Errorf("unexpected type for 'p2s': %T", saltB64)
+ }
+
+ count, ok := h2.Get(CountKey)
+ if !ok {
+ return nil, errors.New("failed to get 'p2c' field")
+ }
+ countFlt, ok := count.(float64)
+ if !ok {
+ return nil, errors.Errorf("unexpected type for 'p2c': %T", count)
+ }
+ salt, err := base64.DecodeString(saltB64Str)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to b64-decode 'salt'")
+ }
+ dec.KeySalt(salt)
+ dec.KeyCount(int(countFlt))
+ }
+
+ plaintext, err = dec.Decrypt(recipient.EncryptedKey(), m.cipherText)
+ if err != nil {
+ lastError = errors.Wrap(err, `failed to decrypt`)
+ continue
+ }
+
+ if h2.Compression() == jwa.Deflate {
+ buf, err := uncompress(plaintext)
+ if err != nil {
+ lastError = errors.Wrap(err, `failed to uncompress payload`)
+ continue
+ }
+ plaintext = buf
+ }
+ break
+ }
+
+ if plaintext == nil {
+ if lastError != nil {
+ return nil, errors.Errorf(`failed to find matching recipient to decrypt key (last error = %s)`, lastError)
+ }
+ return nil, errors.New("failed to find matching recipient")
+ }
+
+ return plaintext, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/options.go b/vendor/github.com/lestrrat-go/jwx/jwe/options.go
new file mode 100644
index 000000000..617e0e47c
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/options.go
@@ -0,0 +1,87 @@
+package jwe
+
+import (
+ "context"
+
+ "github.com/lestrrat-go/option"
+)
+
+type Option = option.Interface
+type identMessage struct{}
+type identPostParser struct{}
+type identPrettyFormat struct{}
+type identProtectedHeader struct{}
+
+type DecryptOption interface {
+ Option
+ decryptOption()
+}
+
+type decryptOption struct {
+ Option
+}
+
+func (*decryptOption) decryptOption() {}
+
+type SerializerOption interface {
+ Option
+ serializerOption()
+}
+
+type serializerOption struct {
+ Option
+}
+
+func (*serializerOption) serializerOption() {}
+
+type EncryptOption interface {
+ Option
+ encryptOption()
+}
+
+type encryptOption struct {
+ Option
+}
+
+func (*encryptOption) encryptOption() {}
+
+// WithPrettyFormat specifies if the `jwe.JSON` serialization tool
+// should generate pretty-formatted output
+func WithPrettyFormat(b bool) SerializerOption {
+ return &serializerOption{option.New(identPrettyFormat{}, b)}
+}
+
+// Specify contents of the protected header. Some fields such as
+// "enc" and "zip" will be overwritten when encryption is performed.
+func WithProtectedHeaders(h Headers) EncryptOption {
+ cloned, _ := h.Clone(context.Background())
+ return &encryptOption{option.New(identProtectedHeader{}, cloned)}
+}
+
+// WithMessage provides a message object to be populated by `jwe.Decrpt`
+// Using this option allows you to decrypt AND obtain the `jwe.Message`
+// in one go.
+//
+// Note that you should NOT be using the message object for anything other
+// than inspecting its contents. Particularly, do not expect the message
+// reliable when you call `Decrypt` on it. `(jwe.Message).Decrypt` is
+// slated to be deprecated in the next major version.
+func WithMessage(m *Message) DecryptOption {
+ return &decryptOption{option.New(identMessage{}, m)}
+}
+
+// WithPostParser specifies the handler to be called immediately
+// after the JWE message has been parsed, but before decryption
+// takes place during `jwe.Decrypt`.
+//
+// This option exists to allow advanced users that require the use
+// of information stored in the JWE message to determine how the
+// decryption should be handled.
+//
+// For security reasons it is highly recommended that you thoroughly
+// study how the process works before using this option. This is especially
+// true if you are trying to infer key algorithms and keys to use to
+// decrypt a message using non-standard hints.
+func WithPostParser(p PostParser) DecryptOption {
+ return &decryptOption{option.New(identPostParser{}, p)}
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go b/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go
new file mode 100644
index 000000000..27ca9b861
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwe/serializer.go
@@ -0,0 +1,94 @@
+package jwe
+
+import (
+ "context"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/json"
+
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/pkg/errors"
+)
+
+// Compact encodes the given message into a JWE compact serialization format.
+//
+// Currently `Compact()` does not take any options, but the API is
+// set up as such to allow future expansions
+func Compact(m *Message, _ ...SerializerOption) ([]byte, error) {
+ if len(m.recipients) != 1 {
+ return nil, errors.New("wrong number of recipients for compact serialization")
+ }
+
+ recipient := m.recipients[0]
+
+ // The protected header must be a merge between the message-wide
+ // protected header AND the recipient header
+
+ // There's something wrong if m.protectedHeaders is nil, but
+ // it could happen
+ if m.protectedHeaders == nil {
+ return nil, errors.New("invalid protected header")
+ }
+
+ ctx := context.TODO()
+ hcopy, err := m.protectedHeaders.Clone(ctx)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to copy protected header")
+ }
+ hcopy, err = hcopy.Merge(ctx, m.unprotectedHeaders)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to merge unprotected header")
+ }
+ hcopy, err = hcopy.Merge(ctx, recipient.Headers())
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to merge recipient header")
+ }
+
+ protected, err := hcopy.Encode()
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to encode header")
+ }
+
+ encryptedKey := base64.Encode(recipient.EncryptedKey())
+ iv := base64.Encode(m.initializationVector)
+ cipher := base64.Encode(m.cipherText)
+ tag := base64.Encode(m.tag)
+
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+
+ buf.Grow(len(protected) + len(encryptedKey) + len(iv) + len(cipher) + len(tag) + 4)
+ buf.Write(protected)
+ buf.WriteByte('.')
+ buf.Write(encryptedKey)
+ buf.WriteByte('.')
+ buf.Write(iv)
+ buf.WriteByte('.')
+ buf.Write(cipher)
+ buf.WriteByte('.')
+ buf.Write(tag)
+
+ result := make([]byte, buf.Len())
+ copy(result, buf.Bytes())
+ return result, nil
+}
+
+// JSON encodes the message into a JWE JSON serialization format.
+//
+// If `WithPrettyFormat(true)` is passed as an option, the returned
+// value will be formatted using `json.MarshalIndent()`
+func JSON(m *Message, options ...SerializerOption) ([]byte, error) {
+ var pretty bool
+ for _, option := range options {
+ //nolint:forcetypeassert
+ switch option.Ident() {
+ case identPrettyFormat{}:
+ pretty = option.Value().(bool)
+ }
+ }
+
+ if pretty {
+ return json.MarshalIndent(m, "", " ")
+ }
+ return json.Marshal(m)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/README.md b/vendor/github.com/lestrrat-go/jwx/jws/README.md
new file mode 100644
index 000000000..f6e3dcbac
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/README.md
@@ -0,0 +1,111 @@
+# JWS [](https://pkg.go.dev/github.com/lestrrat-go/jwx/jws)
+
+Package jws implements JWS as described in [RFC7515](https://tools.ietf.org/html/rfc7515) and [RFC7797](https://tools.ietf.org/html/rfc7797)
+
+* Parse and generate compact or JSON serializations
+* Sign and verify arbitrary payload
+* Use any of the keys supported in [github.com/lestrrat-go/jwx/jwk](../jwk)
+* Add arbitrary fields in the JWS object
+* Ability to add/replace existing signature methods
+* Respect "b64" settings for RFC7797
+
+How-to style documentation can be found in the [docs directory](../docs).
+
+Examples are located in the examples directory ([jws_example_test.go](../examples/jws_example_test.go))
+
+Supported signature algorithms:
+
+| Algorithm | Supported? | Constant in [jwa](../jwa) |
+|:----------------------------------------|:-----------|:-------------------------|
+| HMAC using SHA-256 | YES | jwa.HS256 |
+| HMAC using SHA-384 | YES | jwa.HS384 |
+| HMAC using SHA-512 | YES | jwa.HS512 |
+| RSASSA-PKCS-v1.5 using SHA-256 | YES | jwa.RS256 |
+| RSASSA-PKCS-v1.5 using SHA-384 | YES | jwa.RS384 |
+| RSASSA-PKCS-v1.5 using SHA-512 | YES | jwa.RS512 |
+| ECDSA using P-256 and SHA-256 | YES | jwa.ES256 |
+| ECDSA using P-384 and SHA-384 | YES | jwa.ES384 |
+| ECDSA using P-521 and SHA-512 | YES | jwa.ES512 |
+| ECDSA using secp256k1 and SHA-256 (2) | YES | jwa.ES256K |
+| RSASSA-PSS using SHA256 and MGF1-SHA256 | YES | jwa.PS256 |
+| RSASSA-PSS using SHA384 and MGF1-SHA384 | YES | jwa.PS384 |
+| RSASSA-PSS using SHA512 and MGF1-SHA512 | YES | jwa.PS512 |
+| EdDSA (1) | YES | jwa.EdDSA |
+
+* Note 1: Experimental
+* Note 2: Experimental, and must be toggled using `-tags jwx_es256k` build tag
+
+# SYNOPSIS
+
+## Sign and verify arbitrary data
+
+```go
+import(
+ "crypto/rand"
+ "crypto/rsa"
+ "log"
+
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jws"
+)
+
+func main() {
+ privkey, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ log.Printf("failed to generate private key: %s", err)
+ return
+ }
+
+ buf, err := jws.Sign([]byte("Lorem ipsum"), jwa.RS256, privkey)
+ if err != nil {
+ log.Printf("failed to created JWS message: %s", err)
+ return
+ }
+
+ // When you receive a JWS message, you can verify the signature
+ // and grab the payload sent in the message in one go:
+ verified, err := jws.Verify(buf, jwa.RS256, &privkey.PublicKey)
+ if err != nil {
+ log.Printf("failed to verify message: %s", err)
+ return
+ }
+
+ log.Printf("signed message verified! -> %s", verified)
+}
+```
+
+## Programatically manipulate `jws.Message`
+
+```go
+func ExampleMessage() {
+ // initialization for the following variables have been omitted.
+ // please see jws_example_test.go for details
+ var decodedPayload, decodedSig1, decodedSig2 []byte
+ var public1, protected1, public2, protected2 jws.Header
+
+ // Construct a message. DO NOT use values that are base64 encoded
+ m := jws.NewMessage().
+ SetPayload(decodedPayload).
+ AppendSignature(
+ jws.NewSignature().
+ SetSignature(decodedSig1).
+ SetProtectedHeaders(public1).
+ SetPublicHeaders(protected1),
+ ).
+ AppendSignature(
+ jws.NewSignature().
+ SetSignature(decodedSig2).
+ SetProtectedHeaders(public2).
+ SetPublicHeaders(protected2),
+ )
+
+ buf, err := json.MarshalIndent(m, "", " ")
+ if err != nil {
+ fmt.Printf("%s\n", err)
+ return
+ }
+
+ _ = buf
+}
+```
+
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go b/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go
new file mode 100644
index 000000000..840eaa7c0
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go
@@ -0,0 +1,165 @@
+package jws
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "io"
+
+ "github.com/lestrrat-go/jwx/internal/keyconv"
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+var ecdsaSigners map[jwa.SignatureAlgorithm]*ecdsaSigner
+var ecdsaVerifiers map[jwa.SignatureAlgorithm]*ecdsaVerifier
+
+func init() {
+ algs := map[jwa.SignatureAlgorithm]crypto.Hash{
+ jwa.ES256: crypto.SHA256,
+ jwa.ES384: crypto.SHA384,
+ jwa.ES512: crypto.SHA512,
+ jwa.ES256K: crypto.SHA256,
+ }
+ ecdsaSigners = make(map[jwa.SignatureAlgorithm]*ecdsaSigner)
+ ecdsaVerifiers = make(map[jwa.SignatureAlgorithm]*ecdsaVerifier)
+
+ for alg, hash := range algs {
+ ecdsaSigners[alg] = &ecdsaSigner{
+ alg: alg,
+ hash: hash,
+ }
+ ecdsaVerifiers[alg] = &ecdsaVerifier{
+ alg: alg,
+ hash: hash,
+ }
+ }
+}
+
+func newECDSASigner(alg jwa.SignatureAlgorithm) Signer {
+ return ecdsaSigners[alg]
+}
+
+// ecdsaSigners are immutable.
+type ecdsaSigner struct {
+ alg jwa.SignatureAlgorithm
+ hash crypto.Hash
+}
+
+func (s ecdsaSigner) Algorithm() jwa.SignatureAlgorithm {
+ return s.alg
+}
+
+type ecdsaCryptoSigner struct {
+ key *ecdsa.PrivateKey
+ hash crypto.Hash
+}
+
+func (s *ecdsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
+ if key == nil {
+ return nil, errors.New(`missing private key while signing payload`)
+ }
+
+ signer, ok := key.(crypto.Signer)
+ if ok {
+ // We support crypto.Signer, but we DON'T support
+ // ecdsa.PrivateKey as a crypto.Signer, because it encodes
+ // the result in ASN1 format.
+ if pk, ok := key.(*ecdsa.PrivateKey); ok {
+ signer = newECDSACryptoSigner(pk, s.hash)
+ }
+ } else {
+ var privkey ecdsa.PrivateKey
+ if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil {
+ return nil, errors.Wrapf(err, `failed to retrieve ecdsa.PrivateKey out of %T`, key)
+ }
+ signer = newECDSACryptoSigner(&privkey, s.hash)
+ }
+
+ h := s.hash.New()
+ if _, err := h.Write(payload); err != nil {
+ return nil, errors.Wrap(err, "failed to write payload using ecdsa")
+ }
+ return signer.Sign(rand.Reader, h.Sum(nil), s.hash)
+}
+
+func newECDSACryptoSigner(key *ecdsa.PrivateKey, hash crypto.Hash) crypto.Signer {
+ return &ecdsaCryptoSigner{
+ key: key,
+ hash: hash,
+ }
+}
+
+func (cs *ecdsaCryptoSigner) Public() crypto.PublicKey {
+ return cs.key.PublicKey
+}
+
+func (cs *ecdsaCryptoSigner) Sign(seed io.Reader, digest []byte, _ crypto.SignerOpts) ([]byte, error) {
+ r, s, err := ecdsa.Sign(seed, cs.key, digest)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to sign payload using ecdsa")
+ }
+
+ curveBits := cs.key.Curve.Params().BitSize
+ keyBytes := curveBits / 8
+ // Curve bits do not need to be a multiple of 8.
+ if curveBits%8 > 0 {
+ keyBytes++
+ }
+
+ rBytes := r.Bytes()
+ rBytesPadded := make([]byte, keyBytes)
+ copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
+
+ sBytes := s.Bytes()
+ sBytesPadded := make([]byte, keyBytes)
+ copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
+
+ out := append(rBytesPadded, sBytesPadded...)
+ return out, nil
+}
+
+// ecdsaVerifiers are immutable.
+type ecdsaVerifier struct {
+ alg jwa.SignatureAlgorithm
+ hash crypto.Hash
+}
+
+func newECDSAVerifier(alg jwa.SignatureAlgorithm) Verifier {
+ return ecdsaVerifiers[alg]
+}
+
+func (v ecdsaVerifier) Algorithm() jwa.SignatureAlgorithm {
+ return v.alg
+}
+
+func (v *ecdsaVerifier) Verify(payload []byte, signature []byte, key interface{}) error {
+ if key == nil {
+ return errors.New(`missing public key while verifying payload`)
+ }
+
+ var pubkey ecdsa.PublicKey
+ if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil {
+ return errors.Wrapf(err, `failed to retrieve ecdsa.PublicKey out of %T`, key)
+ }
+
+ r := pool.GetBigInt()
+ s := pool.GetBigInt()
+ defer pool.ReleaseBigInt(r)
+ defer pool.ReleaseBigInt(s)
+
+ n := len(signature) / 2
+ r.SetBytes(signature[:n])
+ s.SetBytes(signature[n:])
+
+ h := v.hash.New()
+ if _, err := h.Write(payload); err != nil {
+ return errors.Wrap(err, "failed to write payload using ecdsa")
+ }
+
+ if !ecdsa.Verify(&pubkey, h.Sum(nil), r, s) {
+ return errors.New(`failed to verify signature using ecdsa`)
+ }
+ return nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/eddsa.go b/vendor/github.com/lestrrat-go/jwx/jws/eddsa.go
new file mode 100644
index 000000000..02e88c8d5
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/eddsa.go
@@ -0,0 +1,62 @@
+package jws
+
+import (
+ "crypto"
+ "crypto/ed25519"
+ "crypto/rand"
+
+ "github.com/lestrrat-go/jwx/internal/keyconv"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+type eddsaSigner struct{}
+
+func newEdDSASigner() Signer {
+ return &eddsaSigner{}
+}
+
+func (s eddsaSigner) Algorithm() jwa.SignatureAlgorithm {
+ return jwa.EdDSA
+}
+
+func (s eddsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
+ if key == nil {
+ return nil, errors.New(`missing private key while signing payload`)
+ }
+
+ // The ed25519.PrivateKey object implements crypto.Signer, so we should
+ // simply accept a crypto.Signer here.
+ signer, ok := key.(crypto.Signer)
+ if !ok {
+ // This fallback exists for cases when jwk.Key was passed, or
+ // users gave us a pointer instead of non-pointer, etc.
+ var privkey ed25519.PrivateKey
+ if err := keyconv.Ed25519PrivateKey(&privkey, key); err != nil {
+ return nil, errors.Wrapf(err, `failed to retrieve ed25519.PrivateKey out of %T`, key)
+ }
+ signer = privkey
+ }
+ return signer.Sign(rand.Reader, payload, crypto.Hash(0))
+}
+
+type eddsaVerifier struct{}
+
+func newEdDSAVerifier() Verifier {
+ return &eddsaVerifier{}
+}
+
+func (v eddsaVerifier) Verify(payload, signature []byte, key interface{}) (err error) {
+ if key == nil {
+ return errors.New(`missing public key while verifying payload`)
+ }
+
+ var pubkey ed25519.PublicKey
+ if err := keyconv.Ed25519PublicKey(&pubkey, key); err != nil {
+ return errors.Wrapf(err, `failed to retrieve ed25519.PublicKey out of %T`, key)
+ }
+ if !ed25519.Verify(pubkey, payload, signature) {
+ return errors.New(`failed to match EdDSA signature`)
+ }
+ return nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/gen.sh b/vendor/github.com/lestrrat-go/jwx/jws/gen.sh
new file mode 100644
index 000000000..dde877e1a
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/gen.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# Script to perform code generation. This exists to overcome
+# the fact that go:generate doesn't really allow you to change directories
+
+set -e
+
+pushd internal/cmd/genheader
+go build -o genheader main.go
+popd
+
+./internal/cmd/genheader/genheader -objects=internal/cmd/genheader/objects.yml
+
+rm internal/cmd/genheader/genheader
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/headers.go b/vendor/github.com/lestrrat-go/jwx/jws/headers.go
new file mode 100644
index 000000000..2eca43158
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/headers.go
@@ -0,0 +1,69 @@
+package jws
+
+import (
+ "context"
+
+ "github.com/lestrrat-go/iter/mapiter"
+ "github.com/lestrrat-go/jwx/internal/iter"
+ "github.com/pkg/errors"
+)
+
+// Iterate returns a channel that successively returns all the
+// header name and values.
+func (h *stdHeaders) Iterate(ctx context.Context) Iterator {
+ pairs := h.makePairs()
+ ch := make(chan *HeaderPair, len(pairs))
+ go func(ctx context.Context, ch chan *HeaderPair, pairs []*HeaderPair) {
+ defer close(ch)
+ for _, pair := range pairs {
+ select {
+ case <-ctx.Done():
+ return
+ case ch <- pair:
+ }
+ }
+ }(ctx, ch, pairs)
+ return mapiter.New(ch)
+}
+
+func (h *stdHeaders) Walk(ctx context.Context, visitor Visitor) error {
+ return iter.WalkMap(ctx, h, visitor)
+}
+
+func (h *stdHeaders) AsMap(ctx context.Context) (map[string]interface{}, error) {
+ return iter.AsMap(ctx, h)
+}
+
+func (h *stdHeaders) Copy(ctx context.Context, dst Headers) error {
+ for _, pair := range h.makePairs() {
+ if err := dst.Set(pair.Key.(string), pair.Value); err != nil {
+ return errors.Wrapf(err, `failed to set header`)
+ }
+ }
+ return nil
+}
+
+// mergeHeaders merges two headers, and works even if the first Header
+// object is nil. This is not exported because ATM it felt like this
+// function is not frequently used, and MergeHeaders seemed a clunky name
+func mergeHeaders(ctx context.Context, h1, h2 Headers) (Headers, error) {
+ h3 := NewHeaders()
+
+ if h1 != nil {
+ if err := h1.Copy(ctx, h3); err != nil {
+ return nil, errors.Wrap(err, `failed to copy headers from first Header`)
+ }
+ }
+
+ if h2 != nil {
+ if err := h2.Copy(ctx, h3); err != nil {
+ return nil, errors.Wrap(err, `failed to copy headers from second Header`)
+ }
+ }
+
+ return h3, nil
+}
+
+func (h *stdHeaders) Merge(ctx context.Context, h2 Headers) (Headers, error) {
+ return mergeHeaders(ctx, h, h2)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go b/vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go
new file mode 100644
index 000000000..de624bcc2
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/headers_gen.go
@@ -0,0 +1,561 @@
+// This file is auto-generated by jws/internal/cmd/genheaders/main.go. DO NOT EDIT
+
+package jws
+
+import (
+ "bytes"
+ "context"
+ "sort"
+ "sync"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/pkg/errors"
+)
+
+const (
+ AlgorithmKey = "alg"
+ ContentTypeKey = "cty"
+ CriticalKey = "crit"
+ JWKKey = "jwk"
+ JWKSetURLKey = "jku"
+ KeyIDKey = "kid"
+ TypeKey = "typ"
+ X509CertChainKey = "x5c"
+ X509CertThumbprintKey = "x5t"
+ X509CertThumbprintS256Key = "x5t#S256"
+ X509URLKey = "x5u"
+)
+
+// Headers describe a standard Header set.
+type Headers interface {
+ json.Marshaler
+ json.Unmarshaler
+ Algorithm() jwa.SignatureAlgorithm
+ ContentType() string
+ Critical() []string
+ JWK() jwk.Key
+ JWKSetURL() string
+ KeyID() string
+ Type() string
+ X509CertChain() []string
+ X509CertThumbprint() string
+ X509CertThumbprintS256() string
+ X509URL() string
+ Iterate(ctx context.Context) Iterator
+ Walk(context.Context, Visitor) error
+ AsMap(context.Context) (map[string]interface{}, error)
+ Copy(context.Context, Headers) error
+ Merge(context.Context, Headers) (Headers, error)
+ Get(string) (interface{}, bool)
+ Set(string, interface{}) error
+ Remove(string) error
+
+ // PrivateParams returns the non-standard elements in the source structure
+ // WARNING: DO NOT USE PrivateParams() IF YOU HAVE CONCURRENT CODE ACCESSING THEM.
+ // Use AsMap() to get a copy of the entire header instead
+ PrivateParams() map[string]interface{}
+}
+
+type stdHeaders struct {
+ algorithm *jwa.SignatureAlgorithm // https://tools.ietf.org/html/rfc7515#section-4.1.1
+ contentType *string // https://tools.ietf.org/html/rfc7515#section-4.1.10
+ critical []string // https://tools.ietf.org/html/rfc7515#section-4.1.11
+ jwk jwk.Key // https://tools.ietf.org/html/rfc7515#section-4.1.3
+ jwkSetURL *string // https://tools.ietf.org/html/rfc7515#section-4.1.2
+ keyID *string // https://tools.ietf.org/html/rfc7515#section-4.1.4
+ typ *string // https://tools.ietf.org/html/rfc7515#section-4.1.9
+ x509CertChain []string // https://tools.ietf.org/html/rfc7515#section-4.1.6
+ x509CertThumbprint *string // https://tools.ietf.org/html/rfc7515#section-4.1.7
+ x509CertThumbprintS256 *string // https://tools.ietf.org/html/rfc7515#section-4.1.8
+ x509URL *string // https://tools.ietf.org/html/rfc7515#section-4.1.5
+ privateParams map[string]interface{}
+ mu *sync.RWMutex
+ dc DecodeCtx
+ raw []byte // stores the raw version of the header so it can be used later
+}
+
+func NewHeaders() Headers {
+ return &stdHeaders{
+ mu: &sync.RWMutex{},
+ }
+}
+
+func (h *stdHeaders) Algorithm() jwa.SignatureAlgorithm {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.algorithm == nil {
+ return ""
+ }
+ return *(h.algorithm)
+}
+
+func (h *stdHeaders) ContentType() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.contentType == nil {
+ return ""
+ }
+ return *(h.contentType)
+}
+
+func (h *stdHeaders) Critical() []string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.critical
+}
+
+func (h *stdHeaders) JWK() jwk.Key {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.jwk
+}
+
+func (h *stdHeaders) JWKSetURL() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.jwkSetURL == nil {
+ return ""
+ }
+ return *(h.jwkSetURL)
+}
+
+func (h *stdHeaders) KeyID() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.keyID == nil {
+ return ""
+ }
+ return *(h.keyID)
+}
+
+func (h *stdHeaders) Type() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.typ == nil {
+ return ""
+ }
+ return *(h.typ)
+}
+
+func (h *stdHeaders) X509CertChain() []string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.x509CertChain
+}
+
+func (h *stdHeaders) X509CertThumbprint() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.x509CertThumbprint == nil {
+ return ""
+ }
+ return *(h.x509CertThumbprint)
+}
+
+func (h *stdHeaders) X509CertThumbprintS256() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.x509CertThumbprintS256 == nil {
+ return ""
+ }
+ return *(h.x509CertThumbprintS256)
+}
+
+func (h *stdHeaders) X509URL() string {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ if h.x509URL == nil {
+ return ""
+ }
+ return *(h.x509URL)
+}
+
+func (h *stdHeaders) DecodeCtx() DecodeCtx {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.dc
+}
+
+func (h *stdHeaders) SetDecodeCtx(dc DecodeCtx) {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ h.dc = dc
+}
+
+func (h *stdHeaders) rawBuffer() []byte {
+ return h.raw
+}
+
+func (h *stdHeaders) makePairs() []*HeaderPair {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ var pairs []*HeaderPair
+ if h.algorithm != nil {
+ pairs = append(pairs, &HeaderPair{Key: AlgorithmKey, Value: *(h.algorithm)})
+ }
+ if h.contentType != nil {
+ pairs = append(pairs, &HeaderPair{Key: ContentTypeKey, Value: *(h.contentType)})
+ }
+ if h.critical != nil {
+ pairs = append(pairs, &HeaderPair{Key: CriticalKey, Value: h.critical})
+ }
+ if h.jwk != nil {
+ pairs = append(pairs, &HeaderPair{Key: JWKKey, Value: h.jwk})
+ }
+ if h.jwkSetURL != nil {
+ pairs = append(pairs, &HeaderPair{Key: JWKSetURLKey, Value: *(h.jwkSetURL)})
+ }
+ if h.keyID != nil {
+ pairs = append(pairs, &HeaderPair{Key: KeyIDKey, Value: *(h.keyID)})
+ }
+ if h.typ != nil {
+ pairs = append(pairs, &HeaderPair{Key: TypeKey, Value: *(h.typ)})
+ }
+ if h.x509CertChain != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509CertChainKey, Value: h.x509CertChain})
+ }
+ if h.x509CertThumbprint != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintKey, Value: *(h.x509CertThumbprint)})
+ }
+ if h.x509CertThumbprintS256 != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509CertThumbprintS256Key, Value: *(h.x509CertThumbprintS256)})
+ }
+ if h.x509URL != nil {
+ pairs = append(pairs, &HeaderPair{Key: X509URLKey, Value: *(h.x509URL)})
+ }
+ for k, v := range h.privateParams {
+ pairs = append(pairs, &HeaderPair{Key: k, Value: v})
+ }
+ sort.Slice(pairs, func(i, j int) bool {
+ return pairs[i].Key.(string) < pairs[j].Key.(string)
+ })
+ return pairs
+}
+
+func (h *stdHeaders) PrivateParams() map[string]interface{} {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ return h.privateParams
+}
+
+func (h *stdHeaders) Get(name string) (interface{}, bool) {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ switch name {
+ case AlgorithmKey:
+ if h.algorithm == nil {
+ return nil, false
+ }
+ return *(h.algorithm), true
+ case ContentTypeKey:
+ if h.contentType == nil {
+ return nil, false
+ }
+ return *(h.contentType), true
+ case CriticalKey:
+ if h.critical == nil {
+ return nil, false
+ }
+ return h.critical, true
+ case JWKKey:
+ if h.jwk == nil {
+ return nil, false
+ }
+ return h.jwk, true
+ case JWKSetURLKey:
+ if h.jwkSetURL == nil {
+ return nil, false
+ }
+ return *(h.jwkSetURL), true
+ case KeyIDKey:
+ if h.keyID == nil {
+ return nil, false
+ }
+ return *(h.keyID), true
+ case TypeKey:
+ if h.typ == nil {
+ return nil, false
+ }
+ return *(h.typ), true
+ case X509CertChainKey:
+ if h.x509CertChain == nil {
+ return nil, false
+ }
+ return h.x509CertChain, true
+ case X509CertThumbprintKey:
+ if h.x509CertThumbprint == nil {
+ return nil, false
+ }
+ return *(h.x509CertThumbprint), true
+ case X509CertThumbprintS256Key:
+ if h.x509CertThumbprintS256 == nil {
+ return nil, false
+ }
+ return *(h.x509CertThumbprintS256), true
+ case X509URLKey:
+ if h.x509URL == nil {
+ return nil, false
+ }
+ return *(h.x509URL), true
+ default:
+ v, ok := h.privateParams[name]
+ return v, ok
+ }
+}
+
+func (h *stdHeaders) Set(name string, value interface{}) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ return h.setNoLock(name, value)
+}
+
+func (h *stdHeaders) setNoLock(name string, value interface{}) error {
+ switch name {
+ case AlgorithmKey:
+ var acceptor jwa.SignatureAlgorithm
+ if err := acceptor.Accept(value); err != nil {
+ return errors.Wrapf(err, `invalid value for %s key`, AlgorithmKey)
+ }
+ h.algorithm = &acceptor
+ return nil
+ case ContentTypeKey:
+ if v, ok := value.(string); ok {
+ h.contentType = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, ContentTypeKey, value)
+ case CriticalKey:
+ if v, ok := value.([]string); ok {
+ h.critical = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, CriticalKey, value)
+ case JWKKey:
+ if v, ok := value.(jwk.Key); ok {
+ h.jwk = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, JWKKey, value)
+ case JWKSetURLKey:
+ if v, ok := value.(string); ok {
+ h.jwkSetURL = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, JWKSetURLKey, value)
+ case KeyIDKey:
+ if v, ok := value.(string); ok {
+ h.keyID = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, KeyIDKey, value)
+ case TypeKey:
+ if v, ok := value.(string); ok {
+ h.typ = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, TypeKey, value)
+ case X509CertChainKey:
+ if v, ok := value.([]string); ok {
+ h.x509CertChain = v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509CertChainKey, value)
+ case X509CertThumbprintKey:
+ if v, ok := value.(string); ok {
+ h.x509CertThumbprint = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintKey, value)
+ case X509CertThumbprintS256Key:
+ if v, ok := value.(string); ok {
+ h.x509CertThumbprintS256 = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509CertThumbprintS256Key, value)
+ case X509URLKey:
+ if v, ok := value.(string); ok {
+ h.x509URL = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, X509URLKey, value)
+ default:
+ if h.privateParams == nil {
+ h.privateParams = map[string]interface{}{}
+ }
+ h.privateParams[name] = value
+ }
+ return nil
+}
+
+func (h *stdHeaders) Remove(key string) error {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ switch key {
+ case AlgorithmKey:
+ h.algorithm = nil
+ case ContentTypeKey:
+ h.contentType = nil
+ case CriticalKey:
+ h.critical = nil
+ case JWKKey:
+ h.jwk = nil
+ case JWKSetURLKey:
+ h.jwkSetURL = nil
+ case KeyIDKey:
+ h.keyID = nil
+ case TypeKey:
+ h.typ = nil
+ case X509CertChainKey:
+ h.x509CertChain = nil
+ case X509CertThumbprintKey:
+ h.x509CertThumbprint = nil
+ case X509CertThumbprintS256Key:
+ h.x509CertThumbprintS256 = nil
+ case X509URLKey:
+ h.x509URL = nil
+ default:
+ delete(h.privateParams, key)
+ }
+ return nil
+}
+
+func (h *stdHeaders) UnmarshalJSON(buf []byte) error {
+ h.algorithm = nil
+ h.contentType = nil
+ h.critical = nil
+ h.jwk = nil
+ h.jwkSetURL = nil
+ h.keyID = nil
+ h.typ = nil
+ h.x509CertChain = nil
+ h.x509CertThumbprint = nil
+ h.x509CertThumbprintS256 = nil
+ h.x509URL = nil
+ dec := json.NewDecoder(bytes.NewReader(buf))
+LOOP:
+ for {
+ tok, err := dec.Token()
+ if err != nil {
+ return errors.Wrap(err, `error reading token`)
+ }
+ switch tok := tok.(type) {
+ case json.Delim:
+ // Assuming we're doing everything correctly, we should ONLY
+ // get either '{' or '}' here.
+ if tok == '}' { // End of object
+ break LOOP
+ } else if tok != '{' {
+ return errors.Errorf(`expected '{', but got '%c'`, tok)
+ }
+ case string: // Objects can only have string keys
+ switch tok {
+ case AlgorithmKey:
+ var decoded jwa.SignatureAlgorithm
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, AlgorithmKey)
+ }
+ h.algorithm = &decoded
+ case ContentTypeKey:
+ if err := json.AssignNextStringToken(&h.contentType, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, ContentTypeKey)
+ }
+ case CriticalKey:
+ var decoded []string
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, CriticalKey)
+ }
+ h.critical = decoded
+ case JWKKey:
+ var buf json.RawMessage
+ if err := dec.Decode(&buf); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, JWKKey)
+ }
+ key, err := jwk.ParseKey(buf)
+ if err != nil {
+ return errors.Wrapf(err, `failed to parse JWK for key %s`, JWKKey)
+ }
+ h.jwk = key
+ case JWKSetURLKey:
+ if err := json.AssignNextStringToken(&h.jwkSetURL, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, JWKSetURLKey)
+ }
+ case KeyIDKey:
+ if err := json.AssignNextStringToken(&h.keyID, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, KeyIDKey)
+ }
+ case TypeKey:
+ if err := json.AssignNextStringToken(&h.typ, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, TypeKey)
+ }
+ case X509CertChainKey:
+ var decoded []string
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509CertChainKey)
+ }
+ h.x509CertChain = decoded
+ case X509CertThumbprintKey:
+ if err := json.AssignNextStringToken(&h.x509CertThumbprint, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintKey)
+ }
+ case X509CertThumbprintS256Key:
+ if err := json.AssignNextStringToken(&h.x509CertThumbprintS256, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509CertThumbprintS256Key)
+ }
+ case X509URLKey:
+ if err := json.AssignNextStringToken(&h.x509URL, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, X509URLKey)
+ }
+ default:
+ decoded, err := registry.Decode(dec, tok)
+ if err != nil {
+ return err
+ }
+ h.setNoLock(tok, decoded)
+ }
+ default:
+ return errors.Errorf(`invalid token %T`, tok)
+ }
+ }
+
+ if dc := h.dc; dc != nil {
+ if dc.CollectRaw() {
+ h.raw = buf
+ }
+ }
+ return nil
+}
+
+func (h stdHeaders) MarshalJSON() ([]byte, error) {
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+ buf.WriteByte('{')
+ enc := json.NewEncoder(buf)
+ for i, p := range h.makePairs() {
+ if i > 0 {
+ buf.WriteRune(',')
+ }
+ buf.WriteRune('"')
+ buf.WriteString(p.Key.(string))
+ buf.WriteString(`":`)
+ v := p.Value
+ switch v := v.(type) {
+ case []byte:
+ buf.WriteRune('"')
+ buf.WriteString(base64.EncodeToString(v))
+ buf.WriteRune('"')
+ default:
+ if err := enc.Encode(v); err != nil {
+ errors.Errorf(`failed to encode value for field %s`, p.Key)
+ }
+ buf.Truncate(buf.Len() - 1)
+ }
+ }
+ buf.WriteByte('}')
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/hmac.go b/vendor/github.com/lestrrat-go/jwx/jws/hmac.go
new file mode 100644
index 000000000..e85471ac1
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/hmac.go
@@ -0,0 +1,77 @@
+package jws
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/sha512"
+ "hash"
+
+ "github.com/lestrrat-go/jwx/internal/keyconv"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+var hmacSignFuncs = map[jwa.SignatureAlgorithm]hmacSignFunc{}
+
+func init() {
+ algs := map[jwa.SignatureAlgorithm]func() hash.Hash{
+ jwa.HS256: sha256.New,
+ jwa.HS384: sha512.New384,
+ jwa.HS512: sha512.New,
+ }
+
+ for alg, h := range algs {
+ hmacSignFuncs[alg] = makeHMACSignFunc(h)
+ }
+}
+
+func newHMACSigner(alg jwa.SignatureAlgorithm) Signer {
+ return &HMACSigner{
+ alg: alg,
+ sign: hmacSignFuncs[alg], // we know this will succeed
+ }
+}
+
+func makeHMACSignFunc(hfunc func() hash.Hash) hmacSignFunc {
+ return func(payload []byte, key []byte) ([]byte, error) {
+ h := hmac.New(hfunc, key)
+ if _, err := h.Write(payload); err != nil {
+ return nil, errors.Wrap(err, "failed to write payload using hmac")
+ }
+ return h.Sum(nil), nil
+ }
+}
+
+func (s HMACSigner) Algorithm() jwa.SignatureAlgorithm {
+ return s.alg
+}
+
+func (s HMACSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
+ var hmackey []byte
+ if err := keyconv.ByteSliceKey(&hmackey, key); err != nil {
+ return nil, errors.Wrapf(err, `invalid key type %T. []byte is required`, key)
+ }
+
+ if len(hmackey) == 0 {
+ return nil, errors.New(`missing key while signing payload`)
+ }
+
+ return s.sign(payload, hmackey)
+}
+
+func newHMACVerifier(alg jwa.SignatureAlgorithm) Verifier {
+ s := newHMACSigner(alg)
+ return &HMACVerifier{signer: s}
+}
+
+func (v HMACVerifier) Verify(payload, signature []byte, key interface{}) (err error) {
+ expected, err := v.signer.Sign(payload, key)
+ if err != nil {
+ return errors.Wrap(err, `failed to generated signature`)
+ }
+
+ if !hmac.Equal(signature, expected) {
+ return errors.New(`failed to match hmac signature`)
+ }
+ return nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/interface.go b/vendor/github.com/lestrrat-go/jwx/jws/interface.go
new file mode 100644
index 000000000..b92f543ad
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/interface.go
@@ -0,0 +1,105 @@
+package jws
+
+import (
+ "github.com/lestrrat-go/iter/mapiter"
+ "github.com/lestrrat-go/jwx/internal/iter"
+ "github.com/lestrrat-go/jwx/jwa"
+)
+
+type DecodeCtx interface {
+ CollectRaw() bool
+}
+
+// Message represents a full JWS encoded message. Flattened serialization
+// is not supported as a struct, but rather it's represented as a
+// Message struct with only one `signature` element.
+//
+// Do not expect to use the Message object to verify or construct a
+// signed payload with. You should only use this when you want to actually
+// programmatically view the contents of the full JWS payload.
+//
+// As of this version, there is one big incompatibility when using Message
+// objects to convert between compact and JSON representations.
+// The protected header is sometimes encoded differently from the original
+// message and the JSON serialization that we use in Go.
+//
+// For example, the protected header `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9`
+// decodes to
+//
+// {"typ":"JWT",
+// "alg":"HS256"}
+//
+// However, when we parse this into a message, we create a jws.Header object,
+// which, when we marshal into a JSON object again, becomes
+//
+// {"typ":"JWT","alg":"HS256"}
+//
+// Notice that serialization lacks a line break and a space between `"JWT",`
+// and `"alg"`. This causes a problem when verifying the signatures AFTER
+// a compact JWS message has been unmarshaled into a jws.Message.
+//
+// jws.Verify() doesn't go through this step, and therefore this does not
+// manifest itself. However, you may see this discrepancy when you manually
+// go through these conversions, and/or use the `jwx` tool like so:
+//
+// jwx jws parse message.jws | jwx jws verify --key somekey.jwk --stdin
+//
+// In this scenario, the first `jwx jws parse` outputs a parsed jws.Message
+// which is marshaled into JSON. At this point the message's protected
+// headers and the signatures don't match.
+//
+// To sign and verify, use the appropriate `Sign()` and `Verify()` functions.
+type Message struct {
+ dc DecodeCtx
+ payload []byte
+ signatures []*Signature
+ b64 bool // true if payload should be base64 encoded
+}
+
+type Signature struct {
+ dc DecodeCtx
+ headers Headers // Unprotected Headers
+ protected Headers // Protected Headers
+ signature []byte // Signature
+}
+
+type Visitor = iter.MapVisitor
+type VisitorFunc = iter.MapVisitorFunc
+type HeaderPair = mapiter.Pair
+type Iterator = mapiter.Iterator
+
+// Signer generates the signature for a given payload.
+type Signer interface {
+ // Sign creates a signature for the given payload.
+ // The scond argument is the key used for signing the payload, and is usually
+ // the private key type associated with the signature method. For example,
+ // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the
+ // `*"crypto/rsa".PrivateKey` type.
+ // Check the documentation for each signer for details
+ Sign([]byte, interface{}) ([]byte, error)
+
+ Algorithm() jwa.SignatureAlgorithm
+}
+
+type hmacSignFunc func([]byte, []byte) ([]byte, error)
+
+// HMACSigner uses crypto/hmac to sign the payloads.
+type HMACSigner struct {
+ alg jwa.SignatureAlgorithm
+ sign hmacSignFunc
+}
+
+type Verifier interface {
+ // Verify checks whether the payload and signature are valid for
+ // the given key.
+ // `key` is the key used for verifying the payload, and is usually
+ // the public key associated with the signature method. For example,
+ // for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the
+ // `*"crypto/rsa".PublicKey` type.
+ // Check the documentation for each verifier for details
+ Verify(payload []byte, signature []byte, key interface{}) error
+}
+
+type HMACVerifier struct {
+ signer Signer
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/io.go b/vendor/github.com/lestrrat-go/jwx/jws/io.go
new file mode 100644
index 000000000..da4b1df45
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/io.go
@@ -0,0 +1,23 @@
+// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT
+
+package jws
+
+import "os"
+
+// ReadFileOption describes options that can be passed to ReadFile.
+// Currently there are no options available that can be passed to ReadFile, but
+// it is provided here for anticipated future additions
+type ReadFileOption interface {
+ Option
+ readFileOption()
+}
+
+func ReadFile(path string, _ ...ReadFileOption) (*Message, error) {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+
+ defer f.Close()
+ return ParseReader(f)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/jws.go b/vendor/github.com/lestrrat-go/jwx/jws/jws.go
new file mode 100644
index 000000000..7f0facf6a
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/jws.go
@@ -0,0 +1,938 @@
+//go:generate ./gen.sh
+
+// Package jws implements the digital signature on JSON based data
+// structures as described in https://tools.ietf.org/html/rfc7515
+//
+// If you do not care about the details, the only things that you
+// would need to use are the following functions:
+//
+// jws.Sign(payload, algorithm, key)
+// jws.Verify(encodedjws, algorithm, key)
+//
+// To sign, simply use `jws.Sign`. `payload` is a []byte buffer that
+// contains whatever data you want to sign. `alg` is one of the
+// jwa.SignatureAlgorithm constants from package jwa. For RSA and
+// ECDSA family of algorithms, you will need to prepare a private key.
+// For HMAC family, you just need a []byte value. The `jws.Sign`
+// function will return the encoded JWS message on success.
+//
+// To verify, use `jws.Verify`. It will parse the `encodedjws` buffer
+// and verify the result using `algorithm` and `key`. Upon successful
+// verification, the original payload is returned, so you can work on it.
+package jws
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "reflect"
+ "strings"
+ "sync"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/lestrrat-go/backoff/v2"
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/lestrrat-go/jwx/x25519"
+ "github.com/pkg/errors"
+)
+
+var registry = json.NewRegistry()
+
+type payloadSigner struct {
+ signer Signer
+ key interface{}
+ protected Headers
+ public Headers
+}
+
+func (s *payloadSigner) Sign(payload []byte) ([]byte, error) {
+ return s.signer.Sign(payload, s.key)
+}
+
+func (s *payloadSigner) Algorithm() jwa.SignatureAlgorithm {
+ return s.signer.Algorithm()
+}
+
+func (s *payloadSigner) ProtectedHeader() Headers {
+ return s.protected
+}
+
+func (s *payloadSigner) PublicHeader() Headers {
+ return s.public
+}
+
+var signers = make(map[jwa.SignatureAlgorithm]Signer)
+var muSigner = &sync.Mutex{}
+
+// Sign generates a signature for the given payload, and serializes
+// it in compact serialization format. In this format you may NOT use
+// multiple signers.
+//
+// The `alg` parameter is the identifier for the signature algorithm
+// that should be used.
+//
+// For the `key` parameter, any of the following is accepted:
+// * A "raw" key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc)
+// * A crypto.Signer
+// * A jwk.Key
+//
+// A `crypto.Signer` is used when the private part of a key is
+// kept in an inaccessible location, such as hardware.
+// `crypto.Signer` is currently supported for RSA, ECDSA, and EdDSA
+// family of algorithms.
+//
+// If the key is a jwk.Key and the key contains a key ID (`kid` field),
+// then it is added to the protected header generated by the signature
+//
+// The algorithm specified in the `alg` parameter must be able to support
+// the type of key you provided, otherwise an error is returned.
+//
+// If you would like to pass custom headers, use the WithHeaders option.
+//
+// If the headers contain "b64" field, then the boolean value for the field
+// is respected when creating the compact serialization form. That is,
+// if you specify a header with `{"b64": false}`, then the payload is
+// not base64 encoded.
+func Sign(payload []byte, alg jwa.SignatureAlgorithm, key interface{}, options ...SignOption) ([]byte, error) {
+ var hdrs Headers
+ for _, o := range options {
+ //nolint:forcetypeassert
+ switch o.Ident() {
+ case identHeaders{}:
+ hdrs = o.Value().(Headers)
+ }
+ }
+
+ muSigner.Lock()
+ signer, ok := signers[alg]
+ if !ok {
+ v, err := NewSigner(alg)
+ if err != nil {
+ muSigner.Unlock()
+ return nil, errors.Wrap(err, `failed to create signer`)
+ }
+ signers[alg] = v
+ signer = v
+ }
+ muSigner.Unlock()
+
+ sig := &Signature{protected: hdrs}
+ _, signature, err := sig.Sign(payload, signer, key)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed sign payload`)
+ }
+
+ return signature, nil
+}
+
+// SignMulti accepts multiple signers via the options parameter,
+// and creates a JWS in JSON serialization format that contains
+// signatures from applying aforementioned signers.
+//
+// Use `jws.WithSigner(...)` to specify values how to generate
+// each signature in the `"signatures": [ ... ]` field.
+func SignMulti(payload []byte, options ...Option) ([]byte, error) {
+ var signers []*payloadSigner
+ for _, o := range options {
+ switch o.Ident() {
+ case identPayloadSigner{}:
+ signers = append(signers, o.Value().(*payloadSigner))
+ }
+ }
+
+ if len(signers) == 0 {
+ return nil, errors.New(`no signers provided`)
+ }
+
+ var result Message
+
+ result.payload = payload
+
+ result.signatures = make([]*Signature, 0, len(signers))
+ for i, signer := range signers {
+ protected := signer.ProtectedHeader()
+ if protected == nil {
+ protected = NewHeaders()
+ }
+
+ if err := protected.Set(AlgorithmKey, signer.Algorithm()); err != nil {
+ return nil, errors.Wrap(err, `failed to set "alg" header`)
+ }
+
+ if key, ok := signer.key.(jwk.Key); ok {
+ if kid := key.KeyID(); kid != "" {
+ if err := protected.Set(KeyIDKey, kid); err != nil {
+ return nil, errors.Wrap(err, `failed to set "kid" header`)
+ }
+ }
+ }
+ sig := &Signature{
+ headers: signer.PublicHeader(),
+ protected: protected,
+ }
+ _, _, err := sig.Sign(payload, signer.signer, signer.key)
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to generate signature for signer #%d (alg=%s)`, i, signer.Algorithm())
+ }
+
+ result.signatures = append(result.signatures, sig)
+ }
+
+ return json.Marshal(result)
+}
+
+type verifyCtx struct {
+ dst *Message
+ detachedPayload []byte
+ alg jwa.SignatureAlgorithm
+ key interface{}
+ useJKU bool
+ jwksFetcher JWKSetFetcher
+ // This is only used to differentiate compact/JSON serialization
+ // because certain features are enabled/disabled in each
+ isJSON bool
+}
+
+var allowNoneWhitelist = jwk.WhitelistFunc(func(string) bool {
+ return false
+})
+
+// VerifyAuto is a special case of Verify(), where verification is done
+// using verifications parameters that can be obtained using the information
+// that is carried within the JWS message itself.
+//
+// Currently it only supports verification via `jku` which will be fetched
+// using the object specified in `jws.JWKSetFetcher`. Note that URLs in `jku` can
+// only have https scheme.
+//
+// Using this function will result in your program accessing remote resources via https,
+// and therefore extreme caution should be taken which urls can be accessed.
+//
+// Without specifying extra arguments, the default `jws.JWKSetFetcher` will be
+// configured with a whitelist that rejects *ALL URLSs*. This is to
+// protect users from unintentionally allowing their projects to
+// make unwanted requests. Therefore you must explicitly provide an
+// instance of `jwk.Whitelist` that does what you want.
+//
+// If you want open access to any URLs in the `jku`, you can do this by
+// using `jwk.InsecureWhitelist` as the whitelist, but this should be avoided in
+// most cases, especially if the payload comes from outside of a controlled
+// environment.
+//
+// It is also advised that you consider using some sort of backoff via `jws.WithFetchBackoff`
+//
+// Alternatively, you can provide your own `jws.JWKSetFetcher`. In this case
+// there is no way for the framework to force you to set a whitelist, so the
+// default behavior is to allow any URLs. You are responsible for providing
+// your own safety measures.
+func VerifyAuto(buf []byte, options ...VerifyOption) ([]byte, error) {
+ var ctx verifyCtx
+ // enable JKU processing
+ ctx.useJKU = true
+
+ var fetchOptions []jwk.FetchOption
+
+ //nolint:forcetypeassert
+ for _, option := range options {
+ switch option.Ident() {
+ case identMessage{}:
+ ctx.dst = option.Value().(*Message)
+ case identDetachedPayload{}:
+ ctx.detachedPayload = option.Value().([]byte)
+ case identJWKSetFetcher{}:
+ ctx.jwksFetcher = option.Value().(JWKSetFetcher)
+ case identFetchWhitelist{}:
+ fetchOptions = append(fetchOptions, jwk.WithFetchWhitelist(option.Value().(jwk.Whitelist)))
+ case identFetchBackoff{}:
+ fetchOptions = append(fetchOptions, jwk.WithFetchBackoff(option.Value().(backoff.Policy)))
+ case identHTTPClient{}:
+ fetchOptions = append(fetchOptions, jwk.WithHTTPClient(option.Value().(*http.Client)))
+ }
+ }
+
+ // We shove the default Whitelist in the front of the option list.
+ // If the user provided one, it will overwrite our default value
+ if ctx.jwksFetcher == nil {
+ fetchOptions = append([]jwk.FetchOption{jwk.WithFetchWhitelist(allowNoneWhitelist)}, fetchOptions...)
+ ctx.jwksFetcher = NewJWKSetFetcher(fetchOptions...)
+ }
+
+ return ctx.verify(buf)
+}
+
+// Verify checks if the given JWS message is verifiable using `alg` and `key`.
+// `key` may be a "raw" key (e.g. rsa.PublicKey) or a jwk.Key
+//
+// If the verification is successful, `err` is nil, and the content of the
+// payload that was signed is returned. If you need more fine-grained
+// control of the verification process, manually generate a
+// `Verifier` in `verify` subpackage, and call `Verify` method on it.
+// If you need to access signatures and JOSE headers in a JWS message,
+// use `Parse` function to get `Message` object.
+func Verify(buf []byte, alg jwa.SignatureAlgorithm, key interface{}, options ...VerifyOption) ([]byte, error) {
+ var ctx verifyCtx
+ ctx.alg = alg
+ ctx.key = key
+ //nolint:forcetypeassert
+ for _, option := range options {
+ switch option.Ident() {
+ case identMessage{}:
+ ctx.dst = option.Value().(*Message)
+ case identDetachedPayload{}:
+ ctx.detachedPayload = option.Value().([]byte)
+ default:
+ return nil, errors.Errorf(`invalid jws.VerifyOption %q passed`, `With`+strings.TrimPrefix(fmt.Sprintf(`%T`, option.Ident()), `jws.ident`))
+ }
+ }
+
+ return ctx.verify(buf)
+}
+
+func (ctx *verifyCtx) verify(buf []byte) ([]byte, error) {
+ buf = bytes.TrimSpace(buf)
+ if len(buf) == 0 {
+ return nil, errors.New(`attempt to verify empty buffer`)
+ }
+
+ if buf[0] == '{' {
+ return ctx.verifyJSON(buf)
+ }
+ return ctx.verifyCompact(buf)
+}
+
+// VerifySet uses keys store in a jwk.Set to verify the payload in `buf`.
+//
+// In order for `VerifySet()` to use a key in the given set, the
+// `jwk.Key` object must have a valid "alg" field, and it also must
+// have either an empty value or the value "sig" in the "use" field.
+//
+// Furthermore if the JWS signature asks for a spefici "kid", the
+// `jwk.Key` must have the same "kid" as the signature.
+func VerifySet(buf []byte, set jwk.Set) ([]byte, error) {
+ n := set.Len()
+ for i := 0; i < n; i++ {
+ key, ok := set.Get(i)
+ if !ok {
+ continue
+ }
+ if key.Algorithm() == "" { // algorithm is not
+ continue
+ }
+
+ if usage := key.KeyUsage(); usage != "" && usage != jwk.ForSignature.String() {
+ continue
+ }
+
+ buf, err := Verify(buf, jwa.SignatureAlgorithm(key.Algorithm()), key)
+ if err != nil {
+ continue
+ }
+
+ return buf, nil
+ }
+
+ return nil, errors.New(`failed to verify message with any of the keys in the jwk.Set object`)
+}
+
+func (ctx *verifyCtx) verifyJSON(signed []byte) ([]byte, error) {
+ ctx.isJSON = true
+
+ var m Message
+ m.SetDecodeCtx(collectRawCtx{})
+ defer m.clearRaw()
+ if err := json.Unmarshal(signed, &m); err != nil {
+ return nil, errors.Wrap(err, `failed to unmarshal JSON message`)
+ }
+ m.SetDecodeCtx(nil)
+
+ if len(m.payload) != 0 && ctx.detachedPayload != nil {
+ return nil, errors.New(`can't specify detached payload for JWS with payload`)
+ }
+
+ if ctx.detachedPayload != nil {
+ m.payload = ctx.detachedPayload
+ }
+
+ // Pre-compute the base64 encoded version of payload
+ var payload string
+ if m.b64 {
+ payload = base64.EncodeToString(m.payload)
+ } else {
+ payload = string(m.payload)
+ }
+
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+
+ for i, sig := range m.signatures {
+ buf.Reset()
+
+ var encodedProtectedHeader string
+ if rbp, ok := sig.protected.(interface{ rawBuffer() []byte }); ok {
+ if raw := rbp.rawBuffer(); raw != nil {
+ encodedProtectedHeader = base64.EncodeToString(raw)
+ }
+ }
+
+ if encodedProtectedHeader == "" {
+ protected, err := json.Marshal(sig.protected)
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to marshal "protected" for signature #%d`, i+1)
+ }
+
+ encodedProtectedHeader = base64.EncodeToString(protected)
+ }
+
+ buf.WriteString(encodedProtectedHeader)
+ buf.WriteByte('.')
+ buf.WriteString(payload)
+
+ if !ctx.useJKU {
+ if hdr := sig.protected; hdr != nil && hdr.KeyID() != "" {
+ if jwkKey, ok := ctx.key.(jwk.Key); ok {
+ if jwkKey.KeyID() != hdr.KeyID() {
+ continue
+ }
+ }
+ }
+
+ verifier, err := NewVerifier(ctx.alg)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create verifier")
+ }
+
+ if _, err := ctx.tryVerify(verifier, sig.protected, buf.Bytes(), sig.signature, m.payload); err == nil {
+ if ctx.dst != nil {
+ *(ctx.dst) = m
+ }
+ return m.payload, nil
+ }
+ // Don't fallthrough or bail out. Try the next signature.
+ continue
+ }
+
+ if _, err := ctx.verifyJKU(sig.protected, buf.Bytes(), sig.signature, m.payload); err == nil {
+ if ctx.dst != nil {
+ *(ctx.dst) = m
+ }
+ return m.payload, nil
+ }
+ // try next
+ }
+ return nil, errors.New(`could not verify with any of the signatures`)
+}
+
+// get the value of b64 header field.
+// If the field does not exist, returns true (default)
+// Otherwise return the value specified by the header field.
+func getB64Value(hdr Headers) bool {
+ b64raw, ok := hdr.Get("b64")
+ if !ok {
+ return true // default
+ }
+
+ b64, ok := b64raw.(bool) // default
+ if !ok {
+ return false
+ }
+ return b64
+}
+
+func (ctx *verifyCtx) verifyCompact(signed []byte) ([]byte, error) {
+ protected, payload, signature, err := SplitCompact(signed)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed extract from compact serialization format`)
+ }
+
+ verifyBuf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(verifyBuf)
+
+ verifyBuf.Write(protected)
+ verifyBuf.WriteByte('.')
+ if len(payload) == 0 && ctx.detachedPayload != nil {
+ payload = ctx.detachedPayload
+ }
+ verifyBuf.Write(payload)
+
+ decodedSignature, err := base64.Decode(signature)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decode signature`)
+ }
+
+ hdr := NewHeaders()
+ decodedProtected, err := base64.Decode(protected)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decode headers`)
+ }
+
+ if err := json.Unmarshal(decodedProtected, hdr); err != nil {
+ return nil, errors.Wrap(err, `failed to decode headers`)
+ }
+
+ if !ctx.useJKU {
+ if hdr.KeyID() != "" {
+ if jwkKey, ok := ctx.key.(jwk.Key); ok {
+ if jwkKey.KeyID() != hdr.KeyID() {
+ return nil, errors.New(`"kid" fields do not match`)
+ }
+ }
+ }
+
+ verifier, err := NewVerifier(ctx.alg)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create verifier")
+ }
+
+ return ctx.tryVerify(verifier, hdr, verifyBuf.Bytes(), decodedSignature, payload)
+ }
+
+ return ctx.verifyJKU(hdr, verifyBuf.Bytes(), decodedSignature, payload)
+}
+
+// JWKSetFetcher is used to fetch JWK Set spcified in the `jku` field.
+type JWKSetFetcher interface {
+ Fetch(string) (jwk.Set, error)
+}
+
+// SimpleJWKSetFetcher is the default object used to fetch JWK Sets specified in `jku`,
+// which uses `jwk.Fetch()`
+//
+// For more complicated cases, such as using `jwk.AutoRefetch`, you will have to
+// create your custom instance of `jws.JWKSetFetcher`
+type SimpleJWKSetFetcher struct {
+ options []jwk.FetchOption
+}
+
+func NewJWKSetFetcher(options ...jwk.FetchOption) *SimpleJWKSetFetcher {
+ return &SimpleJWKSetFetcher{options: options}
+}
+
+func (f *SimpleJWKSetFetcher) Fetch(u string) (jwk.Set, error) {
+ return jwk.Fetch(context.TODO(), u, f.options...)
+}
+
+type JWKSetFetchFunc func(string) (jwk.Set, error)
+
+func (f JWKSetFetchFunc) Fetch(u string) (jwk.Set, error) {
+ return f(u)
+}
+
+func (ctx *verifyCtx) verifyJKU(hdr Headers, verifyBuf, decodedSignature, payload []byte) ([]byte, error) {
+ u := hdr.JWKSetURL()
+ if u == "" {
+ return nil, errors.New(`use of "jku" field specified, but the field is empty`)
+ }
+ uo, err := url.Parse(u)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to parse "jku"`)
+ }
+ if uo.Scheme != "https" {
+ return nil, errors.New(`url in "jku" must be HTTPS`)
+ }
+
+ set, err := ctx.jwksFetcher.Fetch(u)
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to fetch "jku"`)
+ }
+
+ // Because we're using a JWKS here, we MUST have "kid" that matches
+ // the payload
+ if hdr.KeyID() == "" {
+ return nil, errors.Errorf(`"kid" is required on the JWS message to use "jku"`)
+ }
+
+ key, ok := set.LookupKeyID(hdr.KeyID())
+ if !ok {
+ return nil, errors.New(`key specified via "kid" is not present in the JWK set specified by "jku"`)
+ }
+
+ // hooray, we found a key. Now the algorithm will have to be inferred.
+ algs, err := AlgorithmsForKey(key)
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to get a list of signature methods for key type %s`, key.KeyType())
+ }
+
+ // for each of these algorithms, just ... keep trying ...
+ ctx.key = key
+ hdrAlg := hdr.Algorithm()
+ for _, alg := range algs {
+ // if we have a "alg" field in the JWS, we can only proceed if
+ // the inferred algorithm matches
+ if hdrAlg != "" && hdrAlg != alg {
+ continue
+ }
+
+ verifier, err := NewVerifier(alg)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to create verifier")
+ }
+
+ if decoded, err := ctx.tryVerify(verifier, hdr, verifyBuf, decodedSignature, payload); err == nil {
+ return decoded, nil
+ }
+ }
+ return nil, errors.New(`failed to verify payload using key in "jku"`)
+}
+
+func (ctx *verifyCtx) tryVerify(verifier Verifier, hdr Headers, buf, decodedSignature, payload []byte) ([]byte, error) {
+ if err := verifier.Verify(buf, decodedSignature, ctx.key); err != nil {
+ return nil, errors.Wrap(err, `failed to verify message`)
+ }
+
+ var decodedPayload []byte
+
+ // When verifying JSON messages, we do not need to decode
+ // the payload, as we already have it
+ if !ctx.isJSON {
+ // This is a special case for RFC7797
+ if !getB64Value(hdr) { // it's not base64 encoded
+ decodedPayload = payload
+ }
+
+ if decodedPayload == nil {
+ v, err := base64.Decode(payload)
+ if err != nil {
+ return nil, errors.Wrap(err, `message verified, failed to decode payload`)
+ }
+ decodedPayload = v
+ }
+
+ // For compact serialization, we need to create and assign the message
+ // if requested
+ if ctx.dst != nil {
+ // Construct a new Message object
+ m := NewMessage()
+ m.SetPayload(decodedPayload)
+ sig := NewSignature()
+ sig.SetProtectedHeaders(hdr)
+ sig.SetSignature(decodedSignature)
+ m.AppendSignature(sig)
+
+ *(ctx.dst) = *m
+ }
+ }
+ return decodedPayload, nil
+}
+
+// This is an "optimized" ioutil.ReadAll(). It will attempt to read
+// all of the contents from the reader IF the reader is of a certain
+// concrete type.
+func readAll(rdr io.Reader) ([]byte, bool) {
+ switch rdr.(type) {
+ case *bytes.Reader, *bytes.Buffer, *strings.Reader:
+ data, err := ioutil.ReadAll(rdr)
+ if err != nil {
+ return nil, false
+ }
+ return data, true
+ default:
+ return nil, false
+ }
+}
+
+// Parse parses contents from the given source and creates a jws.Message
+// struct. The input can be in either compact or full JSON serialization.
+func Parse(src []byte) (*Message, error) {
+ for i := 0; i < len(src); i++ {
+ r := rune(src[i])
+ if r >= utf8.RuneSelf {
+ r, _ = utf8.DecodeRune(src)
+ }
+ if !unicode.IsSpace(r) {
+ if r == '{' {
+ return parseJSON(src)
+ }
+ return parseCompact(src)
+ }
+ }
+ return nil, errors.New("invalid byte sequence")
+}
+
+// Parse parses contents from the given source and creates a jws.Message
+// struct. The input can be in either compact or full JSON serialization.
+func ParseString(src string) (*Message, error) {
+ return Parse([]byte(src))
+}
+
+// Parse parses contents from the given source and creates a jws.Message
+// struct. The input can be in either compact or full JSON serialization.
+func ParseReader(src io.Reader) (*Message, error) {
+ if data, ok := readAll(src); ok {
+ return Parse(data)
+ }
+
+ rdr := bufio.NewReader(src)
+ var first rune
+ for {
+ r, _, err := rdr.ReadRune()
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to read rune`)
+ }
+ if !unicode.IsSpace(r) {
+ first = r
+ if err := rdr.UnreadRune(); err != nil {
+ return nil, errors.Wrap(err, `failed to unread rune`)
+ }
+
+ break
+ }
+ }
+
+ var parser func(io.Reader) (*Message, error)
+ if first == '{' {
+ parser = parseJSONReader
+ } else {
+ parser = parseCompactReader
+ }
+
+ m, err := parser(rdr)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to parse jws message`)
+ }
+
+ return m, nil
+}
+
+func parseJSONReader(src io.Reader) (result *Message, err error) {
+ var m Message
+ if err := json.NewDecoder(src).Decode(&m); err != nil {
+ return nil, errors.Wrap(err, `failed to unmarshal jws message`)
+ }
+ return &m, nil
+}
+
+func parseJSON(data []byte) (result *Message, err error) {
+ var m Message
+ if err := json.Unmarshal(data, &m); err != nil {
+ return nil, errors.Wrap(err, `failed to unmarshal jws message`)
+ }
+ return &m, nil
+}
+
+// SplitCompact splits a JWT and returns its three parts
+// separately: protected headers, payload and signature.
+func SplitCompact(src []byte) ([]byte, []byte, []byte, error) {
+ parts := bytes.Split(src, []byte("."))
+ if len(parts) < 3 {
+ return nil, nil, nil, errors.New(`invalid number of segments`)
+ }
+ return parts[0], parts[1], parts[2], nil
+}
+
+// SplitCompactString splits a JWT and returns its three parts
+// separately: protected headers, payload and signature.
+func SplitCompactString(src string) ([]byte, []byte, []byte, error) {
+ parts := strings.Split(src, ".")
+ if len(parts) < 3 {
+ return nil, nil, nil, errors.New(`invalid number of segments`)
+ }
+ return []byte(parts[0]), []byte(parts[1]), []byte(parts[2]), nil
+}
+
+// SplitCompactReader splits a JWT and returns its three parts
+// separately: protected headers, payload and signature.
+func SplitCompactReader(rdr io.Reader) ([]byte, []byte, []byte, error) {
+ if data, ok := readAll(rdr); ok {
+ return SplitCompact(data)
+ }
+
+ var protected []byte
+ var payload []byte
+ var signature []byte
+ var periods int
+ var state int
+
+ buf := make([]byte, 4096)
+ var sofar []byte
+
+ for {
+ // read next bytes
+ n, err := rdr.Read(buf)
+ // return on unexpected read error
+ if err != nil && err != io.EOF {
+ return nil, nil, nil, errors.Wrap(err, `unexpected end of input`)
+ }
+
+ // append to current buffer
+ sofar = append(sofar, buf[:n]...)
+ // loop to capture multiple '.' in current buffer
+ for loop := true; loop; {
+ var i = bytes.IndexByte(sofar, '.')
+ if i == -1 && err != io.EOF {
+ // no '.' found -> exit and read next bytes (outer loop)
+ loop = false
+ continue
+ } else if i == -1 && err == io.EOF {
+ // no '.' found -> process rest and exit
+ i = len(sofar)
+ loop = false
+ } else {
+ // '.' found
+ periods++
+ }
+
+ // Reaching this point means we have found a '.' or EOF and process the rest of the buffer
+ switch state {
+ case 0:
+ protected = sofar[:i]
+ state++
+ case 1:
+ payload = sofar[:i]
+ state++
+ case 2:
+ signature = sofar[:i]
+ }
+ // Shorten current buffer
+ if len(sofar) > i {
+ sofar = sofar[i+1:]
+ }
+ }
+ // Exit on EOF
+ if err == io.EOF {
+ break
+ }
+ }
+ if periods != 2 {
+ return nil, nil, nil, errors.New(`invalid number of segments`)
+ }
+
+ return protected, payload, signature, nil
+}
+
+// parseCompactReader parses a JWS value serialized via compact serialization.
+func parseCompactReader(rdr io.Reader) (m *Message, err error) {
+ protected, payload, signature, err := SplitCompactReader(rdr)
+ if err != nil {
+ return nil, errors.Wrap(err, `invalid compact serialization format`)
+ }
+ return parse(protected, payload, signature)
+}
+
+func parseCompact(data []byte) (m *Message, err error) {
+ protected, payload, signature, err := SplitCompact(data)
+ if err != nil {
+ return nil, errors.Wrap(err, `invalid compact serialization format`)
+ }
+ return parse(protected, payload, signature)
+}
+
+func parse(protected, payload, signature []byte) (*Message, error) {
+ decodedHeader, err := base64.Decode(protected)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decode protected headers`)
+ }
+
+ hdr := NewHeaders()
+ if err := json.Unmarshal(decodedHeader, hdr); err != nil {
+ return nil, errors.Wrap(err, `failed to parse JOSE headers`)
+ }
+
+ decodedPayload, err := base64.Decode(payload)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decode payload`)
+ }
+
+ decodedSignature, err := base64.Decode(signature)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decode signature`)
+ }
+
+ var msg Message
+ msg.payload = decodedPayload
+ msg.signatures = append(msg.signatures, &Signature{
+ protected: hdr,
+ signature: decodedSignature,
+ })
+ return &msg, nil
+}
+
+// RegisterCustomField allows users to specify that a private field
+// be decoded as an instance of the specified type. This option has
+// a global effect.
+//
+// For example, suppose you have a custom field `x-birthday`, which
+// you want to represent as a string formatted in RFC3339 in JSON,
+// but want it back as `time.Time`.
+//
+// In that case you would register a custom field as follows
+//
+// jwe.RegisterCustomField(`x-birthday`, timeT)
+//
+// Then `hdr.Get("x-birthday")` will still return an `interface{}`,
+// but you can convert its type to `time.Time`
+//
+// bdayif, _ := hdr.Get(`x-birthday`)
+// bday := bdayif.(time.Time)
+//
+func RegisterCustomField(name string, object interface{}) {
+ registry.Register(name, object)
+}
+
+// Helpers for signature verification
+var rawKeyToKeyType = make(map[reflect.Type]jwa.KeyType)
+var keyTypeToAlgorithms = make(map[jwa.KeyType][]jwa.SignatureAlgorithm)
+
+func init() {
+ rawKeyToKeyType[reflect.TypeOf([]byte(nil))] = jwa.OctetSeq
+ rawKeyToKeyType[reflect.TypeOf(ed25519.PublicKey(nil))] = jwa.OKP
+ rawKeyToKeyType[reflect.TypeOf(rsa.PublicKey{})] = jwa.RSA
+ rawKeyToKeyType[reflect.TypeOf((*rsa.PublicKey)(nil))] = jwa.RSA
+ rawKeyToKeyType[reflect.TypeOf(ecdsa.PublicKey{})] = jwa.EC
+ rawKeyToKeyType[reflect.TypeOf((*ecdsa.PublicKey)(nil))] = jwa.EC
+
+ addAlgorithmForKeyType(jwa.OKP, jwa.EdDSA)
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} {
+ addAlgorithmForKeyType(jwa.OctetSeq, alg)
+ }
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} {
+ addAlgorithmForKeyType(jwa.RSA, alg)
+ }
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512} {
+ addAlgorithmForKeyType(jwa.EC, alg)
+ }
+}
+
+func addAlgorithmForKeyType(kty jwa.KeyType, alg jwa.SignatureAlgorithm) {
+ keyTypeToAlgorithms[kty] = append(keyTypeToAlgorithms[kty], alg)
+}
+
+// AlgorithmsForKey returns the possible signature algorithms that can
+// be used for a given key. It only takes in consideration keys/algorithms
+// for verification purposes, as this is the only usage where one may need
+// dynamically figure out which method to use.
+func AlgorithmsForKey(key interface{}) ([]jwa.SignatureAlgorithm, error) {
+ var kty jwa.KeyType
+ switch key := key.(type) {
+ case jwk.Key:
+ kty = key.KeyType()
+ case rsa.PublicKey, *rsa.PublicKey, rsa.PrivateKey, *rsa.PrivateKey:
+ kty = jwa.RSA
+ case ecdsa.PublicKey, *ecdsa.PublicKey, ecdsa.PrivateKey, *ecdsa.PrivateKey:
+ kty = jwa.EC
+ case ed25519.PublicKey, ed25519.PrivateKey, x25519.PublicKey, x25519.PrivateKey:
+ kty = jwa.OKP
+ case []byte:
+ kty = jwa.OctetSeq
+ default:
+ return nil, errors.Errorf(`invalid key %T`, key)
+ }
+
+ algs, ok := keyTypeToAlgorithms[kty]
+ if !ok {
+ return nil, errors.Errorf(`invalid key type %q`, kty)
+ }
+ return algs, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/message.go b/vendor/github.com/lestrrat-go/jwx/jws/message.go
new file mode 100644
index 000000000..178625032
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/message.go
@@ -0,0 +1,431 @@
+package jws
+
+import (
+ "bytes"
+ "context"
+
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/pkg/errors"
+)
+
+type collectRawCtx struct{}
+
+func (collectRawCtx) CollectRaw() bool {
+ return true
+}
+
+func NewSignature() *Signature {
+ return &Signature{}
+}
+
+func (s *Signature) DecodeCtx() DecodeCtx {
+ return s.dc
+}
+
+func (s *Signature) SetDecodeCtx(dc DecodeCtx) {
+ s.dc = dc
+}
+
+func (s Signature) PublicHeaders() Headers {
+ return s.headers
+}
+
+func (s *Signature) SetPublicHeaders(v Headers) *Signature {
+ s.headers = v
+ return s
+}
+
+func (s Signature) ProtectedHeaders() Headers {
+ return s.protected
+}
+
+func (s *Signature) SetProtectedHeaders(v Headers) *Signature {
+ s.protected = v
+ return s
+}
+
+func (s Signature) Signature() []byte {
+ return s.signature
+}
+
+func (s *Signature) SetSignature(v []byte) *Signature {
+ s.signature = v
+ return s
+}
+
+type signatureUnmarshalProbe struct {
+ Header Headers `json:"header,omitempty"`
+ Protected *string `json:"protected,omitempty"`
+ Signature *string `json:"signature,omitempty"`
+}
+
+func (s *Signature) UnmarshalJSON(data []byte) error {
+ var sup signatureUnmarshalProbe
+ sup.Header = NewHeaders()
+ if err := json.Unmarshal(data, &sup); err != nil {
+ return errors.Wrap(err, `failed to unmarshal signature into temporary struct`)
+ }
+
+ s.headers = sup.Header
+ if buf := sup.Protected; buf != nil {
+ src := []byte(*buf)
+ if !bytes.HasPrefix(src, []byte{'{'}) {
+ decoded, err := base64.Decode(src)
+ if err != nil {
+ return errors.Wrap(err, `failed to base64 decode protected headers`)
+ }
+ src = decoded
+ }
+
+ prt := NewHeaders()
+ prt.(*stdHeaders).SetDecodeCtx(s.DecodeCtx())
+ if err := json.Unmarshal(src, prt); err != nil {
+ return errors.Wrap(err, `failed to unmarshal protected headers`)
+ }
+ prt.(*stdHeaders).SetDecodeCtx(nil)
+ s.protected = prt
+ }
+
+ decoded, err := base64.DecodeString(*sup.Signature)
+ if err != nil {
+ return errors.Wrap(err, `failed to base decode signature`)
+ }
+ s.signature = decoded
+ return nil
+}
+
+// Sign populates the signature field, with a signature generated by
+// given the signer object and payload.
+//
+// The first return value is the raw signature in binary format.
+// The second return value s the full three-segment signature
+// (e.g. "eyXXXX.XXXXX.XXXX")
+func (s *Signature) Sign(payload []byte, signer Signer, key interface{}) ([]byte, []byte, error) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ hdrs, err := mergeHeaders(ctx, s.headers, s.protected)
+ if err != nil {
+ return nil, nil, errors.Wrap(err, `failed to merge headers`)
+ }
+
+ if err := hdrs.Set(AlgorithmKey, signer.Algorithm()); err != nil {
+ return nil, nil, errors.Wrap(err, `failed to set "alg"`)
+ }
+
+ // If the key is a jwk.Key instance, obtain the raw key
+ if jwkKey, ok := key.(jwk.Key); ok {
+ // If we have a key ID specified by this jwk.Key, use that in the header
+ if kid := jwkKey.KeyID(); kid != "" {
+ if err := hdrs.Set(jwk.KeyIDKey, kid); err != nil {
+ return nil, nil, errors.Wrap(err, `set key ID from jwk.Key`)
+ }
+ }
+ }
+ hdrbuf, err := json.Marshal(hdrs)
+ if err != nil {
+ return nil, nil, errors.Wrap(err, `failed to marshal headers`)
+ }
+
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+
+ buf.WriteString(base64.EncodeToString(hdrbuf))
+ buf.WriteByte('.')
+ if getB64Value(hdrs) {
+ buf.WriteString(base64.EncodeToString(payload))
+ } else {
+ if bytes.ContainsRune(payload, '.') {
+ return nil, nil, errors.New(`payload must not contain a "." when b64 = false`)
+ }
+ buf.Write(payload)
+ }
+
+ signature, err := signer.Sign(buf.Bytes(), key)
+ if err != nil {
+ return nil, nil, errors.Wrap(err, `failed to sign payload`)
+ }
+ s.signature = signature
+
+ buf.WriteByte('.')
+ buf.WriteString(base64.EncodeToString(signature))
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+
+ return signature, ret, nil
+}
+
+func NewMessage() *Message {
+ return &Message{}
+}
+
+// Clears the internal raw buffer that was accumulated during
+// the verify phase
+func (m *Message) clearRaw() {
+ for _, sig := range m.signatures {
+ if protected := sig.protected; protected != nil {
+ if cr, ok := protected.(*stdHeaders); ok {
+ cr.raw = nil
+ }
+ }
+ }
+}
+
+func (m *Message) SetDecodeCtx(dc DecodeCtx) {
+ m.dc = dc
+}
+
+func (m *Message) DecodeCtx() DecodeCtx {
+ return m.dc
+}
+
+// Payload returns the decoded payload
+func (m Message) Payload() []byte {
+ return m.payload
+}
+
+func (m *Message) SetPayload(v []byte) *Message {
+ m.payload = v
+ return m
+}
+
+func (m Message) Signatures() []*Signature {
+ return m.signatures
+}
+
+func (m *Message) AppendSignature(v *Signature) *Message {
+ m.signatures = append(m.signatures, v)
+ return m
+}
+
+func (m *Message) ClearSignatures() *Message {
+ m.signatures = nil
+ return m
+}
+
+// LookupSignature looks up a particular signature entry using
+// the `kid` value
+func (m Message) LookupSignature(kid string) []*Signature {
+ var sigs []*Signature
+ for _, sig := range m.signatures {
+ if hdr := sig.PublicHeaders(); hdr != nil {
+ hdrKeyID := hdr.KeyID()
+ if hdrKeyID == kid {
+ sigs = append(sigs, sig)
+ continue
+ }
+ }
+
+ if hdr := sig.ProtectedHeaders(); hdr != nil {
+ hdrKeyID := hdr.KeyID()
+ if hdrKeyID == kid {
+ sigs = append(sigs, sig)
+ continue
+ }
+ }
+ }
+ return sigs
+}
+
+// This struct is used to first probe for the structure of the
+// incoming JSON object. We then decide how to parse it
+// from the fields that are populated.
+type messageUnmarshalProbe struct {
+ Payload *string `json:"payload"`
+ Signatures []json.RawMessage `json:"signatures,omitempty"`
+ Header Headers `json:"header,omitempty"`
+ Protected *string `json:"protected,omitempty"`
+ Signature *string `json:"signature,omitempty"`
+}
+
+func (m *Message) UnmarshalJSON(buf []byte) error {
+ m.payload = nil
+ m.signatures = nil
+ m.b64 = true
+
+ var mup messageUnmarshalProbe
+ mup.Header = NewHeaders()
+ if err := json.Unmarshal(buf, &mup); err != nil {
+ return errors.Wrap(err, `failed to unmarshal into temporary structure`)
+ }
+
+ b64 := true
+ if mup.Signature == nil { // flattened signature is NOT present
+ if len(mup.Signatures) == 0 {
+ return errors.New(`required field "signatures" not present`)
+ }
+
+ m.signatures = make([]*Signature, 0, len(mup.Signatures))
+ for i, rawsig := range mup.Signatures {
+ var sig Signature
+ sig.SetDecodeCtx(m.DecodeCtx())
+ if err := json.Unmarshal(rawsig, &sig); err != nil {
+ return errors.Wrapf(err, `failed to unmarshal signature #%d`, i+1)
+ }
+ sig.SetDecodeCtx(nil)
+
+ if i == 0 {
+ if !getB64Value(sig.protected) {
+ b64 = false
+ }
+ } else {
+ if b64 != getB64Value(sig.protected) {
+ return errors.Errorf(`b64 value must be the same for all signatures`)
+ }
+ }
+
+ m.signatures = append(m.signatures, &sig)
+ }
+ } else { // .signature is present, it's a flattened structure
+ if len(mup.Signatures) != 0 {
+ return errors.New(`invalid format ("signatures" and "signature" keys cannot both be present)`)
+ }
+
+ var sig Signature
+ sig.headers = mup.Header
+ if src := mup.Protected; src != nil {
+ decoded, err := base64.DecodeString(*src)
+ if err != nil {
+ return errors.Wrap(err, `failed to base64 decode flattened protected headers`)
+ }
+ prt := NewHeaders()
+ prt.(*stdHeaders).SetDecodeCtx(m.DecodeCtx())
+ if err := json.Unmarshal(decoded, prt); err != nil {
+ return errors.Wrap(err, `failed to unmarshal flattened protected headers`)
+ }
+ prt.(*stdHeaders).SetDecodeCtx(nil)
+ sig.protected = prt
+ }
+
+ decoded, err := base64.DecodeString(*mup.Signature)
+ if err != nil {
+ return errors.Wrap(err, `failed to base64 decode flattened signature`)
+ }
+ sig.signature = decoded
+
+ m.signatures = []*Signature{&sig}
+ b64 = getB64Value(sig.protected)
+ }
+
+ if mup.Payload != nil {
+ if !b64 { // NOT base64 encoded
+ m.payload = []byte(*mup.Payload)
+ } else {
+ decoded, err := base64.DecodeString(*mup.Payload)
+ if err != nil {
+ return errors.Wrap(err, `failed to base64 decode payload`)
+ }
+ m.payload = decoded
+ }
+ }
+ m.b64 = b64
+ return nil
+}
+
+func (m Message) MarshalJSON() ([]byte, error) {
+ if len(m.signatures) == 1 {
+ return m.marshalFlattened()
+ }
+ return m.marshalFull()
+}
+
+func (m Message) marshalFlattened() ([]byte, error) {
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+
+ sig := m.signatures[0]
+
+ buf.WriteRune('{')
+ var wrote bool
+
+ if hdr := sig.headers; hdr != nil {
+ hdrjs, err := hdr.MarshalJSON()
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to marshal "header" (flattened format)`)
+ }
+ buf.WriteString(`"header":`)
+ buf.Write(hdrjs)
+ wrote = true
+ }
+
+ if wrote {
+ buf.WriteRune(',')
+ }
+ buf.WriteString(`"payload":"`)
+ buf.WriteString(base64.EncodeToString(m.payload))
+ buf.WriteRune('"')
+
+ if protected := sig.protected; protected != nil {
+ protectedbuf, err := protected.MarshalJSON()
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to marshal "protected" (flattened format)`)
+ }
+ buf.WriteString(`,"protected":"`)
+ buf.WriteString(base64.EncodeToString(protectedbuf))
+ buf.WriteRune('"')
+ }
+
+ buf.WriteString(`,"signature":"`)
+ buf.WriteString(base64.EncodeToString(sig.signature))
+ buf.WriteRune('"')
+ buf.WriteRune('}')
+
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
+
+func (m Message) marshalFull() ([]byte, error) {
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+
+ buf.WriteString(`{"payload":"`)
+ buf.WriteString(base64.EncodeToString(m.payload))
+ buf.WriteString(`","signatures":[`)
+ for i, sig := range m.signatures {
+ if i > 0 {
+ buf.WriteRune(',')
+ }
+
+ buf.WriteRune('{')
+ var wrote bool
+ if hdr := sig.headers; hdr != nil {
+ hdrbuf, err := hdr.MarshalJSON()
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to marshal "header" for signature #%d`, i+1)
+ }
+ buf.WriteString(`"header":`)
+ buf.Write(hdrbuf)
+ wrote = true
+ }
+
+ if protected := sig.protected; protected != nil {
+ protectedbuf, err := protected.MarshalJSON()
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to marshal "protected" for signature #%d`, i+1)
+ }
+ if wrote {
+ buf.WriteRune(',')
+ }
+ buf.WriteString(`"protected":"`)
+ buf.WriteString(base64.EncodeToString(protectedbuf))
+ buf.WriteRune('"')
+ wrote = true
+ }
+
+ if wrote {
+ buf.WriteRune(',')
+ }
+ buf.WriteString(`"signature":"`)
+ buf.WriteString(base64.EncodeToString(sig.signature))
+ buf.WriteString(`"}`)
+ }
+ buf.WriteString(`]}`)
+
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/option.go b/vendor/github.com/lestrrat-go/jwx/jws/option.go
new file mode 100644
index 000000000..bda0926e8
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/option.go
@@ -0,0 +1,103 @@
+package jws
+
+import (
+ "net/http"
+
+ "github.com/lestrrat-go/backoff/v2"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/lestrrat-go/option"
+)
+
+type Option = option.Interface
+
+type identPayloadSigner struct{}
+type identDetachedPayload struct{}
+type identHeaders struct{}
+type identMessage struct{}
+type identFetchBackoff struct{}
+type identFetchWhitelist struct{}
+type identHTTPClient struct{}
+type identJWKSetFetcher struct{}
+
+func WithSigner(signer Signer, key interface{}, public, protected Headers) Option {
+ return option.New(identPayloadSigner{}, &payloadSigner{
+ signer: signer,
+ key: key,
+ protected: protected,
+ public: public,
+ })
+}
+
+type SignOption interface {
+ Option
+ signOption()
+}
+
+type signOption struct {
+ Option
+}
+
+func (*signOption) signOption() {}
+
+// WithHeaders allows you to specify extra header values to include in the
+// final JWS message
+func WithHeaders(h Headers) SignOption {
+ return &signOption{option.New(identHeaders{}, h)}
+}
+
+// VerifyOption describes an option that can be passed to the jws.Verify function
+type VerifyOption interface {
+ Option
+ verifyOption()
+}
+
+type verifyOption struct {
+ Option
+}
+
+func (*verifyOption) verifyOption() {}
+
+// WithMessage can be passed to Verify() to obtain the jws.Message upon
+// a successful verification.
+func WithMessage(m *Message) VerifyOption {
+ return &verifyOption{option.New(identMessage{}, m)}
+}
+
+// WithDetachedPayload can be used to verify a JWS message with a
+// detached payload. If you have to verify using this option, you should
+// know exactly how and why this works.
+func WithDetachedPayload(v []byte) VerifyOption {
+ return &verifyOption{option.New(identDetachedPayload{}, v)}
+}
+
+// WithFetchWhitelist specifies the whitelist object to be passed
+// to `jwk.Fetch()` when `jws.VerifyAuto()` is used. If you do not
+// specify a whitelist, `jws.VerifyAuto()` will ALWAYS fail.
+//
+// This option is ignored if WithJWKSetFetcher is specified.
+func WithFetchWhitelist(wl jwk.Whitelist) VerifyOption {
+ return &verifyOption{option.New(identFetchWhitelist{}, wl)}
+}
+
+// WithFetchBackoff specifies the backoff.Policy object to be passed
+// to `jwk.Fetch()` when `jws.VerifyAuto()` is used.
+//
+// This option is ignored if WithJWKSetFetcher is specified.
+func WithFetchBackoff(b backoff.Policy) VerifyOption {
+ return &verifyOption{option.New(identFetchBackoff{}, b)}
+}
+
+// WithHTTPClient specifies the *http.Client object to be passed
+// to `jwk.Fetch()` when `jws.VerifyAuto()` is used.
+//
+// This option is ignored if WithJWKSetFetcher is specified.
+func WithHTTPClient(httpcl *http.Client) VerifyOption {
+ return &verifyOption{option.New(identHTTPClient{}, httpcl)}
+}
+
+// WithJWKSetFetcher specifies the JWKSetFetcher object to be
+// used when `jws.VerifyAuto()`, for example, to use `jwk.AutoRefetch`
+// instead of the default `jwk.Fetch()`
+func WithJWKSetFetcher(f JWKSetFetcher) VerifyOption {
+ return &verifyOption{option.New(identJWKSetFetcher{}, f)}
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/rsa.go b/vendor/github.com/lestrrat-go/jwx/jws/rsa.go
new file mode 100644
index 000000000..3aad0f934
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/rsa.go
@@ -0,0 +1,130 @@
+package jws
+
+import (
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+
+ "github.com/lestrrat-go/jwx/internal/keyconv"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+var rsaSigners map[jwa.SignatureAlgorithm]*rsaSigner
+var rsaVerifiers map[jwa.SignatureAlgorithm]*rsaVerifier
+
+func init() {
+ algs := map[jwa.SignatureAlgorithm]struct {
+ Hash crypto.Hash
+ PSS bool
+ }{
+ jwa.RS256: {
+ Hash: crypto.SHA256,
+ },
+ jwa.RS384: {
+ Hash: crypto.SHA384,
+ },
+ jwa.RS512: {
+ Hash: crypto.SHA512,
+ },
+ jwa.PS256: {
+ Hash: crypto.SHA256,
+ PSS: true,
+ },
+ jwa.PS384: {
+ Hash: crypto.SHA384,
+ PSS: true,
+ },
+ jwa.PS512: {
+ Hash: crypto.SHA512,
+ PSS: true,
+ },
+ }
+
+ rsaSigners = make(map[jwa.SignatureAlgorithm]*rsaSigner)
+ rsaVerifiers = make(map[jwa.SignatureAlgorithm]*rsaVerifier)
+ for alg, item := range algs {
+ rsaSigners[alg] = &rsaSigner{
+ alg: alg,
+ hash: item.Hash,
+ pss: item.PSS,
+ }
+ rsaVerifiers[alg] = &rsaVerifier{
+ alg: alg,
+ hash: item.Hash,
+ pss: item.PSS,
+ }
+ }
+}
+
+type rsaSigner struct {
+ alg jwa.SignatureAlgorithm
+ hash crypto.Hash
+ pss bool
+}
+
+func newRSASigner(alg jwa.SignatureAlgorithm) Signer {
+ return rsaSigners[alg]
+}
+
+func (rs *rsaSigner) Algorithm() jwa.SignatureAlgorithm {
+ return rs.alg
+}
+
+func (rs *rsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
+ if key == nil {
+ return nil, errors.New(`missing private key while signing payload`)
+ }
+
+ signer, ok := key.(crypto.Signer)
+ if !ok {
+ var privkey rsa.PrivateKey
+ if err := keyconv.RSAPrivateKey(&privkey, key); err != nil {
+ return nil, errors.Wrapf(err, `failed to retrieve rsa.PrivateKey out of %T`, key)
+ }
+ signer = &privkey
+ }
+
+ h := rs.hash.New()
+ if _, err := h.Write(payload); err != nil {
+ return nil, errors.Wrap(err, "failed to write payload to hash")
+ }
+ if rs.pss {
+ return signer.Sign(rand.Reader, h.Sum(nil), &rsa.PSSOptions{
+ Hash: rs.hash,
+ SaltLength: rsa.PSSSaltLengthEqualsHash,
+ })
+ }
+ return signer.Sign(rand.Reader, h.Sum(nil), rs.hash)
+}
+
+type rsaVerifier struct {
+ alg jwa.SignatureAlgorithm
+ hash crypto.Hash
+ pss bool
+}
+
+func newRSAVerifier(alg jwa.SignatureAlgorithm) Verifier {
+ return rsaVerifiers[alg]
+}
+
+func (rv *rsaVerifier) Verify(payload, signature []byte, key interface{}) error {
+ if key == nil {
+ return errors.New(`missing public key while verifying payload`)
+ }
+
+ var pubkey rsa.PublicKey
+ if err := keyconv.RSAPublicKey(&pubkey, key); err != nil {
+ return errors.Wrapf(err, `failed to retrieve rsa.PublicKey out of %T`, key)
+ }
+
+ h := rv.hash.New()
+ if _, err := h.Write(payload); err != nil {
+ return errors.Wrap(err, "failed to write payload to hash")
+ }
+
+ if rv.pss {
+ return rsa.VerifyPSS(&pubkey, rv.hash, h.Sum(nil), signature, nil)
+ }
+ return rsa.VerifyPKCS1v15(&pubkey, rv.hash, h.Sum(nil), signature)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/signer.go b/vendor/github.com/lestrrat-go/jwx/jws/signer.go
new file mode 100644
index 000000000..6065817fe
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/signer.go
@@ -0,0 +1,68 @@
+package jws
+
+import (
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+type SignerFactory interface {
+ Create() (Signer, error)
+}
+type SignerFactoryFn func() (Signer, error)
+
+func (fn SignerFactoryFn) Create() (Signer, error) {
+ return fn()
+}
+
+var signerDB map[jwa.SignatureAlgorithm]SignerFactory
+
+// RegisterSigner is used to register a factory object that creates
+// Signer objects based on the given algorithm.
+//
+// For example, if you would like to provide a custom signer for
+// jwa.EdDSA, use this function to register a `SignerFactory`
+// (probably in your `init()`)
+func RegisterSigner(alg jwa.SignatureAlgorithm, f SignerFactory) {
+ signerDB[alg] = f
+}
+
+func init() {
+ signerDB = make(map[jwa.SignatureAlgorithm]SignerFactory)
+
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} {
+ RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
+ return SignerFactoryFn(func() (Signer, error) {
+ return newRSASigner(alg), nil
+ })
+ }(alg))
+ }
+
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512, jwa.ES256K} {
+ RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
+ return SignerFactoryFn(func() (Signer, error) {
+ return newECDSASigner(alg), nil
+ })
+ }(alg))
+ }
+
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} {
+ RegisterSigner(alg, func(alg jwa.SignatureAlgorithm) SignerFactory {
+ return SignerFactoryFn(func() (Signer, error) {
+ return newHMACSigner(alg), nil
+ })
+ }(alg))
+ }
+
+ RegisterSigner(jwa.EdDSA, SignerFactoryFn(func() (Signer, error) {
+ return newEdDSASigner(), nil
+ }))
+}
+
+// NewSigner creates a signer that signs payloads using the given signature algorithm.
+func NewSigner(alg jwa.SignatureAlgorithm) (Signer, error) {
+ f, ok := signerDB[alg]
+ if ok {
+ return f.Create()
+ }
+ return nil, errors.Errorf(`unsupported signature algorithm "%s"`, alg)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jws/verifier.go b/vendor/github.com/lestrrat-go/jwx/jws/verifier.go
new file mode 100644
index 000000000..3a4173b9f
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jws/verifier.go
@@ -0,0 +1,68 @@
+package jws
+
+import (
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/pkg/errors"
+)
+
+type VerifierFactory interface {
+ Create() (Verifier, error)
+}
+type VerifierFactoryFn func() (Verifier, error)
+
+func (fn VerifierFactoryFn) Create() (Verifier, error) {
+ return fn()
+}
+
+var verifierDB map[jwa.SignatureAlgorithm]VerifierFactory
+
+// RegisterVerifier is used to register a factory object that creates
+// Verifier objects based on the given algorithm.
+//
+// For example, if you would like to provide a custom verifier for
+// jwa.EdDSA, use this function to register a `VerifierFactory`
+// (probably in your `init()`)
+func RegisterVerifier(alg jwa.SignatureAlgorithm, f VerifierFactory) {
+ verifierDB[alg] = f
+}
+
+func init() {
+ verifierDB = make(map[jwa.SignatureAlgorithm]VerifierFactory)
+
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.RS256, jwa.RS384, jwa.RS512, jwa.PS256, jwa.PS384, jwa.PS512} {
+ RegisterVerifier(alg, func(alg jwa.SignatureAlgorithm) VerifierFactory {
+ return VerifierFactoryFn(func() (Verifier, error) {
+ return newRSAVerifier(alg), nil
+ })
+ }(alg))
+ }
+
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.ES256, jwa.ES384, jwa.ES512, jwa.ES256K} {
+ RegisterVerifier(alg, func(alg jwa.SignatureAlgorithm) VerifierFactory {
+ return VerifierFactoryFn(func() (Verifier, error) {
+ return newECDSAVerifier(alg), nil
+ })
+ }(alg))
+ }
+
+ for _, alg := range []jwa.SignatureAlgorithm{jwa.HS256, jwa.HS384, jwa.HS512} {
+ RegisterVerifier(alg, func(alg jwa.SignatureAlgorithm) VerifierFactory {
+ return VerifierFactoryFn(func() (Verifier, error) {
+ return newHMACVerifier(alg), nil
+ })
+ }(alg))
+ }
+
+ RegisterVerifier(jwa.EdDSA, VerifierFactoryFn(func() (Verifier, error) {
+ return newEdDSAVerifier(), nil
+ }))
+}
+
+// NewVerifier creates a verifier that signs payloads using the given signature algorithm.
+func NewVerifier(alg jwa.SignatureAlgorithm) (Verifier, error) {
+ f, ok := verifierDB[alg]
+ if ok {
+ return f.Create()
+ }
+ return nil, errors.Errorf(`unsupported signature algorithm "%s"`, alg)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/README.md b/vendor/github.com/lestrrat-go/jwx/jwt/README.md
new file mode 100644
index 000000000..9e97439f6
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/README.md
@@ -0,0 +1,224 @@
+# JWT [](https://pkg.go.dev/github.com/lestrrat-go/jwx/jwt)
+
+Package jwt implements JSON Web Tokens as described in [RFC7519](https://tools.ietf.org/html/rfc7519).
+
+* Convenience methods for oft-used keys ("aud", "sub", "iss", etc)
+* Convenience functions to extract/parse from http.Request, http.Header, url.Values
+* Ability to Get/Set arbitrary keys
+* Conversion to and from JSON
+* Generate signed tokens
+* Verify signed tokens
+* Extra support for OpenID tokens via [github.com/lestrrat-go/jwx/jwt/openid](./jwt/openid)
+
+How-to style documentation can be found in the [docs directory](../docs).
+
+More examples are located in the examples directory ([jwt_example_test.go](../examples/jwt_example_test.go))
+
+# SYNOPSIS
+
+## Verify a signed JWT
+
+```go
+ token, err := jwt.Parse(payload, jwt.WithKeySet(keyset))
+ if err != nil {
+ fmt.Printf("failed to parse payload: %s\n", err)
+ }
+```
+
+## Token Usage
+
+```go
+func ExampleJWT() {
+ const aLongLongTimeAgo = 233431200
+
+ t := jwt.New()
+ t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`)
+ t.Set(jwt.AudienceKey, `Golang Users`)
+ t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0))
+ t.Set(`privateClaimKey`, `Hello, World!`)
+
+ buf, err := json.MarshalIndent(t, "", " ")
+ if err != nil {
+ fmt.Printf("failed to generate JSON: %s\n", err)
+ return
+ }
+
+ fmt.Printf("%s\n", buf)
+ fmt.Printf("aud -> '%s'\n", t.Audience())
+ fmt.Printf("iat -> '%s'\n", t.IssuedAt().Format(time.RFC3339))
+ if v, ok := t.Get(`privateClaimKey`); ok {
+ fmt.Printf("privateClaimKey -> '%s'\n", v)
+ }
+ fmt.Printf("sub -> '%s'\n", t.Subject())
+
+ key, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ log.Printf("failed to generate private key: %s", err)
+ return
+ }
+
+ {
+ // Signing a token (using raw rsa.PrivateKey)
+ signed, err := jwt.Sign(t, jwa.RS256, key)
+ if err != nil {
+ log.Printf("failed to sign token: %s", err)
+ return
+ }
+ _ = signed
+ }
+
+ {
+ // Signing a token (using JWK)
+ jwkKey, err := jwk.New(key)
+ if err != nil {
+ log.Printf("failed to create JWK key: %s", err)
+ return
+ }
+
+ signed, err := jwt.Sign(t, jwa.RS256, jwkKey)
+ if err != nil {
+ log.Printf("failed to sign token: %s", err)
+ return
+ }
+ _ = signed
+ }
+}
+```
+
+## OpenID Claims
+
+`jwt` package can work with token types other than the default one.
+For OpenID claims, use the token created by `openid.New()`, or
+use the `jwt.WithToken(openid.New())`. If you need to use other specialized
+claims, use `jwt.WithToken()` to specify the exact token type
+
+```go
+func Example_openid() {
+ const aLongLongTimeAgo = 233431200
+
+ t := openid.New()
+ t.Set(jwt.SubjectKey, `https://github.com/lestrrat-go/jwx/jwt`)
+ t.Set(jwt.AudienceKey, `Golang Users`)
+ t.Set(jwt.IssuedAtKey, time.Unix(aLongLongTimeAgo, 0))
+ t.Set(`privateClaimKey`, `Hello, World!`)
+
+ addr := openid.NewAddress()
+ addr.Set(openid.AddressPostalCodeKey, `105-0011`)
+ addr.Set(openid.AddressCountryKey, `日本`)
+ addr.Set(openid.AddressRegionKey, `東京都`)
+ addr.Set(openid.AddressLocalityKey, `港区`)
+ addr.Set(openid.AddressStreetAddressKey, `芝公園 4-2-8`)
+ t.Set(openid.AddressKey, addr)
+
+ buf, err := json.MarshalIndent(t, "", " ")
+ if err != nil {
+ fmt.Printf("failed to generate JSON: %s\n", err)
+ return
+ }
+ fmt.Printf("%s\n", buf)
+
+ t2, err := jwt.Parse(buf, jwt.WithToken(openid.New()))
+ if err != nil {
+ fmt.Printf("failed to parse JSON: %s\n", err)
+ return
+ }
+ if _, ok := t2.(openid.Token); !ok {
+ fmt.Printf("using jwt.WithToken(openid.New()) creates an openid.Token instance")
+ return
+ }
+}
+```
+
+# FAQ
+
+## Why is `jwt.Token` an interface?
+
+In this package, `jwt.Token` is an interface. This is not an arbitrary choice: there are actual reason for the type being an interface.
+
+We understand that if you are migrating from another library this may be a deal breaker, but we hope you can at least appreciate the fact that this was not done arbitrarily, and that there were real technical trade offs that were evaluated.
+
+### No uninitialized tokens
+
+First and foremost, by making it an interface, you cannot use an uninitialized token:
+
+```go
+var token1 jwt.Token // this is nil, you can't just start using this
+if err := json.Unmarshal(data, &token1); err != nil { // so you can't do this
+ ...
+}
+
+// But you _can_ do this, and we _want_ you to do this so the object is properly initialized
+token2 = jwt.New()
+if err := json.Unmarshal(data, &token2); err != nil { // actually, in practice you should use jwt.Parse()
+ ....
+}
+```
+
+### But why does it need to be initialized?
+
+There are several reasons, but one of the reasons is that I'm using a sync.Mutex to avoid races. We want this to be properly initialized.
+
+The other reason is that we support custom claims out of the box. The `map[string]interface{}` container is initialized during new. This is important when checking for equality using reflect-y methods (akin to `reflect.DeepEqual`), because if you allowed zero values, you could end up with "empty" tokens, that actually differ. Consider the following:
+
+```go
+// assume jwt.Token was s struct, not an interface
+token1 := jwt.Token{ privateClaims: make(map[string]interface{}) }
+token2 := jwt.Token{ privateClaims: nil }
+```
+
+These are semantically equivalent, but users would need to be aware of this difference when comparing values. By forcing the user to use a constructor, we can force a uniform empty state.
+
+### Standard way to store values
+
+Unlike some other libraries, this library allows you to store standard claims and non-standard claims in the same token.
+
+You _want_ to store standard claims in a properly typed field, which we do for fields like "iss", "nbf", etc.
+But for non-standard claims, there is just no way of doing this, so we _have_ to use a container like `map[string]interface{}`
+
+This means that if you allow direct access to these fields via a struct, you will have two different ways to access the claims, which is confusing:
+
+```go
+tok.Issuer = ...
+tok.PrivateClaims["foo"] = ...
+```
+
+So we want to hide where this data is stored, and use a standard method like `Set()` and `Get()` to store all the values.
+At this point you are effectively going to hide the implementation detail from the user, so you end up with a struct like below, which is fundamentally not so different from providing just an interface{}:
+
+```go
+type Token struct {
+ // unexported fields
+}
+
+func (tok *Token) Set(...) { ... }
+```
+
+### Use of pointers to store values
+
+We wanted to differentiate the state between a claim being uninitialized, and a claim being initialized to empty.
+
+So we use pointers to store values:
+
+```go
+type stdToken struct {
+ ....
+ issuer *string // if nil, uninitialized. if &(""), initialized to empty
+}
+```
+
+This is fine for us, but we doubt that this would be something users would want to do.
+This is a subtle difference, but cluttering up the API with slight variations of the same type (i.e. pointers vs non-pointers) seemed like a bad idea to us.
+
+```go
+token.Issuer = &issuer // want to avoid this
+
+token.Set(jwt.IssuerKey, "foobar") // so this is what we picked
+```
+
+This way users no longer need to care how the data is internally stored.
+
+### Allow more than one type of token through the same interface
+
+`dgrijalva/jwt-go` does this in a different way, but we felt that it would be more intuitive for all tokens to follow a single interface so there is fewer type conversions required.
+
+See the `openid` token for an example.
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go b/vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go
new file mode 100644
index 000000000..4ce5a42cf
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/builder_gen.go
@@ -0,0 +1,71 @@
+// This file is auto-generated by jwt/internal/cmd/gentoken/main.go. DO NOT EDIT
+
+package jwt
+
+import (
+ "time"
+
+ "github.com/pkg/errors"
+)
+
+// Builder is a convenience wrapper around the New() constructor
+// and the Set() methods to assign values to Token claims.
+// Users can successively call Claim() on the Builder, and have it
+// construct the Token when Build() is called. This alleviates the
+// need for the user to check for the return value of every single
+// Set() method call.
+// Note that each call to Claim() overwrites the value set from the
+// previous call.
+type Builder struct {
+ claims []*ClaimPair
+}
+
+func NewBuilder() *Builder {
+ return &Builder{}
+}
+
+func (b *Builder) Claim(name string, value interface{}) *Builder {
+ b.claims = append(b.claims, &ClaimPair{Key: name, Value: value})
+ return b
+}
+
+func (b *Builder) Audience(v []string) *Builder {
+ return b.Claim(AudienceKey, v)
+}
+
+func (b *Builder) Expiration(v time.Time) *Builder {
+ return b.Claim(ExpirationKey, v)
+}
+
+func (b *Builder) IssuedAt(v time.Time) *Builder {
+ return b.Claim(IssuedAtKey, v)
+}
+
+func (b *Builder) Issuer(v string) *Builder {
+ return b.Claim(IssuerKey, v)
+}
+
+func (b *Builder) JwtID(v string) *Builder {
+ return b.Claim(JwtIDKey, v)
+}
+
+func (b *Builder) NotBefore(v time.Time) *Builder {
+ return b.Claim(NotBeforeKey, v)
+}
+
+func (b *Builder) Subject(v string) *Builder {
+ return b.Claim(SubjectKey, v)
+}
+
+// Build creates a new token based on the claims that the builder has received
+// so far. If a claim cannot be set, then the method returns a nil Token with
+// a en error as a second return value
+func (b *Builder) Build() (Token, error) {
+ tok := New()
+ for _, claim := range b.claims {
+ if err := tok.Set(claim.Key.(string), claim.Value); err != nil {
+ return nil, errors.Wrapf(err, `failed to set claim %q`, claim.Key.(string))
+ }
+ }
+ return tok, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh b/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh
new file mode 100644
index 000000000..ceee3bdcd
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/gen.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+pushd internal/cmd/gentoken
+go build -o gentoken main.go
+popd
+
+./internal/cmd/gentoken/gentoken -objects=internal/cmd/gentoken/objects.yml
+
+rm internal/cmd/gentoken/gentoken
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/http.go b/vendor/github.com/lestrrat-go/jwx/jwt/http.go
new file mode 100644
index 000000000..9000085d8
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/http.go
@@ -0,0 +1,187 @@
+package jwt
+
+import (
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/pkg/errors"
+)
+
+// ParseHeader parses a JWT stored in a http.Header.
+//
+// For the header "Authorization", it will strip the prefix "Bearer " and will
+// treat the remaining value as a JWT.
+func ParseHeader(hdr http.Header, name string, options ...ParseOption) (Token, error) {
+ key := http.CanonicalHeaderKey(name)
+ v := strings.TrimSpace(hdr.Get(key))
+ if v == "" {
+ return nil, errors.Errorf(`empty header (%s)`, key)
+ }
+
+ if key == "Authorization" {
+ // Authorization header is an exception. We strip the "Bearer " from
+ // the prefix
+ v = strings.TrimSpace(strings.TrimPrefix(v, "Bearer"))
+ }
+
+ return ParseString(v, options...)
+}
+
+// ParseForm parses a JWT stored in a url.Value.
+func ParseForm(values url.Values, name string, options ...ParseOption) (Token, error) {
+ v := strings.TrimSpace(values.Get(name))
+ if v == "" {
+ return nil, errors.Errorf(`empty value (%s)`, name)
+ }
+
+ return ParseString(v, options...)
+}
+
+// ParseRequest searches a http.Request object for a JWT token.
+//
+// Specifying WithHeaderKey() will tell it to search under a specific
+// header key. Specifying WithFormKey() will tell it to search under
+// a specific form field.
+//
+// By default, "Authorization" header will be searched.
+//
+// If WithHeaderKey() is used, you must explicitly re-enable searching for "Authorization" header.
+//
+// # searches for "Authorization"
+// jwt.ParseRequest(req)
+//
+// # searches for "x-my-token" ONLY.
+// jwt.ParseRequest(req, http.WithHeaderKey("x-my-token"))
+//
+// # searches for "Authorization" AND "x-my-token"
+// jwt.ParseRequest(req, http.WithHeaderKey("Authorization"), http.WithHeaderKey("x-my-token"))
+func ParseRequest(req *http.Request, options ...ParseOption) (Token, error) {
+ var hdrkeys []string
+ var formkeys []string
+ var parseOptions []ParseOption
+ for _, option := range options {
+ switch option.Ident() {
+ case identHeaderKey{}:
+ hdrkeys = append(hdrkeys, option.Value().(string))
+ case identFormKey{}:
+ formkeys = append(formkeys, option.Value().(string))
+ default:
+ parseOptions = append(parseOptions, option)
+ }
+ }
+ if len(hdrkeys) == 0 {
+ hdrkeys = append(hdrkeys, "Authorization")
+ }
+
+ mhdrs := pool.GetKeyToErrorMap()
+ defer pool.ReleaseKeyToErrorMap(mhdrs)
+ mfrms := pool.GetKeyToErrorMap()
+ defer pool.ReleaseKeyToErrorMap(mfrms)
+
+ for _, hdrkey := range hdrkeys {
+ // Check presence via a direct map lookup
+ if _, ok := req.Header[http.CanonicalHeaderKey(hdrkey)]; !ok {
+ // if non-existent, not error
+ continue
+ }
+
+ tok, err := ParseHeader(req.Header, hdrkey, parseOptions...)
+ if err != nil {
+ mhdrs[hdrkey] = err
+ continue
+ }
+ return tok, nil
+ }
+
+ if cl := req.ContentLength; cl > 0 {
+ if err := req.ParseForm(); err != nil {
+ return nil, errors.Wrap(err, `failed to parse form`)
+ }
+ }
+
+ for _, formkey := range formkeys {
+ // Check presence via a direct map lookup
+ if _, ok := req.Form[formkey]; !ok {
+ // if non-existent, not error
+ continue
+ }
+
+ tok, err := ParseForm(req.Form, formkey, parseOptions...)
+ if err != nil {
+ mfrms[formkey] = err
+ continue
+ }
+ return tok, nil
+ }
+
+ // Everything below is a preulde to error reporting.
+ var triedHdrs strings.Builder
+ for i, hdrkey := range hdrkeys {
+ if i > 0 {
+ triedHdrs.WriteString(", ")
+ }
+ triedHdrs.WriteString(strconv.Quote(hdrkey))
+ }
+
+ var triedForms strings.Builder
+ for i, formkey := range formkeys {
+ if i > 0 {
+ triedForms.WriteString(", ")
+ }
+ triedForms.WriteString(strconv.Quote(formkey))
+ }
+
+ var b strings.Builder
+ b.WriteString(`failed to find a valid token in any location of the request (tried: [header keys: `)
+ b.WriteString(triedHdrs.String())
+ b.WriteByte(']')
+ if triedForms.Len() > 0 {
+ b.WriteString(", form keys: [")
+ b.WriteString(triedForms.String())
+ b.WriteByte(']')
+ }
+ b.WriteByte(')')
+
+ lmhdrs := len(mhdrs)
+ lmfrms := len(mfrms)
+ if lmhdrs > 0 || lmfrms > 0 {
+ b.WriteString(". Additionally, errors were encountered during attempts to parse")
+
+ if lmhdrs > 0 {
+ b.WriteString(" headers: (")
+ count := 0
+ for hdrkey, err := range mhdrs {
+ if count > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString("[header key: ")
+ b.WriteString(strconv.Quote(hdrkey))
+ b.WriteString(", error: ")
+ b.WriteString(strconv.Quote(err.Error()))
+ b.WriteString("]")
+ count++
+ }
+ b.WriteString(")")
+ }
+
+ if lmfrms > 0 {
+ count := 0
+ b.WriteString(" forms: (")
+ for formkey, err := range mfrms {
+ if count > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString("[form key: ")
+ b.WriteString(strconv.Quote(formkey))
+ b.WriteString(", error: ")
+ b.WriteString(strconv.Quote(err.Error()))
+ b.WriteString("]")
+ count++
+ }
+ }
+ }
+ return nil, errors.New(b.String())
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/interface.go b/vendor/github.com/lestrrat-go/jwx/jwt/interface.go
new file mode 100644
index 000000000..1e2c26924
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/interface.go
@@ -0,0 +1,14 @@
+package jwt
+
+import (
+ "github.com/lestrrat-go/iter/mapiter"
+ "github.com/lestrrat-go/jwx/internal/iter"
+ "github.com/lestrrat-go/jwx/internal/json"
+)
+
+type ClaimPair = mapiter.Pair
+type Iterator = mapiter.Iterator
+type Visitor = iter.MapVisitor
+type VisitorFunc = iter.MapVisitorFunc
+type DecodeCtx = json.DecodeCtx
+type TokenWithDecodeCtx = json.DecodeCtxContainer
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go b/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go
new file mode 100644
index 000000000..2675cfdc1
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/date.go
@@ -0,0 +1,98 @@
+package types
+
+import (
+ "strconv"
+ "time"
+
+ "github.com/lestrrat-go/jwx/internal/json"
+
+ "github.com/pkg/errors"
+)
+
+// NumericDate represents the date format used in the 'nbf' claim
+type NumericDate struct {
+ time.Time
+}
+
+func (n *NumericDate) Get() time.Time {
+ if n == nil {
+ return (time.Time{}).UTC()
+ }
+ return n.Time
+}
+
+func numericToTime(v interface{}, t *time.Time) bool {
+ var n int64
+ switch x := v.(type) {
+ case int64:
+ n = x
+ case int32:
+ n = int64(x)
+ case int16:
+ n = int64(x)
+ case int8:
+ n = int64(x)
+ case int:
+ n = int64(x)
+ case float32:
+ n = int64(x)
+ case float64:
+ n = int64(x)
+ default:
+ return false
+ }
+
+ *t = time.Unix(n, 0)
+ return true
+}
+
+func (n *NumericDate) Accept(v interface{}) error {
+ var t time.Time
+
+ switch x := v.(type) {
+ case string:
+ i, err := strconv.ParseInt(x[:], 10, 64)
+ if err != nil {
+ return errors.Errorf(`invalid epoch value %#v`, x)
+ }
+ t = time.Unix(i, 0)
+
+ case json.Number:
+ intval, err := x.Int64()
+ if err != nil {
+ return errors.Wrapf(err, `failed to convert json value %#v to int64`, x)
+ }
+ t = time.Unix(intval, 0)
+ case time.Time:
+ t = x
+ default:
+ if !numericToTime(v, &t) {
+ return errors.Errorf(`invalid type %T`, v)
+ }
+ }
+ n.Time = t.UTC()
+ return nil
+}
+
+// MarshalJSON translates from internal representation to JSON NumericDate
+// See https://tools.ietf.org/html/rfc7519#page-6
+func (n *NumericDate) MarshalJSON() ([]byte, error) {
+ if n.IsZero() {
+ return json.Marshal(nil)
+ }
+ return json.Marshal(n.Unix())
+}
+
+func (n *NumericDate) UnmarshalJSON(data []byte) error {
+ var v interface{}
+ if err := json.Unmarshal(data, &v); err != nil {
+ return errors.Wrap(err, `failed to unmarshal date`)
+ }
+
+ var n2 NumericDate
+ if err := n2.Accept(v); err != nil {
+ return errors.Wrap(err, `invalid value for NumericDate`)
+ }
+ *n = n2
+ return nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go b/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go
new file mode 100644
index 000000000..9a27c6829
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/internal/types/string.go
@@ -0,0 +1,43 @@
+package types
+
+import (
+ "github.com/lestrrat-go/jwx/internal/json"
+
+ "github.com/pkg/errors"
+)
+
+type StringList []string
+
+func (l StringList) Get() []string {
+ return []string(l)
+}
+
+func (l *StringList) Accept(v interface{}) error {
+ switch x := v.(type) {
+ case string:
+ *l = StringList([]string{x})
+ case []string:
+ *l = StringList(x)
+ case []interface{}:
+ list := make(StringList, len(x))
+ for i, e := range x {
+ if s, ok := e.(string); ok {
+ list[i] = s
+ continue
+ }
+ return errors.Errorf(`invalid list element type %T`, e)
+ }
+ *l = list
+ default:
+ return errors.Errorf(`invalid type: %T`, v)
+ }
+ return nil
+}
+
+func (l *StringList) UnmarshalJSON(data []byte) error {
+ var v interface{}
+ if err := json.Unmarshal(data, &v); err != nil {
+ return errors.Wrap(err, `failed to unmarshal data`)
+ }
+ return l.Accept(v)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/io.go b/vendor/github.com/lestrrat-go/jwx/jwt/io.go
new file mode 100644
index 000000000..e69617087
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/io.go
@@ -0,0 +1,29 @@
+// Automatically generated by internal/cmd/genreadfile/main.go. DO NOT EDIT
+
+package jwt
+
+import "os"
+
+// ReadFileOption describes options that can be passed to ReadFile.
+type ReadFileOption interface {
+ Option
+ readFileOption()
+}
+
+func ReadFile(path string, options ...ReadFileOption) (Token, error) {
+ var parseOptions []ParseOption
+ for _, option := range options {
+ switch option := option.(type) {
+ case ParseOption:
+ parseOptions = append(parseOptions, option)
+ }
+ }
+
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+
+ defer f.Close()
+ return ParseReader(f, parseOptions...)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/jwt.go b/vendor/github.com/lestrrat-go/jwx/jwt/jwt.go
new file mode 100644
index 000000000..d8d50120f
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/jwt.go
@@ -0,0 +1,560 @@
+//go:generate ./gen.sh
+
+// Package jwt implements JSON Web Tokens as described in https://tools.ietf.org/html/rfc7519
+package jwt
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "strings"
+ "sync/atomic"
+
+ "github.com/lestrrat-go/backoff/v2"
+ "github.com/lestrrat-go/jwx"
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/jwe"
+
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/lestrrat-go/jwx/jws"
+ "github.com/pkg/errors"
+)
+
+const _jwt = `jwt`
+
+// Settings controls global settings that are specific to JWTs.
+func Settings(options ...GlobalOption) {
+ var flattenAudienceBool bool
+
+ //nolint:forcetypeassert
+ for _, option := range options {
+ switch option.Ident() {
+ case identFlattenAudience{}:
+ flattenAudienceBool = option.Value().(bool)
+ }
+ }
+
+ v := atomic.LoadUint32(&json.FlattenAudience)
+ if (v == 1) != flattenAudienceBool {
+ var newVal uint32
+ if flattenAudienceBool {
+ newVal = 1
+ }
+ atomic.CompareAndSwapUint32(&json.FlattenAudience, v, newVal)
+ }
+}
+
+var registry = json.NewRegistry()
+
+// ParseString calls Parse against a string
+func ParseString(s string, options ...ParseOption) (Token, error) {
+ return parseBytes([]byte(s), options...)
+}
+
+// Parse parses the JWT token payload and creates a new `jwt.Token` object.
+// The token must be encoded in either JSON format or compact format.
+//
+// This function can work with encrypted and/or signed tokens. Any combination
+// of JWS and JWE may be applied to the token, but this function will only
+// attempt to verify/decrypt up to 2 levels (i.e. JWS only, JWE only, JWS then
+// JWE, or JWE then JWS)
+//
+// If the token is signed and you want to verify the payload matches the signature,
+// you must pass the jwt.WithVerify(alg, key) or jwt.WithKeySet(jwk.Set) option.
+// If you do not specify these parameters, no verification will be performed.
+//
+// If you also want to assert the validity of the JWT itself (i.e. expiration
+// and such), use the `Validate()` function on the returned token, or pass the
+// `WithValidate(true)` option. Validate options can also be passed to
+// `Parse`
+//
+// This function takes both ParseOption and ValidateOption types:
+// ParseOptions control the parsing behavior, and ValidateOptions are
+// passed to `Validate()` when `jwt.WithValidate` is specified.
+func Parse(s []byte, options ...ParseOption) (Token, error) {
+ return parseBytes(s, options...)
+}
+
+// ParseReader calls Parse against an io.Reader
+func ParseReader(src io.Reader, options ...ParseOption) (Token, error) {
+ // We're going to need the raw bytes regardless. Read it.
+ data, err := ioutil.ReadAll(src)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to read from token data source`)
+ }
+ return parseBytes(data, options...)
+}
+
+type parseCtx struct {
+ decryptParams DecryptParameters
+ verifyParams VerifyParameters
+ keySet jwk.Set
+ keySetProvider KeySetProvider
+ token Token
+ validateOpts []ValidateOption
+ verifyAutoOpts []jws.VerifyOption
+ localReg *json.Registry
+ inferAlgorithm bool
+ pedantic bool
+ skipVerification bool
+ useDefault bool
+ validate bool
+ verifyAuto bool
+}
+
+func parseBytes(data []byte, options ...ParseOption) (Token, error) {
+ var ctx parseCtx
+ for _, o := range options {
+ if v, ok := o.(ValidateOption); ok {
+ ctx.validateOpts = append(ctx.validateOpts, v)
+ continue
+ }
+
+ //nolint:forcetypeassert
+ switch o.Ident() {
+ case identVerifyAuto{}:
+ ctx.verifyAuto = o.Value().(bool)
+ case identFetchWhitelist{}:
+ ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithFetchWhitelist(o.Value().(jwk.Whitelist)))
+ case identHTTPClient{}:
+ ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithHTTPClient(o.Value().(*http.Client)))
+ case identFetchBackoff{}:
+ ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithFetchBackoff(o.Value().(backoff.Policy)))
+ case identJWKSetFetcher{}:
+ ctx.verifyAutoOpts = append(ctx.verifyAutoOpts, jws.WithJWKSetFetcher(o.Value().(jws.JWKSetFetcher)))
+ case identVerify{}:
+ ctx.verifyParams = o.Value().(VerifyParameters)
+ case identDecrypt{}:
+ ctx.decryptParams = o.Value().(DecryptParameters)
+ case identKeySet{}:
+ ks, ok := o.Value().(jwk.Set)
+ if !ok {
+ return nil, errors.Errorf(`invalid JWK set passed via WithKeySet() option (%T)`, o.Value())
+ }
+ ctx.keySet = ks
+ case identToken{}:
+ token, ok := o.Value().(Token)
+ if !ok {
+ return nil, errors.Errorf(`invalid token passed via WithToken() option (%T)`, o.Value())
+ }
+ ctx.token = token
+ case identPedantic{}:
+ ctx.pedantic = o.Value().(bool)
+ case identDefault{}:
+ ctx.useDefault = o.Value().(bool)
+ case identValidate{}:
+ ctx.validate = o.Value().(bool)
+ case identTypedClaim{}:
+ pair := o.Value().(claimPair)
+ if ctx.localReg == nil {
+ ctx.localReg = json.NewRegistry()
+ }
+ ctx.localReg.Register(pair.Name, pair.Value)
+ case identInferAlgorithmFromKey{}:
+ ctx.inferAlgorithm = o.Value().(bool)
+ case identKeySetProvider{}:
+ ctx.keySetProvider = o.Value().(KeySetProvider)
+ }
+ }
+
+ data = bytes.TrimSpace(data)
+ return parse(&ctx, data)
+}
+
+const (
+ _JwsVerifyInvalid = iota
+ _JwsVerifyDone
+ _JwsVerifyExpectNested
+ _JwsVerifySkipped
+)
+
+func verifyJWS(ctx *parseCtx, payload []byte) ([]byte, int, error) {
+ if ctx.verifyAuto {
+ options := ctx.verifyAutoOpts
+ verified, err := jws.VerifyAuto(payload, options...)
+ return verified, _JwsVerifyDone, err
+ }
+
+ // if we have a key set or a provider, use that
+ ks := ctx.keySet
+ p := ctx.keySetProvider
+ if ks != nil || p != nil {
+ return verifyJWSWithKeySet(ctx, payload)
+ }
+
+ // We can't proceed without verification parameters
+ vp := ctx.verifyParams
+ if vp == nil {
+ return nil, _JwsVerifySkipped, nil
+ }
+
+ return verifyJWSWithParams(ctx, payload, vp.Algorithm(), vp.Key())
+}
+
+func verifyJWSWithKeySet(ctx *parseCtx, payload []byte) ([]byte, int, error) {
+ // First, get the JWS message
+ msg, err := jws.Parse(payload)
+ if err != nil {
+ return nil, _JwsVerifyInvalid, errors.Wrap(err, `failed to parse token data as JWS message`)
+ }
+ ks := ctx.keySet
+ if ks == nil { // the caller should have checked ctx.keySet || ctx.keySetProvider
+ if p := ctx.keySetProvider; p != nil {
+ // "trust" the payload, and parse it so that the provider can do its thing
+ ctx.skipVerification = true
+ tok, err := parse(ctx, msg.Payload())
+ if err != nil {
+ return nil, _JwsVerifyInvalid, err
+ }
+ ctx.skipVerification = false
+
+ v, err := p.KeySetFrom(tok)
+ if err != nil {
+ return nil, _JwsVerifyInvalid, errors.Wrap(err, `failed to obtain jwk.Set from KeySetProvider`)
+ }
+ ks = v
+ }
+ }
+
+ // Bail out early if we don't even have a key in the set
+ if ks.Len() == 0 {
+ return nil, _JwsVerifyInvalid, errors.New(`empty keyset provided`)
+ }
+
+ var key jwk.Key
+
+ // Find the kid. we need the kid, unless the user explicitly
+ // specified to use the "default" (the first and only) key in the set
+ headers := msg.Signatures()[0].ProtectedHeaders()
+ kid := headers.KeyID()
+ if kid == "" {
+ // If the kid is NOT specified... ctx.useDefault needs to be true, and the
+ // JWKs must have exactly one key in it
+ if !ctx.useDefault {
+ return nil, _JwsVerifyInvalid, errors.New(`failed to find matching key: no key ID ("kid") specified in token`)
+ } else if ctx.useDefault && ks.Len() > 1 {
+ return nil, _JwsVerifyInvalid, errors.New(`failed to find matching key: no key ID ("kid") specified in token but multiple keys available in key set`)
+ }
+
+ // if we got here, then useDefault == true AND there is exactly
+ // one key in the set.
+ key, _ = ks.Get(0)
+ } else {
+ // Otherwise we better be able to look up the key, baby.
+ v, ok := ks.LookupKeyID(kid)
+ if !ok {
+ return nil, _JwsVerifyInvalid, errors.Errorf(`failed to find key with key ID %q in key set`, kid)
+ }
+ key = v
+ }
+
+ // We found a key with matching kid. Check fo the algorithm specified in the key.
+ // If we find an algorithm in the key, use that.
+ if v := key.Algorithm(); v != "" {
+ var alg jwa.SignatureAlgorithm
+ if err := alg.Accept(v); err != nil {
+ return nil, _JwsVerifyInvalid, errors.Wrapf(err, `invalid signature algorithm %s`, key.Algorithm())
+ }
+
+ // Okay, we have a valid algorithm, go go
+ return verifyJWSWithParams(ctx, payload, alg, key)
+ }
+
+ if ctx.inferAlgorithm {
+ // Okay, we couldn't deterministically find the single key to use.
+ // fallback to heuristics.
+ for i := 0; i < ks.Len(); i++ {
+ key, _ := ks.Get(i)
+ algs, err := jws.AlgorithmsForKey(key)
+ if err != nil {
+ return nil, _JwsVerifyInvalid, errors.Wrapf(err, `failed to get a list of signature methods for key type %s`, key.KeyType())
+ }
+
+ for _, alg := range algs {
+ // bail out if the JWT has a `alg` field, and it doesn't match
+ if tokAlg := headers.Algorithm(); tokAlg != "" {
+ if tokAlg != alg {
+ continue
+ }
+ }
+
+ // Yippeeeeeee! we found a key that matches both kid and alg!
+ v, state, err := verifyJWSWithParams(ctx, payload, alg, key)
+ if err == nil {
+ return v, state, nil
+ }
+ }
+ }
+ }
+
+ return nil, _JwsVerifyInvalid, errors.New(`failed to match any of the keys`)
+}
+
+func verifyJWSWithParams(ctx *parseCtx, payload []byte, alg jwa.SignatureAlgorithm, key interface{}) ([]byte, int, error) {
+ var m *jws.Message
+ var verifyOpts []jws.VerifyOption
+ if ctx.pedantic {
+ m = jws.NewMessage()
+ verifyOpts = []jws.VerifyOption{jws.WithMessage(m)}
+ }
+ v, err := jws.Verify(payload, alg, key, verifyOpts...)
+ if err != nil {
+ return nil, _JwsVerifyInvalid, errors.Wrap(err, `failed to verify jws signature`)
+ }
+
+ if !ctx.pedantic {
+ return v, _JwsVerifyDone, nil
+ }
+ // This payload could be a JWT+JWS, in which case typ: JWT should be there
+ // If its JWT+(JWE or JWS or...)+JWS, then cty should be JWT
+ for _, sig := range m.Signatures() {
+ hdrs := sig.ProtectedHeaders()
+ if strings.ToLower(hdrs.Type()) == _jwt {
+ return v, _JwsVerifyDone, nil
+ }
+
+ if strings.ToLower(hdrs.ContentType()) == _jwt {
+ return v, _JwsVerifyExpectNested, nil
+ }
+ }
+
+ // Hmmm, it was a JWS and we got... nothing?
+ return nil, _JwsVerifyInvalid, errors.Errorf(`expected "typ" or "cty" fields, neither could be found`)
+}
+
+// verify parameter exists to make sure that we don't accidentally skip
+// over verification just because alg == "" or key == nil or something.
+func parse(ctx *parseCtx, data []byte) (Token, error) {
+ payload := data
+ const maxDecodeLevels = 2
+
+ // If cty = `JWT`, we expect this to be a nested structure
+ var expectNested bool
+
+OUTER:
+ for i := 0; i < maxDecodeLevels; i++ {
+ switch kind := jwx.GuessFormat(payload); kind {
+ case jwx.JWT:
+ if ctx.pedantic {
+ if expectNested {
+ return nil, errors.Errorf(`expected nested encrypted/signed payload, got raw JWT`)
+ }
+ }
+
+ if i == 0 {
+ // We were NOT enveloped in other formats
+ if !ctx.skipVerification {
+ if _, _, err := verifyJWS(ctx, payload); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ break OUTER
+ case jwx.UnknownFormat:
+ // "Unknown" may include invalid JWTs, for example, those who lack "aud"
+ // claim. We could be pedantic and reject these
+ if ctx.pedantic {
+ return nil, errors.Errorf(`invalid JWT`)
+ }
+
+ if i == 0 {
+ // We were NOT enveloped in other formats
+ if !ctx.skipVerification {
+ if _, _, err := verifyJWS(ctx, payload); err != nil {
+ return nil, err
+ }
+ }
+ }
+ break OUTER
+ case jwx.JWS:
+ // Food for thought: This is going to break if you have multiple layers of
+ // JWS enveloping using different keys. It is highly unlikely use case,
+ // but it might happen.
+
+ // skipVerification should only be set to true by us. It's used
+ // when we just want to parse the JWT out of a payload
+ if !ctx.skipVerification {
+ // nested return value means:
+ // false (next envelope _may_ need to be processed)
+ // true (next envelope MUST be processed)
+ v, state, err := verifyJWS(ctx, payload)
+ if err != nil {
+ return nil, err
+ }
+
+ if state != _JwsVerifySkipped {
+ payload = v
+
+ // We only check for cty and typ if the pedantic flag is enabled
+ if !ctx.pedantic {
+ continue
+ }
+
+ if state == _JwsVerifyExpectNested {
+ expectNested = true
+ continue OUTER
+ }
+
+ // if we're not nested, we found our target. bail out of this loop
+ break OUTER
+ }
+ }
+
+ // No verification.
+ m, err := jws.Parse(data)
+ if err != nil {
+ return nil, errors.Wrap(err, `invalid jws message`)
+ }
+ payload = m.Payload()
+ case jwx.JWE:
+ dp := ctx.decryptParams
+ if dp == nil {
+ return nil, errors.Errorf(`jwt.Parse: cannot proceed with JWE encrypted payload without decryption parameters`)
+ }
+
+ var m *jwe.Message
+ var decryptOpts []jwe.DecryptOption
+ if ctx.pedantic {
+ m = jwe.NewMessage()
+ decryptOpts = []jwe.DecryptOption{jwe.WithMessage(m)}
+ }
+
+ v, err := jwe.Decrypt(data, dp.Algorithm(), dp.Key(), decryptOpts...)
+ if err != nil {
+ return nil, errors.Wrap(err, `failed to decrypt payload`)
+ }
+
+ if !ctx.pedantic {
+ payload = v
+ continue
+ }
+
+ if strings.ToLower(m.ProtectedHeaders().Type()) == _jwt {
+ payload = v
+ break OUTER
+ }
+
+ if strings.ToLower(m.ProtectedHeaders().ContentType()) == _jwt {
+ expectNested = true
+ payload = v
+ continue OUTER
+ }
+ default:
+ return nil, errors.Errorf(`unsupported format (layer: #%d)`, i+1)
+ }
+ expectNested = false
+ }
+
+ if ctx.token == nil {
+ ctx.token = New()
+ }
+
+ if ctx.localReg != nil {
+ dcToken, ok := ctx.token.(TokenWithDecodeCtx)
+ if !ok {
+ return nil, errors.Errorf(`typed claim was requested, but the token (%T) does not support DecodeCtx`, ctx.token)
+ }
+ dc := json.NewDecodeCtx(ctx.localReg)
+ dcToken.SetDecodeCtx(dc)
+ defer func() { dcToken.SetDecodeCtx(nil) }()
+ }
+
+ if err := json.Unmarshal(payload, ctx.token); err != nil {
+ return nil, errors.Wrap(err, `failed to parse token`)
+ }
+
+ if ctx.validate {
+ if err := Validate(ctx.token, ctx.validateOpts...); err != nil {
+ return nil, err
+ }
+ }
+ return ctx.token, nil
+}
+
+// Sign is a convenience function to create a signed JWT token serialized in
+// compact form.
+//
+// It accepts either a raw key (e.g. rsa.PrivateKey, ecdsa.PrivateKey, etc)
+// or a jwk.Key, and the name of the algorithm that should be used to sign
+// the token.
+//
+// If the key is a jwk.Key and the key contains a key ID (`kid` field),
+// then it is added to the protected header generated by the signature
+//
+// The algorithm specified in the `alg` parameter must be able to support
+// the type of key you provided, otherwise an error is returned.
+//
+// The protected header will also automatically have the `typ` field set
+// to the literal value `JWT`, unless you provide a custom value for it
+// by jwt.WithHeaders option.
+func Sign(t Token, alg jwa.SignatureAlgorithm, key interface{}, options ...SignOption) ([]byte, error) {
+ return NewSerializer().Sign(alg, key, options...).Serialize(t)
+}
+
+// Equal compares two JWT tokens. Do not use `reflect.Equal` or the like
+// to compare tokens as they will also compare extra detail such as
+// sync.Mutex objects used to control concurrent access.
+//
+// The comparison for values is currently done using a simple equality ("=="),
+// except for time.Time, which uses time.Equal after dropping the monotonic
+// clock and truncating the values to 1 second accuracy.
+//
+// if both t1 and t2 are nil, returns true
+func Equal(t1, t2 Token) bool {
+ if t1 == nil && t2 == nil {
+ return true
+ }
+
+ // we already checked for t1 == t2 == nil, so safe to do this
+ if t1 == nil || t2 == nil {
+ return false
+ }
+
+ j1, err := json.Marshal(t1)
+ if err != nil {
+ return false
+ }
+
+ j2, err := json.Marshal(t2)
+ if err != nil {
+ return false
+ }
+
+ return bytes.Equal(j1, j2)
+}
+
+func (t *stdToken) Clone() (Token, error) {
+ dst := New()
+
+ for _, pair := range t.makePairs() {
+ if err := dst.Set(pair.Key.(string), pair.Value); err != nil {
+ return nil, errors.Wrapf(err, `failed to set %s`, pair.Key.(string))
+ }
+ }
+ return dst, nil
+}
+
+// RegisterCustomField allows users to specify that a private field
+// be decoded as an instance of the specified type. This option has
+// a global effect.
+//
+// For example, suppose you have a custom field `x-birthday`, which
+// you want to represent as a string formatted in RFC3339 in JSON,
+// but want it back as `time.Time`.
+//
+// In that case you would register a custom field as follows
+//
+// jwt.RegisterCustomField(`x-birthday`, timeT)
+//
+// Then `token.Get("x-birthday")` will still return an `interface{}`,
+// but you can convert its type to `time.Time`
+//
+// bdayif, _ := token.Get(`x-birthday`)
+// bday := bdayif.(time.Time)
+//
+func RegisterCustomField(name string, object interface{}) {
+ registry.Register(name, object)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/options.go b/vendor/github.com/lestrrat-go/jwx/jwt/options.go
new file mode 100644
index 000000000..e905c356a
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/options.go
@@ -0,0 +1,538 @@
+package jwt
+
+import (
+ "context"
+ "net/http"
+ "time"
+
+ "github.com/lestrrat-go/backoff/v2"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe"
+ "github.com/lestrrat-go/jwx/jwk"
+ "github.com/lestrrat-go/jwx/jws"
+ "github.com/lestrrat-go/option"
+)
+
+type Option = option.Interface
+
+// GlobalOption describes an Option that can be passed to `Settings()`.
+type GlobalOption interface {
+ Option
+ globalOption()
+}
+
+type globalOption struct {
+ Option
+}
+
+func (*globalOption) globalOption() {}
+
+// ParseRequestOption describes an Option that can be passed to `ParseRequest()`.
+type ParseRequestOption interface {
+ ParseOption
+ httpParseOption()
+}
+
+type httpParseOption struct {
+ ParseOption
+}
+
+func (*httpParseOption) httpParseOption() {}
+
+// ParseOption describes an Option that can be passed to `Parse()`.
+// ParseOption also implements ReadFileOption, therefore it may be
+// safely pass them to `jwt.ReadFile()`
+type ParseOption interface {
+ ReadFileOption
+ parseOption()
+}
+
+type parseOption struct {
+ Option
+}
+
+func newParseOption(n interface{}, v interface{}) ParseOption {
+ return &parseOption{option.New(n, v)}
+}
+
+func (*parseOption) parseOption() {}
+func (*parseOption) readFileOption() {}
+
+// SignOption describes an Option that can be passed to Sign() or
+// (jwt.Serializer).Sign
+type SignOption interface {
+ Option
+ signOption()
+}
+
+type signOption struct {
+ Option
+}
+
+func newSignOption(n interface{}, v interface{}) SignOption {
+ return &signOption{option.New(n, v)}
+}
+
+func (*signOption) signOption() {}
+
+// EncryptOption describes an Option that can be passed to Encrypt() or
+// (jwt.Serializer).Encrypt
+type EncryptOption interface {
+ Option
+ encryptOption()
+}
+
+type encryptOption struct {
+ Option
+}
+
+func newEncryptOption(n interface{}, v interface{}) EncryptOption {
+ return &encryptOption{option.New(n, v)}
+}
+
+func (*encryptOption) encryptOption() {}
+
+// ValidateOption describes an Option that can be passed to Validate().
+// ValidateOption also implements ParseOption, therefore it may be
+// safely passed to `Parse()` (and thus `jwt.ReadFile()`)
+type ValidateOption interface {
+ ParseOption
+ validateOption()
+}
+
+type validateOption struct {
+ ParseOption
+}
+
+func newValidateOption(n interface{}, v interface{}) ValidateOption {
+ return &validateOption{newParseOption(n, v)}
+}
+
+func (*validateOption) validateOption() {}
+
+type identAcceptableSkew struct{}
+type identClock struct{}
+type identContext struct{}
+type identDecrypt struct{}
+type identDefault struct{}
+type identFlattenAudience struct{}
+type identInferAlgorithmFromKey struct{}
+type identJweHeaders struct{}
+type identJwsHeaders struct{}
+type identKeySet struct{}
+type identKeySetProvider struct{}
+type identPedantic struct{}
+type identValidator struct{}
+type identToken struct{}
+type identTypedClaim struct{}
+type identValidate struct{}
+type identVerify struct{}
+type identVerifyAuto struct{}
+type identFetchBackoff struct{}
+type identFetchWhitelist struct{}
+type identHTTPClient struct{}
+type identJWKSetFetcher struct{}
+
+type identHeaderKey struct{}
+type identFormKey struct{}
+
+type VerifyParameters interface {
+ Algorithm() jwa.SignatureAlgorithm
+ Key() interface{}
+}
+
+type verifyParams struct {
+ alg jwa.SignatureAlgorithm
+ key interface{}
+}
+
+func (p *verifyParams) Algorithm() jwa.SignatureAlgorithm {
+ return p.alg
+}
+
+func (p *verifyParams) Key() interface{} {
+ return p.key
+}
+
+// WithVerify forces the Parse method to verify the JWT message
+// using the given key. XXX Should have been named something like
+// WithVerificationKey
+func WithVerify(alg jwa.SignatureAlgorithm, key interface{}) ParseOption {
+ return newParseOption(identVerify{}, &verifyParams{
+ alg: alg,
+ key: key,
+ })
+}
+
+// WithKeySet forces the Parse method to verify the JWT message
+// using one of the keys in the given key set.
+//
+// The key and the JWT MUST have a proper `kid` field set.
+// The key to use for signature verification is chosen by matching
+// the Key ID of the JWT and the ID of the given key set.
+//
+// When using this option, keys MUST have a proper 'alg' field
+// set. This is because we need to know the exact algorithm that
+// you (the user) wants to use to verify the token. We do NOT
+// trust the token's headers, because they can easily be tampered with.
+//
+// However, there _is_ a workaround if you do understand the risks
+// of allowing a library to automatically choose a signature verification strategy,
+// and you do not mind the verification process having to possibly
+// attempt using multiple times before succeeding to verify. See
+// `jwt.InferAlgorithmFromKey` option
+//
+// If you have only one key in the set, and are sure you want to
+// use that key, you can use the `jwt.WithDefaultKey` option.
+//
+// If provided with WithKeySetProvider(), this option takes precedence.
+func WithKeySet(set jwk.Set) ParseOption {
+ return newParseOption(identKeySet{}, set)
+}
+
+// UseDefaultKey is used in conjunction with the option WithKeySet
+// to instruct the Parse method to default to the single key in a key
+// set when no Key ID is included in the JWT. If the key set contains
+// multiple keys then the default behavior is unchanged -- that is,
+// the since we can't determine the key to use, it returns an error.
+func UseDefaultKey(value bool) ParseOption {
+ return newParseOption(identDefault{}, value)
+}
+
+// WithToken specifies the token instance that is used when parsing
+// JWT tokens.
+func WithToken(t Token) ParseOption {
+ return newParseOption(identToken{}, t)
+}
+
+// WithHeaders is passed to `jwt.Sign()` function, to allow specifying arbitrary
+// header values to be included in the header section of the jws message
+//
+// This option will be deprecated in the next major version. Use
+// jwt.WithJwsHeaders() instead.
+func WithHeaders(hdrs jws.Headers) SignOption {
+ return WithJwsHeaders(hdrs)
+}
+
+// WithJwsHeaders is passed to `jwt.Sign()` function or
+// "jwt.Serializer".Sign() method, to allow specifying arbitrary
+// header values to be included in the header section of the JWE message
+func WithJwsHeaders(hdrs jws.Headers) SignOption {
+ return newSignOption(identJwsHeaders{}, hdrs)
+}
+
+// WithJweHeaders is passed to "jwt.Serializer".Encrypt() method to allow
+// specifying arbitrary header values to be included in the protected header
+// of the JWE message
+func WithJweHeaders(hdrs jwe.Headers) EncryptOption {
+ return newEncryptOption(identJweHeaders{}, hdrs)
+}
+
+// WithValidate is passed to `Parse()` method to denote that the
+// validation of the JWT token should be performed after a successful
+// parsing of the incoming payload.
+func WithValidate(b bool) ParseOption {
+ return newParseOption(identValidate{}, b)
+}
+
+// WithClock specifies the `Clock` to be used when verifying
+// claims exp and nbf.
+func WithClock(c Clock) ValidateOption {
+ return newValidateOption(identClock{}, c)
+}
+
+// WithAcceptableSkew specifies the duration in which exp and nbf
+// claims may differ by. This value should be positive
+func WithAcceptableSkew(dur time.Duration) ValidateOption {
+ return newValidateOption(identAcceptableSkew{}, dur)
+}
+
+// WithIssuer specifies that expected issuer value. If not specified,
+// the value of issuer is not verified at all.
+func WithIssuer(s string) ValidateOption {
+ return WithValidator(ClaimValueIs(IssuerKey, s))
+}
+
+// WithSubject specifies that expected subject value. If not specified,
+// the value of subject is not verified at all.
+func WithSubject(s string) ValidateOption {
+ return WithValidator(ClaimValueIs(SubjectKey, s))
+}
+
+// WithJwtID specifies that expected jti value. If not specified,
+// the value of jti is not verified at all.
+func WithJwtID(s string) ValidateOption {
+ return WithValidator(ClaimValueIs(JwtIDKey, s))
+}
+
+// WithAudience specifies that expected audience value.
+// `Validate()` will return true if one of the values in the `aud` element
+// matches this value. If not specified, the value of issuer is not
+// verified at all.
+func WithAudience(s string) ValidateOption {
+ return WithValidator(ClaimContainsString(AudienceKey, s))
+}
+
+// WithClaimValue specifies the expected value for a given claim
+func WithClaimValue(name string, v interface{}) ValidateOption {
+ return WithValidator(ClaimValueIs(name, v))
+}
+
+// WithHeaderKey is used to specify header keys to search for tokens.
+//
+// While the type system allows this option to be passed to jwt.Parse() directly,
+// doing so will have no effect. Only use it for HTTP request parsing functions
+func WithHeaderKey(v string) ParseRequestOption {
+ return &httpParseOption{newParseOption(identHeaderKey{}, v)}
+}
+
+// WithFormKey is used to specify header keys to search for tokens.
+//
+// While the type system allows this option to be passed to jwt.Parse() directly,
+// doing so will have no effect. Only use it for HTTP request parsing functions
+func WithFormKey(v string) ParseRequestOption {
+ return &httpParseOption{newParseOption(identFormKey{}, v)}
+}
+
+// WithFlattenAudience specifies if the "aud" claim should be flattened
+// to a single string upon the token being serialized to JSON.
+//
+// This is sometimes important when a JWT consumer does not understand that
+// the "aud" claim can actually take the form of an array of strings.
+//
+// The default value is `false`, which means that "aud" claims are always
+// rendered as a arrays of strings. This setting has a global effect,
+// and will change the behavior for all JWT serialization.
+func WithFlattenAudience(v bool) GlobalOption {
+ return &globalOption{option.New(identFlattenAudience{}, v)}
+}
+
+type claimPair struct {
+ Name string
+ Value interface{}
+}
+
+// WithTypedClaim allows a private claim to be parsed into the object type of
+// your choice. It works much like the RegisterCustomField, but the effect
+// is only applicable to the jwt.Parse function call which receives this option.
+//
+// While this can be extremely useful, this option should be used with caution:
+// There are many caveats that your entire team/user-base needs to be aware of,
+// and therefore in general its use is discouraged. Only use it when you know
+// what you are doing, and you document its use clearly for others.
+//
+// First and foremost, this is a "per-object" option. Meaning that given the same
+// serialized format, it is possible to generate two objects whose internal
+// representations may differ. That is, if you parse one _WITH_ the option,
+// and the other _WITHOUT_, their internal representation may completely differ.
+// This could potentially lead to problems.
+//
+// Second, specifying this option will slightly slow down the decoding process
+// as it needs to consult multiple definitions sources (global and local), so
+// be careful if you are decoding a large number of tokens, as the effects will stack up.
+//
+// Finally, this option will also NOT work unless the tokens themselves support such
+// parsing mechanism. For example, while tokens obtained from `jwt.New()` and
+// `openid.New()` will respect this option, if you provide your own custom
+// token type, it will need to implement the TokenWithDecodeCtx interface.
+func WithTypedClaim(name string, object interface{}) ParseOption {
+ return newParseOption(identTypedClaim{}, claimPair{Name: name, Value: object})
+}
+
+// WithRequiredClaim specifies that the claim identified the given name
+// must exist in the token. Only the existence of the claim is checked:
+// the actual value associated with that field is not checked.
+func WithRequiredClaim(name string) ValidateOption {
+ return WithValidator(IsRequired(name))
+}
+
+// WithMaxDelta specifies that given two claims `c1` and `c2` that represent time, the difference in
+// time.Duration must be less than equal to the value specified by `d`. If `c1` or `c2` is the
+// empty string, the current time (as computed by `time.Now` or the object passed via
+// `WithClock()`) is used for the comparison.
+//
+// `c1` and `c2` are also assumed to be required, therefore not providing either claim in the
+// token will result in an error.
+//
+// Because there is no way of reliably knowing how to parse private claims, we currently only
+// support `iat`, `exp`, and `nbf` claims.
+//
+// If the empty string is passed to c1 or c2, then the current time (as calculated by time.Now() or
+// the clock object provided via WithClock()) is used.
+//
+// For example, in order to specify that `exp` - `iat` should be less than 10*time.Second, you would write
+//
+// jwt.Validate(token, jwt.WithMaxDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey))
+//
+// If AcceptableSkew of 2 second is specified, the above will return valid for any value of
+// `exp` - `iat` between 8 (10-2) and 12 (10+2).
+func WithMaxDelta(dur time.Duration, c1, c2 string) ValidateOption {
+ return WithValidator(MaxDeltaIs(c1, c2, dur))
+}
+
+// WithMinDelta is almost exactly the same as WithMaxDelta, but force validation to fail if
+// the difference between time claims are less than dur.
+//
+// For example, in order to specify that `exp` - `iat` should be greater than 10*time.Second, you would write
+//
+// jwt.Validate(token, jwt.WithMinDelta(10*time.Second, jwt.ExpirationKey, jwt.IssuedAtKey))
+//
+// The validation would fail if the difference is less than 10 seconds.
+//
+func WithMinDelta(dur time.Duration, c1, c2 string) ValidateOption {
+ return WithValidator(MinDeltaIs(c1, c2, dur))
+}
+
+// WithValidator validates the token with the given Validator.
+//
+// For example, in order to validate tokens that are only valid during August, you would write
+//
+// validator := jwt.ValidatorFunc(func(_ context.Context, t jwt.Token) error {
+// if time.Now().Month() != 8 {
+// return fmt.Errorf(`tokens are only valid during August!`)
+// }
+// return nil
+// })
+// err := jwt.Validate(token, jwt.WithValidator(validator))
+//
+func WithValidator(v Validator) ValidateOption {
+ return newValidateOption(identValidator{}, v)
+}
+
+type decryptParams struct {
+ alg jwa.KeyEncryptionAlgorithm
+ key interface{}
+}
+
+type DecryptParameters interface {
+ Algorithm() jwa.KeyEncryptionAlgorithm
+ Key() interface{}
+}
+
+func (dp *decryptParams) Algorithm() jwa.KeyEncryptionAlgorithm {
+ return dp.alg
+}
+
+func (dp *decryptParams) Key() interface{} {
+ return dp.key
+}
+
+// WithDecrypt allows users to specify parameters for decryption using
+// `jwe.Decrypt`. You must specify this if your JWT is encrypted.
+func WithDecrypt(alg jwa.KeyEncryptionAlgorithm, key interface{}) ParseOption {
+ return newParseOption(identDecrypt{}, &decryptParams{
+ alg: alg,
+ key: key,
+ })
+}
+
+// WithPedantic enables pedantic mode for parsing JWTs. Currently this only
+// applies to checking for the correct `typ` and/or `cty` when necessary.
+func WithPedantic(v bool) ParseOption {
+ return newParseOption(identPedantic{}, v)
+}
+
+// InferAlgorithmFromKey allows jwt.Parse to guess the signature algorithm
+// passed to `jws.Verify()`, in case the key you provided does not have a proper `alg` header.
+//
+// Compared to providing explicit `alg` from the key this is slower, and in
+// case our heuristics are wrong or outdated, may fail to verify the token.
+// Also, automatic detection of signature verification methods are always
+// more vulnerable for potential attack vectors.
+//
+// It is highly recommended that you fix your key to contain a proper `alg`
+// header field instead of resorting to using this option, but sometimes
+// it just needs to happen.
+//
+// Your JWT still need to have an `alg` field, and it must match one of the
+// candidates that we produce for your key
+func InferAlgorithmFromKey(v bool) ParseOption {
+ return newParseOption(identInferAlgorithmFromKey{}, v)
+}
+
+// KeySetProvider is an interface for objects that can choose the appropriate
+// jwk.Set to be used when verifying JWTs
+type KeySetProvider interface {
+ // KeySetFrom returns the jwk.Set to be used to verify the token.
+ // Keep in mind that the token at the point when the method is called is NOT VERIFIED.
+ // DO NOT trust the contents of the Token too much. For example, do not take the
+ // hint as to which signature algorithm to use from the token itself.
+ KeySetFrom(Token) (jwk.Set, error)
+}
+
+// KeySetProviderFunc is an implementation of KeySetProvider that is based
+// on a function.
+type KeySetProviderFunc func(Token) (jwk.Set, error)
+
+func (fn KeySetProviderFunc) KeySetFrom(t Token) (jwk.Set, error) {
+ return fn(t)
+}
+
+// WithKeySetProvider allows users to specify an object to choose which
+// jwk.Set to use for verification.
+//
+// If provided with WithKeySet(), WithKeySet() option takes precedence.
+func WithKeySetProvider(p KeySetProvider) ParseOption {
+ return newParseOption(identKeySetProvider{}, p)
+}
+
+// WithContext allows you to specify a context.Context object to be used
+// with `jwt.Validate()` option.
+//
+// Please be aware that in the next major release of this library,
+// `jwt.Validate()`'s signature will change to include an explicit
+// `context.Context` object.
+func WithContext(ctx context.Context) ValidateOption {
+ return newValidateOption(identContext{}, ctx)
+}
+
+// WithVerifyAuto specifies that the JWS verification should be performed
+// using `jws.VerifyAuto()`, which in turn attempts to verify the message
+// using values that are stored within the JWS message.
+//
+// Only passing this option to `jwt.Parse()` will not result in a successful
+// verification. Please make sure to carefully read the documentation in
+// `jws.VerifyAuto()`, and provide the necessary Whitelist object via
+// `jwt.WithFetchWhitelist()`
+//
+// You might also consider using a backoff policy by using `jwt.WithFetchBackoff()`
+// to control the number of requests being made.
+func WithVerifyAuto(v bool) ParseOption {
+ return newParseOption(identVerifyAuto{}, v)
+}
+
+// WithFetchWhitelist specifies the `jwk.Whitelist` object that should be
+// passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()`
+//
+// This is a wrapper over `jws.WithFetchWhitelist()` that can be passed
+// to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()`
+func WithFetchWhitelist(wl jwk.Whitelist) ParseOption {
+ return newParseOption(identFetchWhitelist{}, wl)
+}
+
+// WithHTTPClient specifies the `*http.Client` object that should be
+// passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()`
+//
+// This is a wrapper over `jws.WithHTTPClient()` that can be passed
+// to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()`
+func WithHTTPClient(httpcl *http.Client) ParseOption {
+ return newParseOption(identHTTPClient{}, httpcl)
+}
+
+// WithFetchBackoff specifies the `backoff.Policy` object that should be
+// passed to `jws.VerifyAuto()`, which in turn will be passed to `jwk.Fetch()`
+//
+// This is a wrapper over `jws.WithFetchBackoff()` that can be passed
+// to `jwt.Parse()`, and will be ignored if you spcify `jws.WithJWKSetFetcher()`
+func WithFetchBackoff(b backoff.Policy) ParseOption {
+ return newParseOption(identFetchBackoff{}, b)
+}
+
+// WithJWKSetFetcher specifies the `jws.JWKSetFetcher` object that should be
+// passed to `jws.VerifyAuto()`
+//
+// This is a wrapper over `jws.WithJWKSetFetcher()` that can be passed
+// to `jwt.Parse()`.
+func WithJWKSetFetcher(f jws.JWKSetFetcher) ParseOption {
+ return newParseOption(identJWKSetFetcher{}, f)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/serialize.go b/vendor/github.com/lestrrat-go/jwx/jwt/serialize.go
new file mode 100644
index 000000000..a3665ce62
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/serialize.go
@@ -0,0 +1,239 @@
+package jwt
+
+import (
+ "fmt"
+
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/jwa"
+ "github.com/lestrrat-go/jwx/jwe"
+ "github.com/lestrrat-go/jwx/jws"
+ "github.com/pkg/errors"
+)
+
+type SerializeCtx interface {
+ Step() int
+ Nested() bool
+}
+
+type serializeCtx struct {
+ step int
+ nested bool
+}
+
+func (ctx *serializeCtx) Step() int {
+ return ctx.step
+}
+
+func (ctx *serializeCtx) Nested() bool {
+ return ctx.nested
+}
+
+type SerializeStep interface {
+ Serialize(SerializeCtx, interface{}) (interface{}, error)
+}
+
+// Serializer is a generic serializer for JWTs. Whereas other conveinience
+// functions can only do one thing (such as generate a JWS signed JWT),
+// Using this construct you can serialize the token however you want.
+//
+// By default the serializer only marshals the token into a JSON payload.
+// You must set up the rest of the steps that should be taken by the
+// serializer.
+//
+// For example, to marshal the token into JSON, then apply JWS and JWE
+// in that order, you would do:
+//
+// serialized, err := jwt.NewSerialer().
+// Sign(jwa.RS256, key).
+// Encrypt(jwa.RSA_OAEP, key.PublicKey).
+// Serialize(token)
+//
+// The `jwt.Sign()` function is equivalent to
+//
+// serialized, err := jwt.NewSerializer().
+// Sign(...args...).
+// Serialize(token)
+type Serializer struct {
+ steps []SerializeStep
+}
+
+// NewSerializer creates a new empty serializer.
+func NewSerializer() *Serializer {
+ return &Serializer{}
+}
+
+// Reset clears all of the registered steps.
+func (s *Serializer) Reset() *Serializer {
+ s.steps = nil
+ return s
+}
+
+// Step adds a new Step to the serialization process
+func (s *Serializer) Step(step SerializeStep) *Serializer {
+ s.steps = append(s.steps, step)
+ return s
+}
+
+type jsonSerializer struct{}
+
+func (jsonSerializer) Serialize(_ SerializeCtx, v interface{}) (interface{}, error) {
+ token, ok := v.(Token)
+ if !ok {
+ return nil, errors.Errorf(`invalid input: expected jwt.Token`)
+ }
+
+ buf, err := json.Marshal(token)
+ if err != nil {
+ return nil, errors.Errorf(`failed to serialize as JSON`)
+ }
+ return buf, nil
+}
+
+type genericHeader interface {
+ Get(string) (interface{}, bool)
+ Set(string, interface{}) error
+}
+
+func setTypeOrCty(ctx SerializeCtx, hdrs genericHeader) error {
+ // cty and typ are common between JWE/JWS, so we don't use
+ // the constants in jws/jwe package here
+ const typKey = `typ`
+ const ctyKey = `cty`
+
+ if ctx.Step() == 1 {
+ // We are executed immediately after json marshaling
+ if _, ok := hdrs.Get(typKey); !ok {
+ if err := hdrs.Set(typKey, `JWT`); err != nil {
+ return errors.Wrapf(err, `failed to set %s key to "JWT"`, typKey)
+ }
+ }
+ } else {
+ if ctx.Nested() {
+ // If this is part of a nested sequence, we should set cty = 'JWT'
+ // https://datatracker.ietf.org/doc/html/rfc7519#section-5.2
+ if err := hdrs.Set(ctyKey, `JWT`); err != nil {
+ return errors.Wrapf(err, `failed to set %s key to "JWT"`, ctyKey)
+ }
+ }
+ }
+ return nil
+}
+
+type jwsSerializer struct {
+ alg jwa.SignatureAlgorithm
+ key interface{}
+ options []SignOption
+}
+
+func (s *jwsSerializer) Serialize(ctx SerializeCtx, v interface{}) (interface{}, error) {
+ payload, ok := v.([]byte)
+ if !ok {
+ return nil, errors.New(`expected []byte as input`)
+ }
+
+ var hdrs jws.Headers
+ //nolint:forcetypeassert
+ for _, option := range s.options {
+ switch option.Ident() {
+ case identJwsHeaders{}:
+ hdrs = option.Value().(jws.Headers)
+ }
+ }
+
+ if hdrs == nil {
+ hdrs = jws.NewHeaders()
+ }
+
+ if err := setTypeOrCty(ctx, hdrs); err != nil {
+ return nil, err // this is already wrapped
+ }
+
+ // JWTs MUST NOT use b64 = false
+ // https://datatracker.ietf.org/doc/html/rfc7797#section-7
+ if v, ok := hdrs.Get("b64"); ok {
+ if bval, bok := v.(bool); bok {
+ if !bval { // b64 = false
+ return nil, errors.New(`b64 cannot be false for JWTs`)
+ }
+ }
+ }
+ return jws.Sign(payload, s.alg, s.key, jws.WithHeaders(hdrs))
+}
+
+func (s *Serializer) Sign(alg jwa.SignatureAlgorithm, key interface{}, options ...SignOption) *Serializer {
+ return s.Step(&jwsSerializer{
+ alg: alg,
+ key: key,
+ options: options,
+ })
+}
+
+type jweSerializer struct {
+ keyalg jwa.KeyEncryptionAlgorithm
+ key interface{}
+ contentalg jwa.ContentEncryptionAlgorithm
+ compressalg jwa.CompressionAlgorithm
+ options []EncryptOption
+}
+
+func (s *jweSerializer) Serialize(ctx SerializeCtx, v interface{}) (interface{}, error) {
+ payload, ok := v.([]byte)
+ if !ok {
+ return nil, fmt.Errorf(`expected []byte as input`)
+ }
+
+ var hdrs jwe.Headers
+ //nolint:forcetypeassert
+ for _, option := range s.options {
+ switch option.Ident() {
+ case identJweHeaders{}:
+ hdrs = option.Value().(jwe.Headers)
+ }
+ }
+
+ if hdrs == nil {
+ hdrs = jwe.NewHeaders()
+ }
+
+ if err := setTypeOrCty(ctx, hdrs); err != nil {
+ return nil, err // this is already wrapped
+ }
+ return jwe.Encrypt(payload, s.keyalg, s.key, s.contentalg, s.compressalg, jwe.WithProtectedHeaders(hdrs))
+}
+
+func (s *Serializer) Encrypt(keyalg jwa.KeyEncryptionAlgorithm, key interface{}, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm, options ...EncryptOption) *Serializer {
+ return s.Step(&jweSerializer{
+ keyalg: keyalg,
+ key: key,
+ contentalg: contentalg,
+ compressalg: compressalg,
+ options: options,
+ })
+}
+
+func (s *Serializer) Serialize(t Token) ([]byte, error) {
+ steps := make([]SerializeStep, len(s.steps)+1)
+ steps[0] = jsonSerializer{}
+ for i, step := range s.steps {
+ steps[i+1] = step
+ }
+
+ var ctx serializeCtx
+ ctx.nested = len(s.steps) > 1
+ var payload interface{} = t
+ for i, step := range steps {
+ ctx.step = i
+ v, err := step.Serialize(&ctx, payload)
+ if err != nil {
+ return nil, errors.Wrapf(err, `failed to serialize token at step #%d`, i+1)
+ }
+ payload = v
+ }
+
+ res, ok := payload.([]byte)
+ if !ok {
+ return nil, errors.New(`invalid serialization produced`)
+ }
+
+ return res, nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/token_gen.go b/vendor/github.com/lestrrat-go/jwx/jwt/token_gen.go
new file mode 100644
index 000000000..4621e398a
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/token_gen.go
@@ -0,0 +1,498 @@
+// This file is auto-generated by jwt/internal/cmd/gentoken/main.go. DO NOT EDIT
+
+package jwt
+
+import (
+ "bytes"
+ "context"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/lestrrat-go/iter/mapiter"
+ "github.com/lestrrat-go/jwx/internal/base64"
+ "github.com/lestrrat-go/jwx/internal/iter"
+ "github.com/lestrrat-go/jwx/internal/json"
+ "github.com/lestrrat-go/jwx/internal/pool"
+ "github.com/lestrrat-go/jwx/jwt/internal/types"
+ "github.com/pkg/errors"
+)
+
+const (
+ AudienceKey = "aud"
+ ExpirationKey = "exp"
+ IssuedAtKey = "iat"
+ IssuerKey = "iss"
+ JwtIDKey = "jti"
+ NotBeforeKey = "nbf"
+ SubjectKey = "sub"
+)
+
+// Token represents a generic JWT token.
+// which are type-aware (to an extent). Other claims may be accessed via the `Get`/`Set`
+// methods but their types are not taken into consideration at all. If you have non-standard
+// claims that you must frequently access, consider creating accessors functions
+// like the following
+//
+// func SetFoo(tok jwt.Token) error
+// func GetFoo(tok jwt.Token) (*Customtyp, error)
+//
+// Embedding jwt.Token into another struct is not recommended, because
+// jwt.Token needs to handle private claims, and this really does not
+// work well when it is embedded in other structure
+type Token interface {
+ Audience() []string
+ Expiration() time.Time
+ IssuedAt() time.Time
+ Issuer() string
+ JwtID() string
+ NotBefore() time.Time
+ Subject() string
+ PrivateClaims() map[string]interface{}
+ Get(string) (interface{}, bool)
+ Set(string, interface{}) error
+ Remove(string) error
+ Clone() (Token, error)
+ Iterate(context.Context) Iterator
+ Walk(context.Context, Visitor) error
+ AsMap(context.Context) (map[string]interface{}, error)
+}
+type stdToken struct {
+ mu *sync.RWMutex
+ dc DecodeCtx // per-object context for decoding
+ audience types.StringList // https://tools.ietf.org/html/rfc7519#section-4.1.3
+ expiration *types.NumericDate // https://tools.ietf.org/html/rfc7519#section-4.1.4
+ issuedAt *types.NumericDate // https://tools.ietf.org/html/rfc7519#section-4.1.6
+ issuer *string // https://tools.ietf.org/html/rfc7519#section-4.1.1
+ jwtID *string // https://tools.ietf.org/html/rfc7519#section-4.1.7
+ notBefore *types.NumericDate // https://tools.ietf.org/html/rfc7519#section-4.1.5
+ subject *string // https://tools.ietf.org/html/rfc7519#section-4.1.2
+ privateClaims map[string]interface{}
+}
+
+// New creates a standard token, with minimal knowledge of
+// possible claims. Standard claims include"aud", "exp", "iat", "iss", "jti", "nbf" and "sub".
+// Convenience accessors are provided for these standard claims
+func New() Token {
+ return &stdToken{
+ mu: &sync.RWMutex{},
+ privateClaims: make(map[string]interface{}),
+ }
+}
+
+func (t *stdToken) Get(name string) (interface{}, bool) {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ switch name {
+ case AudienceKey:
+ if t.audience == nil {
+ return nil, false
+ }
+ v := t.audience.Get()
+ return v, true
+ case ExpirationKey:
+ if t.expiration == nil {
+ return nil, false
+ }
+ v := t.expiration.Get()
+ return v, true
+ case IssuedAtKey:
+ if t.issuedAt == nil {
+ return nil, false
+ }
+ v := t.issuedAt.Get()
+ return v, true
+ case IssuerKey:
+ if t.issuer == nil {
+ return nil, false
+ }
+ v := *(t.issuer)
+ return v, true
+ case JwtIDKey:
+ if t.jwtID == nil {
+ return nil, false
+ }
+ v := *(t.jwtID)
+ return v, true
+ case NotBeforeKey:
+ if t.notBefore == nil {
+ return nil, false
+ }
+ v := t.notBefore.Get()
+ return v, true
+ case SubjectKey:
+ if t.subject == nil {
+ return nil, false
+ }
+ v := *(t.subject)
+ return v, true
+ default:
+ v, ok := t.privateClaims[name]
+ return v, ok
+ }
+}
+
+func (t *stdToken) Remove(key string) error {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ switch key {
+ case AudienceKey:
+ t.audience = nil
+ case ExpirationKey:
+ t.expiration = nil
+ case IssuedAtKey:
+ t.issuedAt = nil
+ case IssuerKey:
+ t.issuer = nil
+ case JwtIDKey:
+ t.jwtID = nil
+ case NotBeforeKey:
+ t.notBefore = nil
+ case SubjectKey:
+ t.subject = nil
+ default:
+ delete(t.privateClaims, key)
+ }
+ return nil
+}
+
+func (t *stdToken) Set(name string, value interface{}) error {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ return t.setNoLock(name, value)
+}
+
+func (t *stdToken) DecodeCtx() DecodeCtx {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.dc
+}
+
+func (t *stdToken) SetDecodeCtx(v DecodeCtx) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.dc = v
+}
+
+func (t *stdToken) setNoLock(name string, value interface{}) error {
+ switch name {
+ case AudienceKey:
+ var acceptor types.StringList
+ if err := acceptor.Accept(value); err != nil {
+ return errors.Wrapf(err, `invalid value for %s key`, AudienceKey)
+ }
+ t.audience = acceptor
+ return nil
+ case ExpirationKey:
+ var acceptor types.NumericDate
+ if err := acceptor.Accept(value); err != nil {
+ return errors.Wrapf(err, `invalid value for %s key`, ExpirationKey)
+ }
+ t.expiration = &acceptor
+ return nil
+ case IssuedAtKey:
+ var acceptor types.NumericDate
+ if err := acceptor.Accept(value); err != nil {
+ return errors.Wrapf(err, `invalid value for %s key`, IssuedAtKey)
+ }
+ t.issuedAt = &acceptor
+ return nil
+ case IssuerKey:
+ if v, ok := value.(string); ok {
+ t.issuer = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, IssuerKey, value)
+ case JwtIDKey:
+ if v, ok := value.(string); ok {
+ t.jwtID = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, JwtIDKey, value)
+ case NotBeforeKey:
+ var acceptor types.NumericDate
+ if err := acceptor.Accept(value); err != nil {
+ return errors.Wrapf(err, `invalid value for %s key`, NotBeforeKey)
+ }
+ t.notBefore = &acceptor
+ return nil
+ case SubjectKey:
+ if v, ok := value.(string); ok {
+ t.subject = &v
+ return nil
+ }
+ return errors.Errorf(`invalid value for %s key: %T`, SubjectKey, value)
+ default:
+ if t.privateClaims == nil {
+ t.privateClaims = map[string]interface{}{}
+ }
+ t.privateClaims[name] = value
+ }
+ return nil
+}
+
+func (t *stdToken) Audience() []string {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ if t.audience != nil {
+ return t.audience.Get()
+ }
+ return nil
+}
+
+func (t *stdToken) Expiration() time.Time {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ if t.expiration != nil {
+ return t.expiration.Get()
+ }
+ return time.Time{}
+}
+
+func (t *stdToken) IssuedAt() time.Time {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ if t.issuedAt != nil {
+ return t.issuedAt.Get()
+ }
+ return time.Time{}
+}
+
+func (t *stdToken) Issuer() string {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ if t.issuer != nil {
+ return *(t.issuer)
+ }
+ return ""
+}
+
+func (t *stdToken) JwtID() string {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ if t.jwtID != nil {
+ return *(t.jwtID)
+ }
+ return ""
+}
+
+func (t *stdToken) NotBefore() time.Time {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ if t.notBefore != nil {
+ return t.notBefore.Get()
+ }
+ return time.Time{}
+}
+
+func (t *stdToken) Subject() string {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ if t.subject != nil {
+ return *(t.subject)
+ }
+ return ""
+}
+
+func (t *stdToken) PrivateClaims() map[string]interface{} {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.privateClaims
+}
+
+func (t *stdToken) makePairs() []*ClaimPair {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+
+ pairs := make([]*ClaimPair, 0, 7)
+ if t.audience != nil {
+ v := t.audience.Get()
+ pairs = append(pairs, &ClaimPair{Key: AudienceKey, Value: v})
+ }
+ if t.expiration != nil {
+ v := t.expiration.Get()
+ pairs = append(pairs, &ClaimPair{Key: ExpirationKey, Value: v})
+ }
+ if t.issuedAt != nil {
+ v := t.issuedAt.Get()
+ pairs = append(pairs, &ClaimPair{Key: IssuedAtKey, Value: v})
+ }
+ if t.issuer != nil {
+ v := *(t.issuer)
+ pairs = append(pairs, &ClaimPair{Key: IssuerKey, Value: v})
+ }
+ if t.jwtID != nil {
+ v := *(t.jwtID)
+ pairs = append(pairs, &ClaimPair{Key: JwtIDKey, Value: v})
+ }
+ if t.notBefore != nil {
+ v := t.notBefore.Get()
+ pairs = append(pairs, &ClaimPair{Key: NotBeforeKey, Value: v})
+ }
+ if t.subject != nil {
+ v := *(t.subject)
+ pairs = append(pairs, &ClaimPair{Key: SubjectKey, Value: v})
+ }
+ for k, v := range t.privateClaims {
+ pairs = append(pairs, &ClaimPair{Key: k, Value: v})
+ }
+ sort.Slice(pairs, func(i, j int) bool {
+ return pairs[i].Key.(string) < pairs[j].Key.(string)
+ })
+ return pairs
+}
+
+func (t *stdToken) UnmarshalJSON(buf []byte) error {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.audience = nil
+ t.expiration = nil
+ t.issuedAt = nil
+ t.issuer = nil
+ t.jwtID = nil
+ t.notBefore = nil
+ t.subject = nil
+ dec := json.NewDecoder(bytes.NewReader(buf))
+LOOP:
+ for {
+ tok, err := dec.Token()
+ if err != nil {
+ return errors.Wrap(err, `error reading token`)
+ }
+ switch tok := tok.(type) {
+ case json.Delim:
+ // Assuming we're doing everything correctly, we should ONLY
+ // get either '{' or '}' here.
+ if tok == '}' { // End of object
+ break LOOP
+ } else if tok != '{' {
+ return errors.Errorf(`expected '{', but got '%c'`, tok)
+ }
+ case string: // Objects can only have string keys
+ switch tok {
+ case AudienceKey:
+ var decoded types.StringList
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, AudienceKey)
+ }
+ t.audience = decoded
+ case ExpirationKey:
+ var decoded types.NumericDate
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, ExpirationKey)
+ }
+ t.expiration = &decoded
+ case IssuedAtKey:
+ var decoded types.NumericDate
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, IssuedAtKey)
+ }
+ t.issuedAt = &decoded
+ case IssuerKey:
+ if err := json.AssignNextStringToken(&t.issuer, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, IssuerKey)
+ }
+ case JwtIDKey:
+ if err := json.AssignNextStringToken(&t.jwtID, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, JwtIDKey)
+ }
+ case NotBeforeKey:
+ var decoded types.NumericDate
+ if err := dec.Decode(&decoded); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, NotBeforeKey)
+ }
+ t.notBefore = &decoded
+ case SubjectKey:
+ if err := json.AssignNextStringToken(&t.subject, dec); err != nil {
+ return errors.Wrapf(err, `failed to decode value for key %s`, SubjectKey)
+ }
+ default:
+ if dc := t.dc; dc != nil {
+ if localReg := dc.Registry(); localReg != nil {
+ decoded, err := localReg.Decode(dec, tok)
+ if err == nil {
+ t.setNoLock(tok, decoded)
+ continue
+ }
+ }
+ }
+ decoded, err := registry.Decode(dec, tok)
+ if err == nil {
+ t.setNoLock(tok, decoded)
+ continue
+ }
+ return errors.Wrapf(err, `could not decode field %s`, tok)
+ }
+ default:
+ return errors.Errorf(`invalid token %T`, tok)
+ }
+ }
+ return nil
+}
+
+func (t stdToken) MarshalJSON() ([]byte, error) {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ buf := pool.GetBytesBuffer()
+ defer pool.ReleaseBytesBuffer(buf)
+ buf.WriteByte('{')
+ enc := json.NewEncoder(buf)
+ for i, pair := range t.makePairs() {
+ f := pair.Key.(string)
+ if i > 0 {
+ buf.WriteByte(',')
+ }
+ buf.WriteRune('"')
+ buf.WriteString(f)
+ buf.WriteString(`":`)
+ switch f {
+ case AudienceKey:
+ if err := json.EncodeAudience(enc, pair.Value.([]string)); err != nil {
+ return nil, errors.Wrap(err, `failed to encode "aud"`)
+ }
+ continue
+ case ExpirationKey, IssuedAtKey, NotBeforeKey:
+ enc.Encode(pair.Value.(time.Time).Unix())
+ continue
+ }
+ switch v := pair.Value.(type) {
+ case []byte:
+ buf.WriteRune('"')
+ buf.WriteString(base64.EncodeToString(v))
+ buf.WriteRune('"')
+ default:
+ if err := enc.Encode(v); err != nil {
+ return nil, errors.Wrapf(err, `failed to marshal field %s`, f)
+ }
+ buf.Truncate(buf.Len() - 1)
+ }
+ }
+ buf.WriteByte('}')
+ ret := make([]byte, buf.Len())
+ copy(ret, buf.Bytes())
+ return ret, nil
+}
+
+func (t *stdToken) Iterate(ctx context.Context) Iterator {
+ pairs := t.makePairs()
+ ch := make(chan *ClaimPair, len(pairs))
+ go func(ctx context.Context, ch chan *ClaimPair, pairs []*ClaimPair) {
+ defer close(ch)
+ for _, pair := range pairs {
+ select {
+ case <-ctx.Done():
+ return
+ case ch <- pair:
+ }
+ }
+ }(ctx, ch, pairs)
+ return mapiter.New(ch)
+}
+
+func (t *stdToken) Walk(ctx context.Context, visitor Visitor) error {
+ return iter.WalkMap(ctx, t, visitor)
+}
+
+func (t *stdToken) AsMap(ctx context.Context) (map[string]interface{}, error) {
+ return iter.AsMap(ctx, t)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwt/validate.go b/vendor/github.com/lestrrat-go/jwx/jwt/validate.go
new file mode 100644
index 000000000..c1a6894c0
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwt/validate.go
@@ -0,0 +1,383 @@
+package jwt
+
+import (
+ "context"
+ "strconv"
+ "time"
+
+ "github.com/pkg/errors"
+)
+
+type Clock interface {
+ Now() time.Time
+}
+type ClockFunc func() time.Time
+
+func (f ClockFunc) Now() time.Time {
+ return f()
+}
+
+func isSupportedTimeClaim(c string) error {
+ switch c {
+ case ExpirationKey, IssuedAtKey, NotBeforeKey:
+ return nil
+ }
+ return NewValidationError(errors.Errorf(`unsupported time claim %s`, strconv.Quote(c)))
+}
+
+func timeClaim(t Token, clock Clock, c string) time.Time {
+ switch c {
+ case ExpirationKey:
+ return t.Expiration()
+ case IssuedAtKey:
+ return t.IssuedAt()
+ case NotBeforeKey:
+ return t.NotBefore()
+ case "":
+ return clock.Now()
+ }
+ return time.Time{} // should *NEVER* reach here, but...
+}
+
+// Validate makes sure that the essential claims stand.
+//
+// See the various `WithXXX` functions for optional parameters
+// that can control the behavior of this method.
+func Validate(t Token, options ...ValidateOption) error {
+ ctx := context.Background()
+ var clock Clock = ClockFunc(time.Now)
+ var skew time.Duration
+ var validators = []Validator{
+ IsIssuedAtValid(),
+ IsExpirationValid(),
+ IsNbfValid(),
+ }
+ for _, o := range options {
+ //nolint:forcetypeassert
+ switch o.Ident() {
+ case identClock{}:
+ clock = o.Value().(Clock)
+ case identAcceptableSkew{}:
+ skew = o.Value().(time.Duration)
+ case identContext{}:
+ ctx = o.Value().(context.Context)
+ case identValidator{}:
+ v := o.Value().(Validator)
+ switch v := v.(type) {
+ case *isInTimeRange:
+ if v.c1 != "" {
+ if err := isSupportedTimeClaim(v.c1); err != nil {
+ return err
+ }
+ validators = append(validators, IsRequired(v.c1))
+ }
+ if v.c2 != "" {
+ if err := isSupportedTimeClaim(v.c2); err != nil {
+ return err
+ }
+ validators = append(validators, IsRequired(v.c2))
+ }
+ }
+ validators = append(validators, v)
+ }
+ }
+
+ ctx = SetValidationCtxSkew(ctx, skew)
+ ctx = SetValidationCtxClock(ctx, clock)
+ for _, v := range validators {
+ if err := v.Validate(ctx, t); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+type isInTimeRange struct {
+ c1 string
+ c2 string
+ dur time.Duration
+ less bool // if true, d =< c1 - c2. otherwise d >= c1 - c2
+}
+
+// MaxDeltaIs implements the logic behind `WithMaxDelta()` option
+func MaxDeltaIs(c1, c2 string, dur time.Duration) Validator {
+ return &isInTimeRange{
+ c1: c1,
+ c2: c2,
+ dur: dur,
+ less: true,
+ }
+}
+
+// MinDeltaIs implements the logic behind `WithMinDelta()` option
+func MinDeltaIs(c1, c2 string, dur time.Duration) Validator {
+ return &isInTimeRange{
+ c1: c1,
+ c2: c2,
+ dur: dur,
+ less: false,
+ }
+}
+
+func (iitr *isInTimeRange) Validate(ctx context.Context, t Token) error {
+ clock := ValidationCtxClock(ctx) // MUST be populated
+ skew := ValidationCtxSkew(ctx) // MUST be populated
+ // We don't check if the claims already exist, because we already did that
+ // by piggybacking on `required` check.
+ t1 := timeClaim(t, clock, iitr.c1).Truncate(time.Second)
+ t2 := timeClaim(t, clock, iitr.c2).Truncate(time.Second)
+ if iitr.less { // t1 - t2 <= iitr.dur
+ // t1 - t2 < iitr.dur + skew
+ if t1.Sub(t2) > iitr.dur+skew {
+ return NewValidationError(errors.Errorf(`iitr between %s and %s exceeds %s (skew %s)`, iitr.c1, iitr.c2, iitr.dur, skew))
+ }
+ } else {
+ if t1.Sub(t2) < iitr.dur-skew {
+ return NewValidationError(errors.Errorf(`iitr between %s and %s is less than %s (skew %s)`, iitr.c1, iitr.c2, iitr.dur, skew))
+ }
+ }
+ return nil
+}
+
+type ValidationError interface {
+ error
+ isValidationError()
+}
+
+func NewValidationError(err error) ValidationError {
+ return &validationError{error: err}
+}
+
+// This is a generic validation error.
+type validationError struct {
+ error
+}
+
+func (validationError) isValidationError() {}
+
+var errTokenExpired = NewValidationError(errors.New(`exp not satisfied`))
+var errInvalidIssuedAt = NewValidationError(errors.New(`iat not satisfied`))
+var errTokenNotYetValid = NewValidationError(errors.New(`nbf not satisfied`))
+
+// ErrTokenExpired returns the immutable error used when `exp` claim
+// is not satisfied
+func ErrTokenExpired() error {
+ return errTokenExpired
+}
+
+// ErrInvalidIssuedAt returns the immutable error used when `iat` claim
+// is not satisfied
+func ErrInvalidIssuedAt() error {
+ return errInvalidIssuedAt
+}
+
+func ErrTokenNotYetValid() error {
+ return errTokenNotYetValid
+}
+
+// Validator describes interface to validate a Token.
+type Validator interface {
+ // Validate should return an error if a required conditions is not met.
+ // This method will be changed in the next major release to return
+ // jwt.ValidationError instead of error to force users to return
+ // a validation error even for user-specified validators
+ Validate(context.Context, Token) error
+}
+
+// ValidatorFunc is a type of Validator that does not have any
+// state, that is implemented as a function
+type ValidatorFunc func(context.Context, Token) error
+
+func (vf ValidatorFunc) Validate(ctx context.Context, tok Token) error {
+ return vf(ctx, tok)
+}
+
+type identValidationCtxClock struct{}
+type identValidationCtxSkew struct{}
+
+func SetValidationCtxClock(ctx context.Context, cl Clock) context.Context {
+ return context.WithValue(ctx, identValidationCtxClock{}, cl)
+}
+
+// ValidationCtxClock returns the Clock object associated with
+// the current validation context. This value will always be available
+// during validation of tokens.
+func ValidationCtxClock(ctx context.Context) Clock {
+ return ctx.Value(identValidationCtxClock{}).(Clock)
+}
+
+func SetValidationCtxSkew(ctx context.Context, dur time.Duration) context.Context {
+ return context.WithValue(ctx, identValidationCtxSkew{}, dur)
+}
+
+func ValidationCtxSkew(ctx context.Context) time.Duration {
+ return ctx.Value(identValidationCtxSkew{}).(time.Duration)
+}
+
+// IsExpirationValid is one of the default validators that will be executed.
+// It does not need to be specified by users, but it exists as an
+// exported field so that you can check what it does.
+//
+// The supplied context.Context object must have the "clock" and "skew"
+// populated with appropriate values using SetValidationCtxClock() and
+// SetValidationCtxSkew()
+func IsExpirationValid() Validator {
+ return ValidatorFunc(isExpirationValid)
+}
+
+func isExpirationValid(ctx context.Context, t Token) error {
+ if tv := t.Expiration(); !tv.IsZero() && tv.Unix() != 0 {
+ clock := ValidationCtxClock(ctx) // MUST be populated
+ now := clock.Now().Truncate(time.Second)
+ ttv := tv.Truncate(time.Second)
+ skew := ValidationCtxSkew(ctx) // MUST be populated
+ if !now.Before(ttv.Add(skew)) {
+ return ErrTokenExpired()
+ }
+ }
+ return nil
+}
+
+// IsIssuedAtValid is one of the default validators that will be executed.
+// It does not need to be specified by users, but it exists as an
+// exported field so that you can check what it does.
+//
+// The supplied context.Context object must have the "clock" and "skew"
+// populated with appropriate values using SetValidationCtxClock() and
+// SetValidationCtxSkew()
+func IsIssuedAtValid() Validator {
+ return ValidatorFunc(isIssuedAtValid)
+}
+
+func isIssuedAtValid(ctx context.Context, t Token) error {
+ if tv := t.IssuedAt(); !tv.IsZero() && tv.Unix() != 0 {
+ clock := ValidationCtxClock(ctx) // MUST be populated
+ now := clock.Now().Truncate(time.Second)
+ ttv := tv.Truncate(time.Second)
+ skew := ValidationCtxSkew(ctx) // MUST be populated
+ if now.Before(ttv.Add(-1 * skew)) {
+ return ErrInvalidIssuedAt()
+ }
+ }
+ return nil
+}
+
+// IsNbfValid is one of the default validators that will be executed.
+// It does not need to be specified by users, but it exists as an
+// exported field so that you can check what it does.
+//
+// The supplied context.Context object must have the "clock" and "skew"
+// populated with appropriate values using SetValidationCtxClock() and
+// SetValidationCtxSkew()
+func IsNbfValid() Validator {
+ return ValidatorFunc(isNbfValid)
+}
+
+func isNbfValid(ctx context.Context, t Token) error {
+ if tv := t.NotBefore(); !tv.IsZero() && tv.Unix() != 0 {
+ clock := ValidationCtxClock(ctx) // MUST be populated
+ now := clock.Now().Truncate(time.Second)
+ ttv := tv.Truncate(time.Second)
+ skew := ValidationCtxSkew(ctx) // MUST be populated
+ // now cannot be before t, so we check for now > t - skew
+ if !now.Equal(ttv) && !now.After(ttv.Add(-1*skew)) {
+ return ErrTokenNotYetValid()
+ }
+ }
+ return nil
+}
+
+type claimContainsString struct {
+ name string
+ value string
+}
+
+// ClaimContainsString can be used to check if the claim called `name`, which is
+// expected to be a list of strings, contains `value`. Currently because of the
+// implementation this will probably only work for `aud` fields.
+func ClaimContainsString(name, value string) Validator {
+ return claimContainsString{
+ name: name,
+ value: value,
+ }
+}
+
+// IsValidationError returns true if the error is a validation error
+func IsValidationError(err error) bool {
+ switch err {
+ case errTokenExpired, errTokenNotYetValid, errInvalidIssuedAt:
+ return true
+ default:
+ switch err.(type) {
+ case *validationError:
+ return true
+ default:
+ return false
+ }
+ }
+}
+
+func (ccs claimContainsString) Validate(_ context.Context, t Token) error {
+ v, ok := t.Get(ccs.name)
+ if !ok {
+ return NewValidationError(errors.Errorf(`claim %q not found`, ccs.name))
+ }
+
+ list, ok := v.([]string)
+ if !ok {
+ return NewValidationError(errors.Errorf(`claim %q must be a []string (got %T)`, ccs.name, v))
+ }
+
+ var found bool
+ for _, v := range list {
+ if v == ccs.value {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return NewValidationError(errors.Errorf(`%s not satisfied`, ccs.name))
+ }
+ return nil
+}
+
+type claimValueIs struct {
+ name string
+ value interface{}
+}
+
+// ClaimValueIs creates a Validator that checks if the value of claim `name`
+// matches `value`. The comparison is done using a simple `==` comparison,
+// and therefore complex comparisons may fail using this code. If you
+// need to do more, use a custom Validator.
+func ClaimValueIs(name string, value interface{}) Validator {
+ return &claimValueIs{name: name, value: value}
+}
+
+func (cv *claimValueIs) Validate(_ context.Context, t Token) error {
+ v, ok := t.Get(cv.name)
+ if !ok {
+ return NewValidationError(errors.Errorf(`%q not satisfied: claim %q does not exist`, cv.name, cv.name))
+ }
+ if v != cv.value {
+ return NewValidationError(errors.Errorf(`%q not satisfied: values do not match`, cv.name))
+ }
+ return nil
+}
+
+// IsRequired creates a Validator that checks if the required claim `name`
+// exists in the token
+func IsRequired(name string) Validator {
+ return isRequired(name)
+}
+
+type isRequired string
+
+func (ir isRequired) Validate(_ context.Context, t Token) error {
+ _, ok := t.Get(string(ir))
+ if !ok {
+ return NewValidationError(errors.Errorf(`required claim %q was not found`, string(ir)))
+ }
+ return nil
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/jwx.go b/vendor/github.com/lestrrat-go/jwx/jwx.go
new file mode 100644
index 000000000..17668d1e5
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/jwx.go
@@ -0,0 +1,44 @@
+//go:generate ./gen.sh
+//go:generate stringer -type=FormatKind
+//go:generate mv formatkind_string.go formatkind_string_gen.go
+
+// Package jwx contains tools that deal with the various JWx (JOSE)
+// technologies such as JWT, JWS, JWE, etc in Go.
+//
+// JWS (https://tools.ietf.org/html/rfc7515)
+// JWE (https://tools.ietf.org/html/rfc7516)
+// JWK (https://tools.ietf.org/html/rfc7517)
+// JWA (https://tools.ietf.org/html/rfc7518)
+// JWT (https://tools.ietf.org/html/rfc7519)
+//
+// The primary focus of this library tool set is to implement the extremely
+// flexible OAuth2 / OpenID Connect protocols.
+//
+// Examples are stored in a separate Go module (to avoid adding
+// dependencies to this module), and thus does not appear in the
+// online documentation for this module.
+// You can find the examples in Github at https://github.com/lestrrat-go/jwx/examples
+//
+// You can find more high level documentation at Github (https://github.com/lestrrat-go/jwx)
+package jwx
+
+import (
+ "github.com/lestrrat-go/jwx/internal/json"
+)
+
+// DecoderSettings gives you a access to configure the "encoding/json".Decoder
+// used to decode JSON objects within the jwx framework.
+func DecoderSettings(options ...JSONOption) {
+ // XXX We're using this format instead of just passing a single boolean
+ // in case a new option is to be added some time later
+ var useNumber bool
+ for _, option := range options {
+ //nolint:forcetypeassert
+ switch option.Ident() {
+ case identUseNumber{}:
+ useNumber = option.Value().(bool)
+ }
+ }
+
+ json.DecoderSettings(useNumber)
+}
diff --git a/vendor/github.com/lestrrat-go/jwx/options.go b/vendor/github.com/lestrrat-go/jwx/options.go
new file mode 100644
index 000000000..2e191abbe
--- /dev/null
+++ b/vendor/github.com/lestrrat-go/jwx/options.go
@@ -0,0 +1,30 @@
+package jwx
+
+import "github.com/lestrrat-go/option"
+
+type identUseNumber struct{}
+
+type Option = option.Interface
+
+type JSONOption interface {
+ Option
+ isJSONOption()
+}
+
+type jsonOption struct {
+ Option
+}
+
+func (o *jsonOption) isJSONOption() {}
+
+func newJSONOption(n interface{}, v interface{}) JSONOption {
+ return &jsonOption{option.New(n, v)}
+}
+
+// WithUseNumber controls whether the jwx package should unmarshal
+// JSON objects with the "encoding/json".Decoder.UseNumber feature on.
+//
+// Default is false.
+func WithUseNumber(b bool) JSONOption {
+ return newJSONOption(identUseNumber{}, b)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index f0752b053..6a032b313 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -140,13 +140,17 @@ github.com/getsentry/sentry-go/internal/ratelimit
## explicit
github.com/go-chi/chi
github.com/go-chi/chi/middleware
-# github.com/go-chi/cors v1.0.0
+# github.com/go-chi/chi/v5 v5.0.7
+## explicit
+github.com/go-chi/chi/v5
+github.com/go-chi/chi/v5/middleware
+# github.com/go-chi/cors v1.2.0
## explicit
github.com/go-chi/cors
-# github.com/go-chi/httprate v0.4.0
+# github.com/go-chi/httprate v0.5.2
## explicit
github.com/go-chi/httprate
-# github.com/go-chi/jwtauth v0.0.0-20190109153619-47840abb19b3
+# github.com/go-chi/jwtauth v1.2.0
## explicit
github.com/go-chi/jwtauth
# github.com/go-oauth2/oauth2/v4 v4.4.2
@@ -251,13 +255,25 @@ github.com/lestrrat-go/iter/arrayiter
github.com/lestrrat-go/iter/mapiter
# github.com/lestrrat-go/jwx v1.2.14
## explicit
+github.com/lestrrat-go/jwx
github.com/lestrrat-go/jwx/internal/base64
github.com/lestrrat-go/jwx/internal/ecutil
github.com/lestrrat-go/jwx/internal/iter
github.com/lestrrat-go/jwx/internal/json
+github.com/lestrrat-go/jwx/internal/keyconv
github.com/lestrrat-go/jwx/internal/pool
github.com/lestrrat-go/jwx/jwa
+github.com/lestrrat-go/jwx/jwe
+github.com/lestrrat-go/jwx/jwe/internal/aescbc
+github.com/lestrrat-go/jwx/jwe/internal/cipher
+github.com/lestrrat-go/jwx/jwe/internal/concatkdf
+github.com/lestrrat-go/jwx/jwe/internal/content_crypt
+github.com/lestrrat-go/jwx/jwe/internal/keyenc
+github.com/lestrrat-go/jwx/jwe/internal/keygen
github.com/lestrrat-go/jwx/jwk
+github.com/lestrrat-go/jwx/jws
+github.com/lestrrat-go/jwx/jwt
+github.com/lestrrat-go/jwx/jwt/internal/types
github.com/lestrrat-go/jwx/x25519
# github.com/lestrrat-go/option v1.0.0
github.com/lestrrat-go/option