From a27ba09872370a1c486c82a28147513f898cc327 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Tue, 19 Apr 2022 14:33:07 +0200 Subject: [PATCH 01/15] feat(op): dynamic issuer depending on request / host BREAKING CHANGE: The OpenID Provider package is now able to handle multiple issuers with a single storage implementation. The issuer will be selected from the host of the request and passed into the context, where every function can read it from if necessary. This results in some fundamental changes: - `Configuration` interface: - `Issuer() string` has been changed to `IssuerFromRequest(r *http.Request) string` - `Insecure() bool` has been added - OpenIDProvider interface and dependants: - `Issuer` has been removed from Config struct - `NewOpenIDProvider` now takes an additional parameter `issuer` and returns a pointer to the public/default implementation and not an OpenIDProvider interface: `NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opOpts ...Option) (OpenIDProvider, error)` changed to `NewOpenIDProvider(ctx context.Context, issuer string, config *Config, storage Storage, opOpts ...Option) (*Provider, error)` - therefore the parameter type Option changed to the public type as well: `Option func(o *Provider) error` - `AuthCallbackURL(o OpenIDProvider) func(string) string` has been changed to `AuthCallbackURL(o OpenIDProvider) func(context.Context, string) string` - `IDTokenHintVerifier() IDTokenHintVerifier` (Authorizer, OpenIDProvider, SessionEnder interfaces), `AccessTokenVerifier() AccessTokenVerifier` (Introspector, OpenIDProvider, Revoker, UserinfoProvider interfaces) and `JWTProfileVerifier() JWTProfileVerifier` (IntrospectorJWTProfile, JWTAuthorizationGrantExchanger, OpenIDProvider, RevokerJWTProfile interfaces) now take a context.Context parameter `IDTokenHintVerifier(context.Context) IDTokenHintVerifier`, `AccessTokenVerifier(context.Context) AccessTokenVerifier` and `JWTProfileVerifier(context.Context) JWTProfileVerifier` - `OidcDevMode` (CAOS_OIDC_DEV) environment variable check has been removed, use `WithAllowInsecure()` Option - Signing: the signer is not kept in memory anymore, but created on request from the loaded key: - `Signer` interface and func `NewSigner` have been removed - `ReadySigner(s Signer) ProbesFn` has been removed - `CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfiguration` has been changed to `CreateDiscoveryConfig(r *http.Request, config Configuration, storage DiscoverStorage) *oidc.DiscoveryConfiguration` - `Storage` interface: - `GetSigningKey(context.Context, chan<- jose.SigningKey)` has been changed to `SigningKey(context.Context) (SigningKey, error)` - `KeySet(context.Context) ([]Key, error)` has been added - `GetKeySet(context.Context) (*jose.JSONWebKeySet, error)` has been changed to `KeySet(context.Context) ([]Key, error)` - `SigAlgorithms(s Signer) []string` has been changed to `SigAlgorithms(ctx context.Context, storage DiscoverStorage) []string` - KeyProvider interface: `GetKeySet(context.Context) (*jose.JSONWebKeySet, error)` has been changed to `KeySet(context.Context) ([]Key, error)` - `CreateIDToken`: the Signer parameter has been removed --- example/server/internal/storage.go | 82 +++-- example/server/login.go | 15 +- example/server/op.go | 18 +- go.mod | 3 +- go.sum | 5 - pkg/op/auth_request.go | 13 +- pkg/op/config.go | 80 ++++- pkg/op/config_test.go | 300 +++++++++++++++-- pkg/op/context.go | 49 +++ pkg/op/context_test.go | 76 +++++ pkg/op/discovery.go | 238 +++++++------ pkg/op/discovery_test.go | 501 ++++++++++++++++++++++++---- pkg/op/keys.go | 19 +- pkg/op/keys_test.go | 32 +- pkg/op/mock/authorizer.mock.go | 37 +- pkg/op/mock/authorizer.mock.impl.go | 19 +- pkg/op/mock/configuration.mock.go | 27 +- pkg/op/mock/generate.go | 3 +- pkg/op/mock/key.mock.go | 16 +- pkg/op/mock/signer.mock.go | 124 +++++-- pkg/op/mock/storage.mock.go | 72 ++-- pkg/op/mock/storage.mock.impl.go | 42 +-- pkg/op/op.go | 223 ++++++------- pkg/op/probes.go | 8 - pkg/op/session.go | 4 +- pkg/op/signer.go | 88 ++--- pkg/op/storage.go | 5 +- pkg/op/token.go | 35 +- pkg/op/token_intospection.go | 7 +- pkg/op/token_jwt_profile.go | 4 +- pkg/op/token_request.go | 4 +- pkg/op/token_revocation.go | 8 +- pkg/op/userinfo.go | 4 +- 33 files changed, 1504 insertions(+), 657 deletions(-) create mode 100644 pkg/op/context.go create mode 100644 pkg/op/context_test.go diff --git a/example/server/internal/storage.go b/example/server/internal/storage.go index 8d61050..d4d51f8 100644 --- a/example/server/internal/storage.go +++ b/example/server/internal/storage.go @@ -43,9 +43,41 @@ type storage struct { } type signingKey struct { - ID string - Algorithm string - Key *rsa.PrivateKey + id string + algorithm jose.SignatureAlgorithm + key *rsa.PrivateKey +} + +func (s *signingKey) SignatureAlgorithm() jose.SignatureAlgorithm { + return s.algorithm +} + +func (s *signingKey) Key() interface{} { + return s.key +} + +func (s *signingKey) ID() string { + return s.id +} + +type publicKey struct { + signingKey +} + +func (s *publicKey) ID() string { + return s.id +} + +func (s *publicKey) Algorithm() jose.SignatureAlgorithm { + return s.algorithm +} + +func (s *publicKey) Use() string { + return "sig" +} + +func (s *publicKey) Key() interface{} { + return &s.key.PublicKey } func NewStorage() *storage { @@ -78,9 +110,9 @@ func NewStorage() *storage { }, }, signingKey: signingKey{ - ID: "id", - Algorithm: "RS256", - Key: key, + id: uuid.NewString(), + algorithm: jose.RS256, + key: key, }, } } @@ -113,6 +145,8 @@ func (s *storage) CheckUsernamePassword(username, password, id string) error { //CreateAuthRequest implements the op.Storage interface //it will be called after parsing and validation of the authentication request func (s *storage) CreateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, userID string) (op.AuthRequest, error) { + headers := op.IssuerFromContext(ctx) + _ = headers //typically, you'll fill your internal / storage model with the information of the passed object request := authRequestToInternal(authReq, userID) @@ -278,39 +312,29 @@ func (s *storage) RevokeToken(ctx context.Context, token string, userID string, return nil } -//GetSigningKey implements the op.Storage interface +//SigningKey implements the op.Storage interface //it will be called when creating the OpenID Provider -func (s *storage) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey) { +func (s *storage) SigningKey(ctx context.Context) (op.SigningKey, error) { //in this example the signing key is a static rsa.PrivateKey and the algorithm used is RS256 //you would obviously have a more complex implementation and store / retrieve the key from your database as well - // - //the idea of the signing key channel is, that you can (with what ever mechanism) rotate your signing key and - //switch the key of the signer via this channel - keyCh <- jose.SigningKey{ - Algorithm: jose.SignatureAlgorithm(s.signingKey.Algorithm), //always tell the signer with algorithm to use - Key: jose.JSONWebKey{ - KeyID: s.signingKey.ID, //always give the key an id so, that it will include it in the token header as `kid` claim - Key: s.signingKey.Key, - }, - } + return &s.signingKey, nil } -//GetKeySet implements the op.Storage interface +//SignatureAlgorithms implements the op.Storage interface +//it will be called to get the sign +func (s *storage) SignatureAlgorithms(context.Context) ([]jose.SignatureAlgorithm, error) { + return []jose.SignatureAlgorithm{s.signingKey.algorithm}, nil +} + +//KeySet implements the op.Storage interface //it will be called to get the current (public) keys, among others for the keys_endpoint or for validating access_tokens on the userinfo_endpoint, ... -func (s *storage) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error) { +func (s *storage) KeySet(ctx context.Context) ([]op.Key, error) { //as mentioned above, this example only has a single signing key without key rotation, //so it will directly use its public key // //when using key rotation you typically would store the public keys alongside the private keys in your database - //and give both of them an expiration date, with the public key having a longer lifetime (e.g. rotate private key every - return &jose.JSONWebKeySet{Keys: []jose.JSONWebKey{ - { - KeyID: s.signingKey.ID, - Algorithm: s.signingKey.Algorithm, - Use: oidc.KeyUseSignature, - Key: &s.signingKey.Key.PublicKey, - }}, - }, nil + //and give both of them an expiration date, with the public key having a longer lifetime + return []op.Key{&publicKey{s.signingKey}}, nil } //GetClientByClientID implements the op.Storage interface diff --git a/example/server/login.go b/example/server/login.go index 90d01d8..51168ac 100644 --- a/example/server/login.go +++ b/example/server/login.go @@ -1,11 +1,14 @@ package main import ( + "context" "fmt" "html/template" "net/http" "github.com/gorilla/mux" + + "github.com/caos/oidc/pkg/op" ) const ( @@ -46,22 +49,22 @@ var ( type login struct { authenticate authenticate router *mux.Router - callback func(string) string + callback func(context.Context, string) string } -func NewLogin(authenticate authenticate, callback func(string) string) *login { +func NewLogin(authenticate authenticate, callback func(context.Context, string) string, issuerInterceptor *op.IssuerInterceptor) *login { l := &login{ authenticate: authenticate, callback: callback, } - l.createRouter() + l.createRouter(issuerInterceptor) return l } -func (l *login) createRouter() { +func (l *login) createRouter(issuerInterceptor *op.IssuerInterceptor) { l.router = mux.NewRouter() l.router.Path("/username").Methods("GET").HandlerFunc(l.loginHandler) - l.router.Path("/username").Methods("POST").HandlerFunc(l.checkLoginHandler) + l.router.Path("/username").Methods("POST").HandlerFunc(issuerInterceptor.HandlerFunc(l.checkLoginHandler)) } type authenticate interface { @@ -111,5 +114,5 @@ func (l *login) checkLoginHandler(w http.ResponseWriter, r *http.Request) { renderLogin(w, id, err) return } - http.Redirect(w, r, l.callback(id), http.StatusFound) + http.Redirect(w, r, l.callback(r.Context(), id), http.StatusFound) } diff --git a/example/server/op.go b/example/server/op.go index 54b5041..45717f9 100644 --- a/example/server/op.go +++ b/example/server/op.go @@ -3,10 +3,8 @@ package main import ( "context" "crypto/sha256" - "fmt" "log" "net/http" - "os" "github.com/gorilla/mux" "golang.org/x/text/language" @@ -30,9 +28,6 @@ func init() { func main() { ctx := context.Background() - //this will allow us to use an issuer with http:// instead of https:// - os.Setenv(op.OidcDevMode, "true") - port := "9998" //the OpenID Provider requires a 32-byte key for (token) encryption @@ -62,7 +57,7 @@ func main() { //the provider will only take care of the OpenID Protocol, so there must be some sort of UI for the login process //for the simplicity of the example this means a simple page with username and password field - l := NewLogin(storage, op.AuthCallbackURL(provider)) + l := NewLogin(storage, op.AuthCallbackURL(provider), op.NewIssuerInterceptor(provider.IssuerFromRequest)) //regardless of how many pages / steps there are in the process, the UI must be registered in the router, //so we will direct all calls to /login to the login UI @@ -72,7 +67,8 @@ func main() { //is served on the correct path // //if your issuer ends with a path (e.g. http://localhost:9998/custom/path/), - //then you would have to set the path prefix (/custom/path/) + //then you would have to set the path prefix (/custom/path/): + //router.PathPrefix("/custom/path/").Handler(http.StripPrefix("/custom/path", provider.HttpHandler())) router.PathPrefix("/").Handler(provider.HttpHandler()) server := &http.Server{ @@ -89,9 +85,8 @@ func main() { //newOP will create an OpenID Provider for localhost on a specified port with a given encryption key //and a predefined default logout uri //it will enable all options (see descriptions) -func newOP(ctx context.Context, storage op.Storage, port string, key [32]byte) (op.OpenIDProvider, error) { +func newOP(ctx context.Context, storage op.Storage, port string, key [32]byte) (*op.Provider, error) { config := &op.Config{ - Issuer: fmt.Sprintf("http://localhost:%s/", port), CryptoKey: key, //will be used if the end_session endpoint is called without a post_logout_redirect_uri @@ -115,7 +110,10 @@ func newOP(ctx context.Context, storage op.Storage, port string, key [32]byte) ( //this example has only static texts (in English), so we'll set the here accordingly SupportedUILocales: []language.Tag{language.English}, } - handler, err := op.NewOpenIDProvider(ctx, config, storage, + //handler, err := op.NewOpenIDProvider(ctx, fmt.Sprintf("http://localhost:%s/", port), config, storage, + handler, err := op.NewDynamicOpenIDProvider(ctx, "/", config, storage, + //we must explicitly allow the use of the http issuer + op.WithAllowInsecure(), //as an example on how to customize an endpoint this will change the authorization_endpoint from /authorize to /auth op.WithCustomAuthEndpoint(op.NewEndpoint("auth")), ) diff --git a/go.mod b/go.mod index f23a1b3..81b5d4a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/caos/oidc go 1.15 require ( - github.com/caos/logging v0.3.1 github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.2 // indirect github.com/google/go-github/v31 v31.0.0 @@ -16,7 +15,9 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.1 golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 + golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect golang.org/x/text v0.3.7 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/square/go-jose.v2 v2.6.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 605a76e..58bdbae 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,6 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/caos/logging v0.3.1 h1:892AMeHs09D0e3ZcGB+QDRsZ5+2xtPAsAhOy8eKfztc= -github.com/caos/logging v0.3.1/go.mod h1:B8QNS0WDmR2Keac52Fw+XN4ZJkzLDGrcRIPB2Ux4uRo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -138,7 +136,6 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -402,8 +399,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index 909b8b0..ff90bd4 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -37,10 +37,8 @@ type Authorizer interface { Storage() Storage Decoder() httphelper.Decoder Encoder() httphelper.Encoder - Signer() Signer - IDTokenHintVerifier() IDTokenHintVerifier + IDTokenHintVerifier(context.Context) IDTokenHintVerifier Crypto() Crypto - Issuer() string RequestObjectSupported() bool } @@ -71,8 +69,9 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { AuthRequestError(w, r, authReq, err, authorizer.Encoder()) return } + ctx := r.Context() if authReq.RequestParam != "" && authorizer.RequestObjectSupported() { - authReq, err = ParseRequestObject(r.Context(), authReq, authorizer.Storage(), authorizer.Issuer()) + authReq, err = ParseRequestObject(ctx, authReq, authorizer.Storage(), IssuerFromContext(ctx)) if err != nil { AuthRequestError(w, r, authReq, err, authorizer.Encoder()) return @@ -82,7 +81,7 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { if validater, ok := authorizer.(AuthorizeValidator); ok { validation = validater.ValidateAuthRequest } - userID, err := validation(r.Context(), authReq, authorizer.Storage(), authorizer.IDTokenHintVerifier()) + userID, err := validation(ctx, authReq, authorizer.Storage(), authorizer.IDTokenHintVerifier(ctx)) if err != nil { AuthRequestError(w, r, authReq, err, authorizer.Encoder()) return @@ -91,12 +90,12 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { AuthRequestError(w, r, authReq, oidc.ErrRequestNotSupported(), authorizer.Encoder()) return } - req, err := authorizer.Storage().CreateAuthRequest(r.Context(), authReq, userID) + req, err := authorizer.Storage().CreateAuthRequest(ctx, authReq, userID) if err != nil { AuthRequestError(w, r, authReq, oidc.DefaultToServerError(err, "unable to save auth request"), authorizer.Encoder()) return } - client, err := authorizer.Storage().GetClientByClientID(r.Context(), req.GetClientID()) + client, err := authorizer.Storage().GetClientByClientID(ctx, req.GetClientID()) if err != nil { AuthRequestError(w, r, req, oidc.DefaultToServerError(err, "unable to retrieve client by id"), authorizer.Encoder()) return diff --git a/pkg/op/config.go b/pkg/op/config.go index 527e134..7b097c4 100644 --- a/pkg/op/config.go +++ b/pkg/op/config.go @@ -2,16 +2,24 @@ package op import ( "errors" + "net/http" "net/url" - "os" + "strings" "golang.org/x/text/language" ) -const OidcDevMode = "CAOS_OIDC_DEV" +var ( + ErrInvalidIssuerPath = errors.New("no fragments or query allowed for issuer") + ErrInvalidIssuerNoIssuer = errors.New("missing issuer") + ErrInvalidIssuerURL = errors.New("invalid url for issuer") + ErrInvalidIssuerMissingHost = errors.New("host for issuer missing") + ErrInvalidIssuerHTTPS = errors.New("scheme for issuer must be `https`") +) type Configuration interface { - Issuer() string + IssuerFromRequest(r *http.Request) string + Insecure() bool AuthorizationEndpoint() Endpoint TokenEndpoint() Endpoint IntrospectionEndpoint() Endpoint @@ -37,32 +45,74 @@ type Configuration interface { SupportedUILocales() []language.Tag } -func ValidateIssuer(issuer string) error { +type IssuerFromRequest func(r *http.Request) string + +func IssuerFromHost(path string) func(bool) (IssuerFromRequest, error) { + return func(allowInsecure bool) (IssuerFromRequest, error) { + issuerPath, err := url.Parse(path) + if err != nil { + return nil, ErrInvalidIssuerURL + } + if err := ValidateIssuerPath(issuerPath); err != nil { + return nil, err + } + return func(r *http.Request) string { + return dynamicIssuer(r.Host, path, allowInsecure) + }, nil + } +} + +func StaticIssuer(issuer string) func(bool) (IssuerFromRequest, error) { + return func(allowInsecure bool) (IssuerFromRequest, error) { + if err := ValidateIssuer(issuer, allowInsecure); err != nil { + return nil, err + } + return func(_ *http.Request) string { + return issuer + }, nil + } +} + +func ValidateIssuer(issuer string, allowInsecure bool) error { if issuer == "" { - return errors.New("missing issuer") + return ErrInvalidIssuerNoIssuer } u, err := url.Parse(issuer) if err != nil { - return errors.New("invalid url for issuer") + return ErrInvalidIssuerURL } if u.Host == "" { - return errors.New("host for issuer missing") + return ErrInvalidIssuerMissingHost } if u.Scheme != "https" { - if !devLocalAllowed(u) { - return errors.New("scheme for issuer must be `https`") + if !devLocalAllowed(u, allowInsecure) { + return ErrInvalidIssuerHTTPS } } - if u.Fragment != "" || len(u.Query()) > 0 { - return errors.New("no fragments or query allowed for issuer") + return ValidateIssuerPath(u) +} + +func ValidateIssuerPath(issuer *url.URL) error { + if issuer.Fragment != "" || len(issuer.Query()) > 0 { + return ErrInvalidIssuerPath } return nil } -func devLocalAllowed(url *url.URL) bool { - _, b := os.LookupEnv(OidcDevMode) - if !b { - return b +func devLocalAllowed(url *url.URL, allowInsecure bool) bool { + if !allowInsecure { + return false } return url.Scheme == "http" } + +func dynamicIssuer(issuer, path string, allowInsecure bool) string { + schema := "https" + if allowInsecure { + schema = "http" + } + if len(path) > 0 && !strings.HasPrefix(path, "/") { + path = "/" + path + } + return schema + "://" + issuer + path +} diff --git a/pkg/op/config_test.go b/pkg/op/config_test.go index 5029df8..cfe4e61 100644 --- a/pkg/op/config_test.go +++ b/pkg/op/config_test.go @@ -1,13 +1,17 @@ package op import ( - "os" + "net/http/httptest" + "net/url" "testing" + + "github.com/stretchr/testify/assert" ) func TestValidateIssuer(t *testing.T) { type args struct { - issuer string + issuer string + allowInsecure bool } tests := []struct { name string @@ -16,65 +20,97 @@ func TestValidateIssuer(t *testing.T) { }{ { "missing issuer fails", - args{""}, + args{ + issuer: "", + }, true, }, { "invalid url for issuer fails", - args{":issuer"}, - true, - }, - { - "invalid url for issuer fails", - args{":issuer"}, + args{ + issuer: ":issuer", + }, true, }, { "host for issuer missing fails", - args{"https:///issuer"}, - true, - }, - { - "host for not https fails", - args{"http://issuer.com"}, + args{ + issuer: "https:///issuer", + }, true, }, { "host with fragment fails", - args{"https://issuer.com/#issuer"}, + args{ + issuer: "https://issuer.com/#issuer", + }, true, }, { "host with query fails", - args{"https://issuer.com?issuer=me"}, + args{ + issuer: "https://issuer.com?issuer=me", + }, + true, + }, + { + "host with http fails", + args{ + issuer: "http://issuer.com", + }, true, }, { "host with https ok", - args{"https://issuer.com"}, + args{ + issuer: "https://issuer.com", + }, false, }, { - "localhost with http fails", - args{"http://localhost:9999"}, + "custom scheme fails", + args{ + issuer: "custom://localhost:9999", + }, + true, + }, + { + "http with allowInsecure ok", + args{ + issuer: "http://localhost:9999", + allowInsecure: true, + }, + false, + }, + { + "https with allowInsecure ok", + args{ + issuer: "https://localhost:9999", + allowInsecure: true, + }, + false, + }, + { + "custom scheme with allowInsecure fails", + args{ + issuer: "custom://localhost:9999", + allowInsecure: true, + }, true, }, } - //ensure env is not set - //nolint:errcheck - os.Unsetenv(OidcDevMode) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := ValidateIssuer(tt.args.issuer); (err != nil) != tt.wantErr { + if err := ValidateIssuer(tt.args.issuer, tt.args.allowInsecure); (err != nil) != tt.wantErr { t.Errorf("ValidateIssuer() error = %v, wantErr %v", err, tt.wantErr) } }) } } -func TestValidateIssuerDevLocalAllowed(t *testing.T) { +func TestValidateIssuerPath(t *testing.T) { type args struct { - issuer string + issuerPath *url.URL } tests := []struct { name string @@ -82,17 +118,217 @@ func TestValidateIssuerDevLocalAllowed(t *testing.T) { wantErr bool }{ { - "localhost with http with dev ok", - args{"http://localhost:9999"}, + "empty ok", + args{func() *url.URL { + u, _ := url.Parse("") + return u + }()}, false, }, + { + "custom ok", + args{func() *url.URL { + u, _ := url.Parse("/custom") + return u + }()}, + false, + }, + { + "fragment fails", + args{func() *url.URL { + u, _ := url.Parse("#fragment") + return u + }()}, + true, + }, + { + "query fails", + args{func() *url.URL { + u, _ := url.Parse("?query=value") + return u + }()}, + true, + }, } - //nolint:errcheck - os.Setenv(OidcDevMode, "true") for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := ValidateIssuer(tt.args.issuer); (err != nil) != tt.wantErr { - t.Errorf("ValidateIssuer() error = %v, wantErr %v", err, tt.wantErr) + if err := ValidateIssuerPath(tt.args.issuerPath); (err != nil) != tt.wantErr { + t.Errorf("ValidateIssuerPath() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestIssuerFromHost(t *testing.T) { + type args struct { + path string + allowInsecure bool + target string + } + type res struct { + issuer string + err error + } + tests := []struct { + name string + args args + res res + }{ + { + "invalid issuer path", + args{ + path: "/#fragment", + allowInsecure: false, + }, + res{ + issuer: "", + err: ErrInvalidIssuerPath, + }, + }, + { + "empty path secure", + args{ + path: "", + allowInsecure: false, + target: "https://issuer.com", + }, + res{ + issuer: "https://issuer.com", + err: nil, + }, + }, + { + "custom path secure", + args{ + path: "/custom/", + allowInsecure: false, + target: "https://issuer.com", + }, + res{ + issuer: "https://issuer.com/custom/", + err: nil, + }, + }, + { + "custom path no leading slash", + args{ + path: "custom/", + allowInsecure: false, + target: "https://issuer.com", + }, + res{ + issuer: "https://issuer.com/custom/", + err: nil, + }, + }, + { + "empty path unsecure", + args{ + path: "", + allowInsecure: true, + target: "http://issuer.com", + }, + res{ + issuer: "http://issuer.com", + err: nil, + }, + }, + { + "custom path unsecure", + args{ + path: "/custom/", + allowInsecure: true, + target: "http://issuer.com", + }, + res{ + issuer: "http://issuer.com/custom/", + err: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + issuer, err := IssuerFromHost(tt.args.path)(tt.args.allowInsecure) + if tt.res.err == nil { + assert.NoError(t, err) + req := httptest.NewRequest("", tt.args.target, nil) + assert.Equal(t, tt.res.issuer, issuer(req)) + } + if tt.res.err != nil { + assert.ErrorIs(t, err, tt.res.err) + } + }) + } +} + +func TestStaticIssuer(t *testing.T) { + type args struct { + issuer string + allowInsecure bool + } + type res struct { + issuer string + err error + } + tests := []struct { + name string + args args + res res + }{ + { + "invalid issuer", + args{ + issuer: "", + allowInsecure: false, + }, + res{ + issuer: "", + err: ErrInvalidIssuerNoIssuer, + }, + }, + { + "empty path secure", + args{ + issuer: "https://issuer.com", + allowInsecure: false, + }, + res{ + issuer: "https://issuer.com", + err: nil, + }, + }, + { + "custom path secure", + args{ + issuer: "https://issuer.com/custom/", + allowInsecure: false, + }, + res{ + issuer: "https://issuer.com/custom/", + err: nil, + }, + }, + { + "unsecure", + args{ + issuer: "http://issuer.com", + allowInsecure: true, + }, + res{ + issuer: "http://issuer.com", + err: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + issuer, err := StaticIssuer(tt.args.issuer)(tt.args.allowInsecure) + if tt.res.err == nil { + assert.NoError(t, err) + assert.Equal(t, tt.res.issuer, issuer(nil)) + } + if tt.res.err != nil { + assert.ErrorIs(t, err, tt.res.err) } }) } diff --git a/pkg/op/context.go b/pkg/op/context.go new file mode 100644 index 0000000..4406273 --- /dev/null +++ b/pkg/op/context.go @@ -0,0 +1,49 @@ +package op + +import ( + "context" + "net/http" +) + +type key int + +var ( + issuer key = 0 +) + +type IssuerInterceptor struct { + issuerFromRequest IssuerFromRequest +} + +//NewIssuerInterceptor will set the issuer into the context +//by the provided IssuerFromRequest (e.g. returned from StaticIssuer or IssuerFromHost) +func NewIssuerInterceptor(issuerFromRequest IssuerFromRequest) *IssuerInterceptor { + return &IssuerInterceptor{ + issuerFromRequest: issuerFromRequest, + } +} + +func (i *IssuerInterceptor) Handler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + i.setIssuerCtx(w, r, next) + }) +} + +func (i *IssuerInterceptor) HandlerFunc(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + i.setIssuerCtx(w, r, next) + } +} + +//IssuerFromContext reads the issuer from the context (set by an IssuerInterceptor) +//it will return an empty string if not found +func IssuerFromContext(ctx context.Context) string { + ctxIssuer, _ := ctx.Value(issuer).(string) + return ctxIssuer +} + +func (i *IssuerInterceptor) setIssuerCtx(w http.ResponseWriter, r *http.Request, next http.Handler) { + ctx := context.WithValue(r.Context(), issuer, i.issuerFromRequest(r)) + r = r.WithContext(ctx) + next.ServeHTTP(w, r) +} diff --git a/pkg/op/context_test.go b/pkg/op/context_test.go new file mode 100644 index 0000000..e6bfcec --- /dev/null +++ b/pkg/op/context_test.go @@ -0,0 +1,76 @@ +package op + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIssuerInterceptor(t *testing.T) { + type fields struct { + issuerFromRequest IssuerFromRequest + } + type args struct { + r *http.Request + next http.Handler + } + type res struct { + issuer string + } + tests := []struct { + name string + fields fields + args args + res res + }{ + { + "empty", + fields{ + func(r *http.Request) string { + return "" + }, + }, + args{}, + res{ + issuer: "", + }, + }, + { + "static", + fields{ + func(r *http.Request) string { + return "static" + }, + }, + args{}, + res{ + issuer: "static", + }, + }, + { + "host", + fields{ + func(r *http.Request) string { + return r.Host + }, + }, + args{}, + res{ + issuer: "issuer.com", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + i := NewIssuerInterceptor(tt.fields.issuerFromRequest) + next := http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { + assert.Equal(t, tt.res.issuer, IssuerFromContext(r.Context())) + }) + req := httptest.NewRequest("", "https://issuer.com", nil) + i.Handler(next).ServeHTTP(nil, req) + i.HandlerFunc(next).ServeHTTP(nil, req) + }) + } +} diff --git a/pkg/op/discovery.go b/pkg/op/discovery.go index 955d0fa..58b9948 100644 --- a/pkg/op/discovery.go +++ b/pkg/op/discovery.go @@ -1,49 +1,17 @@ package op import ( + "context" "net/http" + "gopkg.in/square/go-jose.v2" + httphelper "github.com/caos/oidc/pkg/http" "github.com/caos/oidc/pkg/oidc" ) -func discoveryHandler(c Configuration, s Signer) func(http.ResponseWriter, *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - Discover(w, CreateDiscoveryConfig(c, s)) - } -} - -func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) { - httphelper.MarshalJSON(w, config) -} - -func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfiguration { - return &oidc.DiscoveryConfiguration{ - Issuer: c.Issuer(), - AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()), - TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()), - IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(c.Issuer()), - UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()), - RevocationEndpoint: c.RevocationEndpoint().Absolute(c.Issuer()), - EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()), - JwksURI: c.KeysEndpoint().Absolute(c.Issuer()), - ScopesSupported: Scopes(c), - ResponseTypesSupported: ResponseTypes(c), - GrantTypesSupported: GrantTypes(c), - SubjectTypesSupported: SubjectTypes(c), - IDTokenSigningAlgValuesSupported: SigAlgorithms(s), - RequestObjectSigningAlgValuesSupported: RequestObjectSigAlgorithms(c), - TokenEndpointAuthMethodsSupported: AuthMethodsTokenEndpoint(c), - TokenEndpointAuthSigningAlgValuesSupported: TokenSigAlgorithms(c), - IntrospectionEndpointAuthSigningAlgValuesSupported: IntrospectionSigAlgorithms(c), - IntrospectionEndpointAuthMethodsSupported: AuthMethodsIntrospectionEndpoint(c), - RevocationEndpointAuthSigningAlgValuesSupported: RevocationSigAlgorithms(c), - RevocationEndpointAuthMethodsSupported: AuthMethodsRevocationEndpoint(c), - ClaimsSupported: SupportedClaims(c), - CodeChallengeMethodsSupported: CodeChallengeMethods(c), - UILocalesSupported: c.SupportedUILocales(), - RequestParameterSupported: c.RequestObjectSupported(), - } +type DiscoverStorage interface { + SignatureAlgorithms(context.Context) ([]jose.SignatureAlgorithm, error) } var DefaultSupportedScopes = []string{ @@ -55,6 +23,46 @@ var DefaultSupportedScopes = []string{ oidc.ScopeOfflineAccess, } +func discoveryHandler(c Configuration, s DiscoverStorage) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + Discover(w, CreateDiscoveryConfig(r, c, s)) + } +} + +func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) { + httphelper.MarshalJSON(w, config) +} + +func CreateDiscoveryConfig(r *http.Request, config Configuration, storage DiscoverStorage) *oidc.DiscoveryConfiguration { + issuer := config.IssuerFromRequest(r) + return &oidc.DiscoveryConfiguration{ + Issuer: issuer, + AuthorizationEndpoint: config.AuthorizationEndpoint().Absolute(issuer), + TokenEndpoint: config.TokenEndpoint().Absolute(issuer), + IntrospectionEndpoint: config.IntrospectionEndpoint().Absolute(issuer), + UserinfoEndpoint: config.UserinfoEndpoint().Absolute(issuer), + RevocationEndpoint: config.RevocationEndpoint().Absolute(issuer), + EndSessionEndpoint: config.EndSessionEndpoint().Absolute(issuer), + JwksURI: config.KeysEndpoint().Absolute(issuer), + ScopesSupported: Scopes(config), + ResponseTypesSupported: ResponseTypes(config), + GrantTypesSupported: GrantTypes(config), + SubjectTypesSupported: SubjectTypes(config), + IDTokenSigningAlgValuesSupported: SigAlgorithms(r.Context(), storage), + RequestObjectSigningAlgValuesSupported: RequestObjectSigAlgorithms(config), + TokenEndpointAuthMethodsSupported: AuthMethodsTokenEndpoint(config), + TokenEndpointAuthSigningAlgValuesSupported: TokenSigAlgorithms(config), + IntrospectionEndpointAuthSigningAlgValuesSupported: IntrospectionSigAlgorithms(config), + IntrospectionEndpointAuthMethodsSupported: AuthMethodsIntrospectionEndpoint(config), + RevocationEndpointAuthSigningAlgValuesSupported: RevocationSigAlgorithms(config), + RevocationEndpointAuthMethodsSupported: AuthMethodsRevocationEndpoint(config), + ClaimsSupported: SupportedClaims(config), + CodeChallengeMethodsSupported: CodeChallengeMethods(config), + UILocalesSupported: config.SupportedUILocales(), + RequestParameterSupported: config.RequestObjectSupported(), + } +} + func Scopes(c Configuration) []string { return DefaultSupportedScopes //TODO: config } @@ -84,6 +92,88 @@ func GrantTypes(c Configuration) []oidc.GrantType { return grantTypes } +func SubjectTypes(c Configuration) []string { + return []string{"public"} //TODO: config +} + +func SigAlgorithms(ctx context.Context, storage DiscoverStorage) []string { + algorithms, err := storage.SignatureAlgorithms(ctx) + if err != nil { + return nil + } + algs := make([]string, len(algorithms)) + for i, algorithm := range algorithms { + algs[i] = string(algorithm) + } + return algs +} + +func RequestObjectSigAlgorithms(c Configuration) []string { + if !c.RequestObjectSupported() { + return nil + } + return c.RequestObjectSigningAlgorithmsSupported() +} + +func AuthMethodsTokenEndpoint(c Configuration) []oidc.AuthMethod { + authMethods := []oidc.AuthMethod{ + oidc.AuthMethodNone, + oidc.AuthMethodBasic, + } + if c.AuthMethodPostSupported() { + authMethods = append(authMethods, oidc.AuthMethodPost) + } + if c.AuthMethodPrivateKeyJWTSupported() { + authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT) + } + return authMethods +} + +func TokenSigAlgorithms(c Configuration) []string { + if !c.AuthMethodPrivateKeyJWTSupported() { + return nil + } + return c.TokenEndpointSigningAlgorithmsSupported() +} + +func IntrospectionSigAlgorithms(c Configuration) []string { + if !c.IntrospectionAuthMethodPrivateKeyJWTSupported() { + return nil + } + return c.IntrospectionEndpointSigningAlgorithmsSupported() +} + +func AuthMethodsIntrospectionEndpoint(c Configuration) []oidc.AuthMethod { + authMethods := []oidc.AuthMethod{ + oidc.AuthMethodBasic, + } + if c.AuthMethodPrivateKeyJWTSupported() { + authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT) + } + return authMethods +} + +func RevocationSigAlgorithms(c Configuration) []string { + if !c.RevocationAuthMethodPrivateKeyJWTSupported() { + return nil + } + return c.RevocationEndpointSigningAlgorithmsSupported() +} + +func AuthMethodsRevocationEndpoint(c Configuration) []oidc.AuthMethod { + authMethods := []oidc.AuthMethod{ + oidc.AuthMethodNone, + oidc.AuthMethodBasic, + } + if c.AuthMethodPostSupported() { + authMethods = append(authMethods, oidc.AuthMethodPost) + } + if c.AuthMethodPrivateKeyJWTSupported() { + authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT) + } + return authMethods +} + func SupportedClaims(c Configuration) []string { return []string{ //TODO: config "sub", @@ -113,59 +203,6 @@ func SupportedClaims(c Configuration) []string { } } -func SigAlgorithms(s Signer) []string { - return []string{string(s.SignatureAlgorithm())} -} - -func SubjectTypes(c Configuration) []string { - return []string{"public"} //TODO: config -} - -func AuthMethodsTokenEndpoint(c Configuration) []oidc.AuthMethod { - authMethods := []oidc.AuthMethod{ - oidc.AuthMethodNone, - oidc.AuthMethodBasic, - } - if c.AuthMethodPostSupported() { - authMethods = append(authMethods, oidc.AuthMethodPost) - } - if c.AuthMethodPrivateKeyJWTSupported() { - authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT) - } - return authMethods -} - -func TokenSigAlgorithms(c Configuration) []string { - if !c.AuthMethodPrivateKeyJWTSupported() { - return nil - } - return c.TokenEndpointSigningAlgorithmsSupported() -} - -func AuthMethodsIntrospectionEndpoint(c Configuration) []oidc.AuthMethod { - authMethods := []oidc.AuthMethod{ - oidc.AuthMethodBasic, - } - if c.AuthMethodPrivateKeyJWTSupported() { - authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT) - } - return authMethods -} - -func AuthMethodsRevocationEndpoint(c Configuration) []oidc.AuthMethod { - authMethods := []oidc.AuthMethod{ - oidc.AuthMethodNone, - oidc.AuthMethodBasic, - } - if c.AuthMethodPostSupported() { - authMethods = append(authMethods, oidc.AuthMethodPost) - } - if c.AuthMethodPrivateKeyJWTSupported() { - authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT) - } - return authMethods -} - func CodeChallengeMethods(c Configuration) []oidc.CodeChallengeMethod { codeMethods := make([]oidc.CodeChallengeMethod, 0, 1) if c.CodeMethodS256Supported() { @@ -173,24 +210,3 @@ func CodeChallengeMethods(c Configuration) []oidc.CodeChallengeMethod { } return codeMethods } - -func IntrospectionSigAlgorithms(c Configuration) []string { - if !c.IntrospectionAuthMethodPrivateKeyJWTSupported() { - return nil - } - return c.IntrospectionEndpointSigningAlgorithmsSupported() -} - -func RevocationSigAlgorithms(c Configuration) []string { - if !c.RevocationAuthMethodPrivateKeyJWTSupported() { - return nil - } - return c.RevocationEndpointSigningAlgorithmsSupported() -} - -func RequestObjectSigAlgorithms(c Configuration) []string { - if !c.RequestObjectSupported() { - return nil - } - return c.RequestObjectSigningAlgorithmsSupported() -} diff --git a/pkg/op/discovery_test.go b/pkg/op/discovery_test.go index 1f0663d..953d21f 100644 --- a/pkg/op/discovery_test.go +++ b/pkg/op/discovery_test.go @@ -1,12 +1,13 @@ package op_test import ( + "context" "net/http" "net/http/httptest" - "reflect" "testing" "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gopkg.in/square/go-jose.v2" @@ -47,8 +48,9 @@ func TestDiscover(t *testing.T) { func TestCreateDiscoveryConfig(t *testing.T) { type args struct { - c op.Configuration - s op.Signer + request *http.Request + c op.Configuration + s op.DiscoverStorage } tests := []struct { name string @@ -59,9 +61,8 @@ func TestCreateDiscoveryConfig(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := op.CreateDiscoveryConfig(tt.args.c, tt.args.s); !reflect.DeepEqual(got, tt.want) { - t.Errorf("CreateDiscoveryConfig() = %v, want %v", got, tt.want) - } + got := op.CreateDiscoveryConfig(tt.args.request, tt.args.c, tt.args.s) + assert.Equal(t, tt.want, got) }) } } @@ -83,9 +84,8 @@ func Test_scopes(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := op.Scopes(tt.args.c); !reflect.DeepEqual(got, tt.want) { - t.Errorf("scopes() = %v, want %v", got, tt.want) - } + got := op.Scopes(tt.args.c) + assert.Equal(t, tt.want, got) }) } } @@ -99,13 +99,16 @@ func Test_ResponseTypes(t *testing.T) { args args want []string }{ - // TODO: Add test cases. + { + "code and implicit flow", + args{}, + []string{"code", "id_token", "id_token token"}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := op.ResponseTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { - t.Errorf("responseTypes() = %v, want %v", got, tt.want) - } + got := op.ResponseTypes(tt.args.c) + assert.Equal(t, tt.want, got) }) } } @@ -117,63 +120,48 @@ func Test_GrantTypes(t *testing.T) { tests := []struct { name string args args - want []string - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := op.GrantTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { - t.Errorf("grantTypes() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSupportedClaims(t *testing.T) { - type args struct { - c op.Configuration - } - tests := []struct { - name string - args args - want []string - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := op.SupportedClaims(tt.args.c); !reflect.DeepEqual(got, tt.want) { - t.Errorf("SupportedClaims() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_SigAlgorithms(t *testing.T) { - m := mock.NewMockSigner(gomock.NewController(t)) - type args struct { - s op.Signer - } - tests := []struct { - name string - args args - want []string + want []oidc.GrantType }{ { - "", - args{func() op.Signer { - m.EXPECT().SignatureAlgorithm().Return(jose.RS256) - return m - }()}, - []string{"RS256"}, + "code and implicit flow", + args{ + func() op.Configuration { + c := mock.NewMockConfiguration(gomock.NewController(t)) + c.EXPECT().GrantTypeRefreshTokenSupported().Return(false) + c.EXPECT().GrantTypeTokenExchangeSupported().Return(false) + c.EXPECT().GrantTypeJWTAuthorizationSupported().Return(false) + return c + }(), + }, + []oidc.GrantType{ + oidc.GrantTypeCode, + oidc.GrantTypeImplicit, + }, + }, + { + "code, implicit flow, refresh token, token exchange, jwt profile", + args{ + func() op.Configuration { + c := mock.NewMockConfiguration(gomock.NewController(t)) + c.EXPECT().GrantTypeRefreshTokenSupported().Return(true) + c.EXPECT().GrantTypeTokenExchangeSupported().Return(true) + c.EXPECT().GrantTypeJWTAuthorizationSupported().Return(true) + return c + }(), + }, + []oidc.GrantType{ + oidc.GrantTypeCode, + oidc.GrantTypeImplicit, + oidc.GrantTypeRefreshToken, + oidc.GrantTypeTokenExchange, + oidc.GrantTypeBearer, + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := op.SigAlgorithms(tt.args.s); !reflect.DeepEqual(got, tt.want) { - t.Errorf("sigAlgorithms() = %v, want %v", got, tt.want) - } + got := op.GrantTypes(tt.args.c) + assert.Equal(t, tt.want, got) }) } } @@ -195,9 +183,80 @@ func Test_SubjectTypes(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := op.SubjectTypes(tt.args.c); !reflect.DeepEqual(got, tt.want) { - t.Errorf("subjectTypes() = %v, want %v", got, tt.want) - } + got := op.SubjectTypes(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_SigAlgorithms(t *testing.T) { + m := mock.NewMockDiscoverStorage(gomock.NewController(t)) + type args struct { + s op.DiscoverStorage + } + tests := []struct { + name string + args args + want []string + }{ + { + "", + args{func() op.DiscoverStorage { + m.EXPECT().SignatureAlgorithms(gomock.Any()).Return([]jose.SignatureAlgorithm{jose.RS256}, nil) + return m + }()}, + []string{"RS256"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.SigAlgorithms(context.Background(), tt.args.s) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_RequestObjectSigAlgorithms(t *testing.T) { + m := mock.NewMockConfiguration(gomock.NewController(t)) + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []string + }{ + { + "not supported, empty", + args{func() op.Configuration { + m.EXPECT().RequestObjectSupported().Return(false) + return m + }()}, + nil, + }, + { + "supported, empty", + args{func() op.Configuration { + m.EXPECT().RequestObjectSupported().Return(true) + m.EXPECT().RequestObjectSigningAlgorithmsSupported().Return(nil) + return m + }()}, + nil, + }, + { + "supported, list", + args{func() op.Configuration { + m.EXPECT().RequestObjectSupported().Return(true) + m.EXPECT().RequestObjectSigningAlgorithmsSupported().Return([]string{"RS256"}) + return m + }()}, + []string{"RS256"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.RequestObjectSigAlgorithms(tt.args.c) + assert.Equal(t, tt.want, got) }) } } @@ -244,9 +303,311 @@ func Test_AuthMethodsTokenEndpoint(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := op.AuthMethodsTokenEndpoint(tt.args.c); !reflect.DeepEqual(got, tt.want) { - t.Errorf("authMethods() = %v, want %v", got, tt.want) - } + got := op.AuthMethodsTokenEndpoint(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_TokenSigAlgorithms(t *testing.T) { + m := mock.NewMockConfiguration(gomock.NewController(t)) + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []string + }{ + { + "not supported, empty", + args{func() op.Configuration { + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(false) + return m + }()}, + nil, + }, + { + "supported, empty", + args{func() op.Configuration { + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(true) + m.EXPECT().TokenEndpointSigningAlgorithmsSupported().Return(nil) + return m + }()}, + nil, + }, + { + "supported, list", + args{func() op.Configuration { + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(true) + m.EXPECT().TokenEndpointSigningAlgorithmsSupported().Return([]string{"RS256"}) + return m + }()}, + []string{"RS256"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.TokenSigAlgorithms(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_IntrospectionSigAlgorithms(t *testing.T) { + m := mock.NewMockConfiguration(gomock.NewController(t)) + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []string + }{ + { + "not supported, empty", + args{func() op.Configuration { + m.EXPECT().IntrospectionAuthMethodPrivateKeyJWTSupported().Return(false) + return m + }()}, + nil, + }, + { + "supported, empty", + args{func() op.Configuration { + m.EXPECT().IntrospectionAuthMethodPrivateKeyJWTSupported().Return(true) + m.EXPECT().IntrospectionEndpointSigningAlgorithmsSupported().Return(nil) + return m + }()}, + nil, + }, + { + "supported, list", + args{func() op.Configuration { + m.EXPECT().IntrospectionAuthMethodPrivateKeyJWTSupported().Return(true) + m.EXPECT().IntrospectionEndpointSigningAlgorithmsSupported().Return([]string{"RS256"}) + return m + }()}, + []string{"RS256"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.IntrospectionSigAlgorithms(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_AuthMethodsIntrospectionEndpoint(t *testing.T) { + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []oidc.AuthMethod + }{ + { + "basic only", + args{func() op.Configuration { + m := mock.NewMockConfiguration(gomock.NewController(t)) + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(false) + return m + }()}, + []oidc.AuthMethod{oidc.AuthMethodBasic}, + }, + { + "basic and private_key_jwt", + args{func() op.Configuration { + m := mock.NewMockConfiguration(gomock.NewController(t)) + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(true) + return m + }()}, + []oidc.AuthMethod{oidc.AuthMethodBasic, oidc.AuthMethodPrivateKeyJWT}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.AuthMethodsIntrospectionEndpoint(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_RevocationSigAlgorithms(t *testing.T) { + m := mock.NewMockConfiguration(gomock.NewController(t)) + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []string + }{ + { + "not supported, empty", + args{func() op.Configuration { + m.EXPECT().RevocationAuthMethodPrivateKeyJWTSupported().Return(false) + return m + }()}, + nil, + }, + { + "supported, empty", + args{func() op.Configuration { + m.EXPECT().RevocationAuthMethodPrivateKeyJWTSupported().Return(true) + m.EXPECT().RevocationEndpointSigningAlgorithmsSupported().Return(nil) + return m + }()}, + nil, + }, + { + "supported, list", + args{func() op.Configuration { + m.EXPECT().RevocationAuthMethodPrivateKeyJWTSupported().Return(true) + m.EXPECT().RevocationEndpointSigningAlgorithmsSupported().Return([]string{"RS256"}) + return m + }()}, + []string{"RS256"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.RevocationSigAlgorithms(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_AuthMethodsRevocationEndpoint(t *testing.T) { + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []oidc.AuthMethod + }{ + { + "none and basic", + args{func() op.Configuration { + m := mock.NewMockConfiguration(gomock.NewController(t)) + m.EXPECT().AuthMethodPostSupported().Return(false) + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(false) + return m + }()}, + []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic}, + }, + { + "none, basic and post", + args{func() op.Configuration { + m := mock.NewMockConfiguration(gomock.NewController(t)) + m.EXPECT().AuthMethodPostSupported().Return(true) + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(false) + return m + }()}, + []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost}, + }, + { + "none, basic, post and private_key_jwt", + args{func() op.Configuration { + m := mock.NewMockConfiguration(gomock.NewController(t)) + m.EXPECT().AuthMethodPostSupported().Return(true) + m.EXPECT().AuthMethodPrivateKeyJWTSupported().Return(true) + return m + }()}, + []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost, oidc.AuthMethodPrivateKeyJWT}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.AuthMethodsRevocationEndpoint(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestSupportedClaims(t *testing.T) { + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []string + }{ + { + "scopes", + args{}, + []string{ + "sub", + "aud", + "exp", + "iat", + "iss", + "auth_time", + "nonce", + "acr", + "amr", + "c_hash", + "at_hash", + "act", + "scopes", + "client_id", + "azp", + "preferred_username", + "name", + "family_name", + "given_name", + "locale", + "email", + "email_verified", + "phone_number", + "phone_number_verified", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.SupportedClaims(tt.args.c) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_CodeChallengeMethods(t *testing.T) { + type args struct { + c op.Configuration + } + tests := []struct { + name string + args args + want []oidc.CodeChallengeMethod + }{ + { + "not supported", + args{func() op.Configuration { + m := mock.NewMockConfiguration(gomock.NewController(t)) + m.EXPECT().CodeMethodS256Supported().Return(false) + return m + }()}, + []oidc.CodeChallengeMethod{}, + }, + { + "S256", + args{func() op.Configuration { + m := mock.NewMockConfiguration(gomock.NewController(t)) + m.EXPECT().CodeMethodS256Supported().Return(true) + return m + }()}, + []oidc.CodeChallengeMethod{oidc.CodeChallengeMethodS256}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := op.CodeChallengeMethods(tt.args.c) + assert.Equal(t, tt.want, got) }) } } diff --git a/pkg/op/keys.go b/pkg/op/keys.go index e637066..7490b5c 100644 --- a/pkg/op/keys.go +++ b/pkg/op/keys.go @@ -10,7 +10,7 @@ import ( ) type KeyProvider interface { - GetKeySet(context.Context) (*jose.JSONWebKeySet, error) + KeySet(context.Context) ([]Key, error) } func keysHandler(k KeyProvider) func(http.ResponseWriter, *http.Request) { @@ -20,10 +20,23 @@ func keysHandler(k KeyProvider) func(http.ResponseWriter, *http.Request) { } func Keys(w http.ResponseWriter, r *http.Request, k KeyProvider) { - keySet, err := k.GetKeySet(r.Context()) + keySet, err := k.KeySet(r.Context()) if err != nil { httphelper.MarshalJSONWithStatus(w, err, http.StatusInternalServerError) return } - httphelper.MarshalJSON(w, keySet) + httphelper.MarshalJSON(w, jsonWebKeySet(keySet)) +} + +func jsonWebKeySet(keys []Key) *jose.JSONWebKeySet { + webKeys := make([]jose.JSONWebKey, len(keys)) + for i, key := range keys { + webKeys[i] = jose.JSONWebKey{ + KeyID: key.ID(), + Algorithm: string(key.Algorithm()), + Use: key.Use(), + Key: key.Key(), + } + } + return &jose.JSONWebKeySet{Keys: webKeys} } diff --git a/pkg/op/keys_test.go b/pkg/op/keys_test.go index bf60a3e..e3592c5 100644 --- a/pkg/op/keys_test.go +++ b/pkg/op/keys_test.go @@ -35,7 +35,7 @@ func TestKeys(t *testing.T) { args: args{ k: func() op.KeyProvider { m := mock.NewMockKeyProvider(gomock.NewController(t)) - m.EXPECT().GetKeySet(gomock.Any()).Return(nil, oidc.ErrServerError()) + m.EXPECT().KeySet(gomock.Any()).Return(nil, oidc.ErrServerError()) return m }(), }, @@ -51,39 +51,39 @@ func TestKeys(t *testing.T) { args: args{ k: func() op.KeyProvider { m := mock.NewMockKeyProvider(gomock.NewController(t)) - m.EXPECT().GetKeySet(gomock.Any()).Return(nil, nil) + m.EXPECT().KeySet(gomock.Any()).Return(nil, nil) return m }(), }, res: res{ statusCode: http.StatusOK, contentType: "application/json", + body: `{"keys":[]} +`, }, }, { name: "list", args: args{ k: func() op.KeyProvider { - m := mock.NewMockKeyProvider(gomock.NewController(t)) - m.EXPECT().GetKeySet(gomock.Any()).Return( - &jose.JSONWebKeySet{Keys: []jose.JSONWebKey{ - { - Key: &rsa.PublicKey{ - N: big.NewInt(1), - E: 1, - }, - KeyID: "id", - }, - }}, - nil, - ) + ctrl := gomock.NewController(t) + m := mock.NewMockKeyProvider(ctrl) + k := mock.NewMockKey(ctrl) + k.EXPECT().Key().Return(&rsa.PublicKey{ + N: big.NewInt(1), + E: 1, + }) + k.EXPECT().ID().Return("id") + k.EXPECT().Algorithm().Return(jose.RS256) + k.EXPECT().Use().Return("sig") + m.EXPECT().KeySet(gomock.Any()).Return([]op.Key{k}, nil) return m }(), }, res: res{ statusCode: http.StatusOK, contentType: "application/json", - body: `{"keys":[{"kty":"RSA","kid":"id","n":"AQ","e":"AQ"}]} + body: `{"keys":[{"use":"sig","kty":"RSA","kid":"id","alg":"RS256","n":"AQ","e":"AQ"}]} `, }, }, diff --git a/pkg/op/mock/authorizer.mock.go b/pkg/op/mock/authorizer.mock.go index 3c18022..29b8aac 100644 --- a/pkg/op/mock/authorizer.mock.go +++ b/pkg/op/mock/authorizer.mock.go @@ -5,6 +5,7 @@ package mock import ( + context "context" reflect "reflect" http "github.com/caos/oidc/pkg/http" @@ -78,31 +79,17 @@ func (mr *MockAuthorizerMockRecorder) Encoder() *gomock.Call { } // IDTokenHintVerifier mocks base method. -func (m *MockAuthorizer) IDTokenHintVerifier() op.IDTokenHintVerifier { +func (m *MockAuthorizer) IDTokenHintVerifier(arg0 context.Context) op.IDTokenHintVerifier { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IDTokenHintVerifier") + ret := m.ctrl.Call(m, "IDTokenHintVerifier", arg0) ret0, _ := ret[0].(op.IDTokenHintVerifier) return ret0 } // IDTokenHintVerifier indicates an expected call of IDTokenHintVerifier. -func (mr *MockAuthorizerMockRecorder) IDTokenHintVerifier() *gomock.Call { +func (mr *MockAuthorizerMockRecorder) IDTokenHintVerifier(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDTokenHintVerifier", reflect.TypeOf((*MockAuthorizer)(nil).IDTokenHintVerifier)) -} - -// Issuer mocks base method. -func (m *MockAuthorizer) Issuer() string { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Issuer") - ret0, _ := ret[0].(string) - return ret0 -} - -// Issuer indicates an expected call of Issuer. -func (mr *MockAuthorizerMockRecorder) Issuer() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Issuer", reflect.TypeOf((*MockAuthorizer)(nil).Issuer)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IDTokenHintVerifier", reflect.TypeOf((*MockAuthorizer)(nil).IDTokenHintVerifier), arg0) } // RequestObjectSupported mocks base method. @@ -119,20 +106,6 @@ func (mr *MockAuthorizerMockRecorder) RequestObjectSupported() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RequestObjectSupported", reflect.TypeOf((*MockAuthorizer)(nil).RequestObjectSupported)) } -// Signer mocks base method. -func (m *MockAuthorizer) Signer() op.Signer { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Signer") - ret0, _ := ret[0].(op.Signer) - return ret0 -} - -// Signer indicates an expected call of Signer. -func (mr *MockAuthorizerMockRecorder) Signer() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Signer", reflect.TypeOf((*MockAuthorizer)(nil).Signer)) -} - // Storage mocks base method. func (m *MockAuthorizer) Storage() op.Storage { m.ctrl.T.Helper() diff --git a/pkg/op/mock/authorizer.mock.impl.go b/pkg/op/mock/authorizer.mock.impl.go index a481a8b..c1afdb5 100644 --- a/pkg/op/mock/authorizer.mock.impl.go +++ b/pkg/op/mock/authorizer.mock.impl.go @@ -20,7 +20,7 @@ func NewAuthorizerExpectValid(t *testing.T, wantErr bool) op.Authorizer { m := NewAuthorizer(t) ExpectDecoder(m) ExpectEncoder(m) - ExpectSigner(m, t) + //ExpectSigner(m, t) ExpectStorage(m, t) ExpectVerifier(m, t) // ExpectErrorHandler(m, t, wantErr) @@ -47,17 +47,18 @@ func ExpectEncoder(a op.Authorizer) { mockA.EXPECT().Encoder().AnyTimes().Return(schema.NewEncoder()) } -func ExpectSigner(a op.Authorizer, t *testing.T) { - mockA := a.(*MockAuthorizer) - mockA.EXPECT().Signer().DoAndReturn( - func() op.Signer { - return &Sig{} - }) -} +// +//func ExpectSigner(a op.Authorizer, t *testing.T) { +// mockA := a.(*MockAuthorizer) +// mockA.EXPECT().Signer().DoAndReturn( +// func() op.Signer { +// return &Sig{} +// }) +//} func ExpectVerifier(a op.Authorizer, t *testing.T) { mockA := a.(*MockAuthorizer) - mockA.EXPECT().IDTokenHintVerifier().DoAndReturn( + mockA.EXPECT().IDTokenHintVerifier(gomock.Any()).DoAndReturn( func() op.IDTokenHintVerifier { return op.NewIDTokenHintVerifier("", nil) }) diff --git a/pkg/op/mock/configuration.mock.go b/pkg/op/mock/configuration.mock.go index 3eb4542..a07dbab 100644 --- a/pkg/op/mock/configuration.mock.go +++ b/pkg/op/mock/configuration.mock.go @@ -5,6 +5,7 @@ package mock import ( + http "net/http" reflect "reflect" op "github.com/caos/oidc/pkg/op" @@ -147,6 +148,20 @@ func (mr *MockConfigurationMockRecorder) GrantTypeTokenExchangeSupported() *gomo return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantTypeTokenExchangeSupported", reflect.TypeOf((*MockConfiguration)(nil).GrantTypeTokenExchangeSupported)) } +// Insecure mocks base method. +func (m *MockConfiguration) Insecure() bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insecure") + ret0, _ := ret[0].(bool) + return ret0 +} + +// Insecure indicates an expected call of Insecure. +func (mr *MockConfigurationMockRecorder) Insecure() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insecure", reflect.TypeOf((*MockConfiguration)(nil).Insecure)) +} + // IntrospectionAuthMethodPrivateKeyJWTSupported mocks base method. func (m *MockConfiguration) IntrospectionAuthMethodPrivateKeyJWTSupported() bool { m.ctrl.T.Helper() @@ -189,18 +204,18 @@ func (mr *MockConfigurationMockRecorder) IntrospectionEndpointSigningAlgorithmsS return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IntrospectionEndpointSigningAlgorithmsSupported", reflect.TypeOf((*MockConfiguration)(nil).IntrospectionEndpointSigningAlgorithmsSupported)) } -// Issuer mocks base method. -func (m *MockConfiguration) Issuer() string { +// IssuerFromRequest mocks base method. +func (m *MockConfiguration) IssuerFromRequest(arg0 *http.Request) string { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Issuer") + ret := m.ctrl.Call(m, "IssuerFromRequest", arg0) ret0, _ := ret[0].(string) return ret0 } -// Issuer indicates an expected call of Issuer. -func (mr *MockConfigurationMockRecorder) Issuer() *gomock.Call { +// IssuerFromRequest indicates an expected call of IssuerFromRequest. +func (mr *MockConfigurationMockRecorder) IssuerFromRequest(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Issuer", reflect.TypeOf((*MockConfiguration)(nil).Issuer)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IssuerFromRequest", reflect.TypeOf((*MockConfiguration)(nil).IssuerFromRequest), arg0) } // KeysEndpoint mocks base method. diff --git a/pkg/op/mock/generate.go b/pkg/op/mock/generate.go index 4dd020e..956cc70 100644 --- a/pkg/op/mock/generate.go +++ b/pkg/op/mock/generate.go @@ -4,5 +4,6 @@ package mock //go:generate mockgen -package mock -destination ./authorizer.mock.go github.com/caos/oidc/pkg/op Authorizer //go:generate mockgen -package mock -destination ./client.mock.go github.com/caos/oidc/pkg/op Client //go:generate mockgen -package mock -destination ./configuration.mock.go github.com/caos/oidc/pkg/op Configuration -//go:generate mockgen -package mock -destination ./signer.mock.go github.com/caos/oidc/pkg/op Signer +//go:generate mockgen -package mock -destination ./discovery.mock.go github.com/caos/oidc/pkg/op DiscoverStorage +//go:generate mockgen -package mock -destination ./signer.mock.go github.com/caos/oidc/pkg/op SigningKey,Key //go:generate mockgen -package mock -destination ./key.mock.go github.com/caos/oidc/pkg/op KeyProvider diff --git a/pkg/op/mock/key.mock.go b/pkg/op/mock/key.mock.go index 37e0677..7d6afec 100644 --- a/pkg/op/mock/key.mock.go +++ b/pkg/op/mock/key.mock.go @@ -8,8 +8,8 @@ import ( context "context" reflect "reflect" + op "github.com/caos/oidc/pkg/op" gomock "github.com/golang/mock/gomock" - jose "gopkg.in/square/go-jose.v2" ) // MockKeyProvider is a mock of KeyProvider interface. @@ -35,17 +35,17 @@ func (m *MockKeyProvider) EXPECT() *MockKeyProviderMockRecorder { return m.recorder } -// GetKeySet mocks base method. -func (m *MockKeyProvider) GetKeySet(arg0 context.Context) (*jose.JSONWebKeySet, error) { +// KeySet mocks base method. +func (m *MockKeyProvider) KeySet(arg0 context.Context) ([]op.Key, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetKeySet", arg0) - ret0, _ := ret[0].(*jose.JSONWebKeySet) + ret := m.ctrl.Call(m, "KeySet", arg0) + ret0, _ := ret[0].([]op.Key) ret1, _ := ret[1].(error) return ret0, ret1 } -// GetKeySet indicates an expected call of GetKeySet. -func (mr *MockKeyProviderMockRecorder) GetKeySet(arg0 interface{}) *gomock.Call { +// KeySet indicates an expected call of KeySet. +func (mr *MockKeyProviderMockRecorder) KeySet(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKeySet", reflect.TypeOf((*MockKeyProvider)(nil).GetKeySet), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeySet", reflect.TypeOf((*MockKeyProvider)(nil).KeySet), arg0) } diff --git a/pkg/op/mock/signer.mock.go b/pkg/op/mock/signer.mock.go index 0564aa1..392a118 100644 --- a/pkg/op/mock/signer.mock.go +++ b/pkg/op/mock/signer.mock.go @@ -1,56 +1,55 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: Signer) +// Source: github.com/caos/oidc/pkg/op (interfaces: SigningKey,Key) // Package mock is a generated GoMock package. package mock import ( - context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" jose "gopkg.in/square/go-jose.v2" ) -// MockSigner is a mock of Signer interface. -type MockSigner struct { +// MockSigningKey is a mock of SigningKey interface. +type MockSigningKey struct { ctrl *gomock.Controller - recorder *MockSignerMockRecorder + recorder *MockSigningKeyMockRecorder } -// MockSignerMockRecorder is the mock recorder for MockSigner. -type MockSignerMockRecorder struct { - mock *MockSigner +// MockSigningKeyMockRecorder is the mock recorder for MockSigningKey. +type MockSigningKeyMockRecorder struct { + mock *MockSigningKey } -// NewMockSigner creates a new mock instance. -func NewMockSigner(ctrl *gomock.Controller) *MockSigner { - mock := &MockSigner{ctrl: ctrl} - mock.recorder = &MockSignerMockRecorder{mock} +// NewMockSigningKey creates a new mock instance. +func NewMockSigningKey(ctrl *gomock.Controller) *MockSigningKey { + mock := &MockSigningKey{ctrl: ctrl} + mock.recorder = &MockSigningKeyMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockSigner) EXPECT() *MockSignerMockRecorder { +func (m *MockSigningKey) EXPECT() *MockSigningKeyMockRecorder { return m.recorder } -// Health mocks base method. -func (m *MockSigner) Health(arg0 context.Context) error { +// Key mocks base method. +func (m *MockSigningKey) Key() interface{} { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Health", arg0) - ret0, _ := ret[0].(error) + ret := m.ctrl.Call(m, "Key") + ret0, _ := ret[0].(interface{}) return ret0 } -// Health indicates an expected call of Health. -func (mr *MockSignerMockRecorder) Health(arg0 interface{}) *gomock.Call { +// Key indicates an expected call of Key. +func (mr *MockSigningKeyMockRecorder) Key() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockSigner)(nil).Health), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Key", reflect.TypeOf((*MockSigningKey)(nil).Key)) } // SignatureAlgorithm mocks base method. -func (m *MockSigner) SignatureAlgorithm() jose.SignatureAlgorithm { +func (m *MockSigningKey) SignatureAlgorithm() jose.SignatureAlgorithm { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SignatureAlgorithm") ret0, _ := ret[0].(jose.SignatureAlgorithm) @@ -58,21 +57,86 @@ func (m *MockSigner) SignatureAlgorithm() jose.SignatureAlgorithm { } // SignatureAlgorithm indicates an expected call of SignatureAlgorithm. -func (mr *MockSignerMockRecorder) SignatureAlgorithm() *gomock.Call { +func (mr *MockSigningKeyMockRecorder) SignatureAlgorithm() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignatureAlgorithm", reflect.TypeOf((*MockSigner)(nil).SignatureAlgorithm)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignatureAlgorithm", reflect.TypeOf((*MockSigningKey)(nil).SignatureAlgorithm)) } -// Signer mocks base method. -func (m *MockSigner) Signer() jose.Signer { +// MockKey is a mock of Key interface. +type MockKey struct { + ctrl *gomock.Controller + recorder *MockKeyMockRecorder +} + +// MockKeyMockRecorder is the mock recorder for MockKey. +type MockKeyMockRecorder struct { + mock *MockKey +} + +// NewMockKey creates a new mock instance. +func NewMockKey(ctrl *gomock.Controller) *MockKey { + mock := &MockKey{ctrl: ctrl} + mock.recorder = &MockKeyMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockKey) EXPECT() *MockKeyMockRecorder { + return m.recorder +} + +// Algorithm mocks base method. +func (m *MockKey) Algorithm() jose.SignatureAlgorithm { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Signer") - ret0, _ := ret[0].(jose.Signer) + ret := m.ctrl.Call(m, "Algorithm") + ret0, _ := ret[0].(jose.SignatureAlgorithm) return ret0 } -// Signer indicates an expected call of Signer. -func (mr *MockSignerMockRecorder) Signer() *gomock.Call { +// Algorithm indicates an expected call of Algorithm. +func (mr *MockKeyMockRecorder) Algorithm() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Signer", reflect.TypeOf((*MockSigner)(nil).Signer)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Algorithm", reflect.TypeOf((*MockKey)(nil).Algorithm)) +} + +// ID mocks base method. +func (m *MockKey) ID() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ID") + ret0, _ := ret[0].(string) + return ret0 +} + +// ID indicates an expected call of ID. +func (mr *MockKeyMockRecorder) ID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockKey)(nil).ID)) +} + +// Key mocks base method. +func (m *MockKey) Key() interface{} { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Key") + ret0, _ := ret[0].(interface{}) + return ret0 +} + +// Key indicates an expected call of Key. +func (mr *MockKeyMockRecorder) Key() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Key", reflect.TypeOf((*MockKey)(nil).Key)) +} + +// Use mocks base method. +func (m *MockKey) Use() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Use") + ret0, _ := ret[0].(string) + return ret0 +} + +// Use indicates an expected call of Use. +func (mr *MockKeyMockRecorder) Use() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Use", reflect.TypeOf((*MockKey)(nil).Use)) } diff --git a/pkg/op/mock/storage.mock.go b/pkg/op/mock/storage.mock.go index 0763230..2dcc558 100644 --- a/pkg/op/mock/storage.mock.go +++ b/pkg/op/mock/storage.mock.go @@ -174,21 +174,6 @@ func (mr *MockStorageMockRecorder) GetKeyByIDAndUserID(arg0, arg1, arg2 interfac return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKeyByIDAndUserID", reflect.TypeOf((*MockStorage)(nil).GetKeyByIDAndUserID), arg0, arg1, arg2) } -// GetKeySet mocks base method. -func (m *MockStorage) GetKeySet(arg0 context.Context) (*jose.JSONWebKeySet, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetKeySet", arg0) - ret0, _ := ret[0].(*jose.JSONWebKeySet) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetKeySet indicates an expected call of GetKeySet. -func (mr *MockStorageMockRecorder) GetKeySet(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKeySet", reflect.TypeOf((*MockStorage)(nil).GetKeySet), arg0) -} - // GetPrivateClaimsFromScopes mocks base method. func (m *MockStorage) GetPrivateClaimsFromScopes(arg0 context.Context, arg1, arg2 string, arg3 []string) (map[string]interface{}, error) { m.ctrl.T.Helper() @@ -204,18 +189,6 @@ func (mr *MockStorageMockRecorder) GetPrivateClaimsFromScopes(arg0, arg1, arg2, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrivateClaimsFromScopes", reflect.TypeOf((*MockStorage)(nil).GetPrivateClaimsFromScopes), arg0, arg1, arg2, arg3) } -// GetSigningKey mocks base method. -func (m *MockStorage) GetSigningKey(arg0 context.Context, arg1 chan<- jose.SigningKey) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "GetSigningKey", arg0, arg1) -} - -// GetSigningKey indicates an expected call of GetSigningKey. -func (mr *MockStorageMockRecorder) GetSigningKey(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSigningKey", reflect.TypeOf((*MockStorage)(nil).GetSigningKey), arg0, arg1) -} - // Health mocks base method. func (m *MockStorage) Health(arg0 context.Context) error { m.ctrl.T.Helper() @@ -230,6 +203,21 @@ func (mr *MockStorageMockRecorder) Health(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Health", reflect.TypeOf((*MockStorage)(nil).Health), arg0) } +// KeySet mocks base method. +func (m *MockStorage) KeySet(arg0 context.Context) ([]op.Key, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "KeySet", arg0) + ret0, _ := ret[0].([]op.Key) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// KeySet indicates an expected call of KeySet. +func (mr *MockStorageMockRecorder) KeySet(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeySet", reflect.TypeOf((*MockStorage)(nil).KeySet), arg0) +} + // RevokeToken mocks base method. func (m *MockStorage) RevokeToken(arg0 context.Context, arg1, arg2, arg3 string) *oidc.Error { m.ctrl.T.Helper() @@ -300,6 +288,36 @@ func (mr *MockStorageMockRecorder) SetUserinfoFromToken(arg0, arg1, arg2, arg3, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUserinfoFromToken", reflect.TypeOf((*MockStorage)(nil).SetUserinfoFromToken), arg0, arg1, arg2, arg3, arg4) } +// SignatureAlgorithms mocks base method. +func (m *MockStorage) SignatureAlgorithms(arg0 context.Context) ([]jose.SignatureAlgorithm, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignatureAlgorithms", arg0) + ret0, _ := ret[0].([]jose.SignatureAlgorithm) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignatureAlgorithms indicates an expected call of SignatureAlgorithms. +func (mr *MockStorageMockRecorder) SignatureAlgorithms(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignatureAlgorithms", reflect.TypeOf((*MockStorage)(nil).SignatureAlgorithms), arg0) +} + +// SigningKey mocks base method. +func (m *MockStorage) SigningKey(arg0 context.Context) (op.SigningKey, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SigningKey", arg0) + ret0, _ := ret[0].(op.SigningKey) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SigningKey indicates an expected call of SigningKey. +func (mr *MockStorageMockRecorder) SigningKey(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SigningKey", reflect.TypeOf((*MockStorage)(nil).SigningKey), arg0) +} + // TerminateSession mocks base method. func (m *MockStorage) TerminateSession(arg0 context.Context, arg1, arg2 string) error { m.ctrl.T.Helper() diff --git a/pkg/op/mock/storage.mock.impl.go b/pkg/op/mock/storage.mock.impl.go index 4855cf5..b9f6681 100644 --- a/pkg/op/mock/storage.mock.impl.go +++ b/pkg/op/mock/storage.mock.impl.go @@ -3,11 +3,10 @@ package mock import ( "context" "errors" - "github.com/caos/oidc/pkg/oidc" "testing" "time" - "gopkg.in/square/go-jose.v2" + "github.com/caos/oidc/pkg/oidc" "github.com/golang/mock/gomock" @@ -40,12 +39,12 @@ func NewMockStorageAny(t *testing.T) op.Storage { func NewMockStorageSigningKeyInvalid(t *testing.T) op.Storage { m := NewStorage(t) - ExpectSigningKeyInvalid(m) + //ExpectSigningKeyInvalid(m) return m } func NewMockStorageSigningKey(t *testing.T) op.Storage { m := NewStorage(t) - ExpectSigningKey(m) + //ExpectSigningKey(m) return m } @@ -83,23 +82,24 @@ func ExpectValidClientID(s op.Storage) { }) } -func ExpectSigningKeyInvalid(s op.Storage) { - mockS := s.(*MockStorage) - mockS.EXPECT().GetSigningKey(gomock.Any(), gomock.Any()).DoAndReturn( - func(_ context.Context, keyCh chan<- jose.SigningKey) { - keyCh <- jose.SigningKey{} - }, - ) -} - -func ExpectSigningKey(s op.Storage) { - mockS := s.(*MockStorage) - mockS.EXPECT().GetSigningKey(gomock.Any(), gomock.Any()).DoAndReturn( - func(_ context.Context, keyCh chan<- jose.SigningKey) { - keyCh <- jose.SigningKey{Algorithm: jose.HS256, Key: []byte("key")} - }, - ) -} +// +//func ExpectSigningKeyInvalid(s op.Storage) { +// mockS := s.(*MockStorage) +// mockS.EXPECT().GetSigningKey(gomock.Any(), gomock.Any()).DoAndReturn( +// func(_ context.Context, keyCh chan<- jose.SigningKey) { +// keyCh <- jose.SigningKey{} +// }, +// ) +//} +// +//func ExpectSigningKey(s op.Storage) { +// mockS := s.(*MockStorage) +// mockS.EXPECT().GetSigningKey(gomock.Any(), gomock.Any()).DoAndReturn( +// func(_ context.Context, keyCh chan<- jose.SigningKey) { +// keyCh <- jose.SigningKey{Algorithm: jose.HS256, Key: []byte("key")} +// }, +// ) +//} type ConfClient struct { id string diff --git a/pkg/op/op.go b/pkg/op/op.go index e910bf6..393f56a 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -46,11 +46,10 @@ type OpenIDProvider interface { Storage() Storage Decoder() httphelper.Decoder Encoder() httphelper.Encoder - IDTokenHintVerifier() IDTokenHintVerifier - AccessTokenVerifier() AccessTokenVerifier + IDTokenHintVerifier(context.Context) IDTokenHintVerifier + AccessTokenVerifier(context.Context) AccessTokenVerifier Crypto() Crypto DefaultLogoutRedirectURI() string - Signer() Signer Probes() []ProbesFn HttpHandler() http.Handler } @@ -62,31 +61,26 @@ var allowAllOrigins = func(_ string) bool { } func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router { - intercept := buildInterceptor(interceptors...) router := mux.NewRouter() - router.Use(handlers.CORS( - handlers.AllowCredentials(), - handlers.AllowedHeaders([]string{"authorization", "content-type"}), - handlers.AllowedOriginValidator(allowAllOrigins), - )) + router.Use(intercept(o.IssuerFromRequest, interceptors...)) router.HandleFunc(healthEndpoint, healthHandler) router.HandleFunc(readinessEndpoint, readyHandler(o.Probes())) - router.HandleFunc(oidc.DiscoveryEndpoint, discoveryHandler(o, o.Signer())) - router.Handle(o.AuthorizationEndpoint().Relative(), intercept(authorizeHandler(o))) - router.NewRoute().Path(authCallbackPath(o)).Queries("id", "{id}").Handler(intercept(authorizeCallbackHandler(o))) - router.Handle(o.TokenEndpoint().Relative(), intercept(tokenHandler(o))) + router.HandleFunc(oidc.DiscoveryEndpoint, discoveryHandler(o, o.Storage())) + router.HandleFunc(o.AuthorizationEndpoint().Relative(), authorizeHandler(o)) + router.NewRoute().Path(authCallbackPath(o)).Queries("id", "{id}").HandlerFunc(authorizeCallbackHandler(o)) + router.HandleFunc(o.TokenEndpoint().Relative(), tokenHandler(o)) router.HandleFunc(o.IntrospectionEndpoint().Relative(), introspectionHandler(o)) router.HandleFunc(o.UserinfoEndpoint().Relative(), userinfoHandler(o)) router.HandleFunc(o.RevocationEndpoint().Relative(), revocationHandler(o)) - router.Handle(o.EndSessionEndpoint().Relative(), intercept(endSessionHandler(o))) + router.HandleFunc(o.EndSessionEndpoint().Relative(), endSessionHandler(o)) router.HandleFunc(o.KeysEndpoint().Relative(), keysHandler(o.Storage())) return router } //AuthCallbackURL builds the url for the redirect (with the requestID) after a successful login -func AuthCallbackURL(o OpenIDProvider) func(string) string { - return func(requestID string) string { - return o.AuthorizationEndpoint().Absolute(o.Issuer()) + authCallbackPathSuffix + "?id=" + requestID +func AuthCallbackURL(o OpenIDProvider) func(context.Context, string) string { + return func(ctx context.Context, requestID string) string { + return o.AuthorizationEndpoint().Absolute(IssuerFromContext(ctx)) + authCallbackPathSuffix + "?id=" + requestID } } @@ -95,7 +89,6 @@ func authCallbackPath(o OpenIDProvider) string { } type Config struct { - Issuer string CryptoKey [32]byte DefaultLogoutRedirectURI string CodeMethodS256 bool @@ -117,13 +110,16 @@ type endpoints struct { JwksURI Endpoint } -func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opOpts ...Option) (OpenIDProvider, error) { - err := ValidateIssuer(config.Issuer) - if err != nil { - return nil, err - } +func NewOpenIDProvider(ctx context.Context, issuer string, config *Config, storage Storage, opOpts ...Option) (*Provider, error) { + return newProvider(ctx, config, storage, StaticIssuer(issuer), opOpts...) +} - o := &openidProvider{ +func NewDynamicOpenIDProvider(ctx context.Context, path string, config *Config, storage Storage, opOpts ...Option) (*Provider, error) { + return newProvider(ctx, config, storage, IssuerFromHost(path), opOpts...) +} + +func newProvider(ctx context.Context, config *Config, storage Storage, issuer func(bool) (IssuerFromRequest, error), opOpts ...Option) (_ *Provider, err error) { + o := &Provider{ config: config, storage: storage, endpoints: DefaultEndpoints, @@ -136,9 +132,10 @@ func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opO } } - keyCh := make(chan jose.SigningKey) - go storage.GetSigningKey(ctx, keyCh) - o.signer = NewSigner(ctx, storage, keyCh) + o.issuer, err = issuer(o.insecure) + if err != nil { + return nil, err + } o.httpHandler = CreateRouter(o, o.interceptors...) @@ -152,171 +149,159 @@ func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opO return o, nil } -type openidProvider struct { - config *Config - endpoints *endpoints - storage Storage - signer Signer - idTokenHintVerifier IDTokenHintVerifier - jwtProfileVerifier JWTProfileVerifier - accessTokenVerifier AccessTokenVerifier - keySet *openIDKeySet - crypto Crypto - httpHandler http.Handler - decoder *schema.Decoder - encoder *schema.Encoder - interceptors []HttpInterceptor - timer <-chan time.Time +type Provider struct { + config *Config + issuer IssuerFromRequest + insecure bool + endpoints *endpoints + storage Storage + keySet *openIDKeySet + crypto Crypto + httpHandler http.Handler + decoder *schema.Decoder + encoder *schema.Encoder + interceptors []HttpInterceptor + timer <-chan time.Time } -func (o *openidProvider) Issuer() string { - return o.config.Issuer +func (o *Provider) IssuerFromRequest(r *http.Request) string { + return o.issuer(r) } -func (o *openidProvider) AuthorizationEndpoint() Endpoint { +func (o *Provider) Insecure() bool { + return o.insecure +} + +func (o *Provider) AuthorizationEndpoint() Endpoint { return o.endpoints.Authorization } -func (o *openidProvider) TokenEndpoint() Endpoint { +func (o *Provider) TokenEndpoint() Endpoint { return o.endpoints.Token } -func (o *openidProvider) IntrospectionEndpoint() Endpoint { +func (o *Provider) IntrospectionEndpoint() Endpoint { return o.endpoints.Introspection } -func (o *openidProvider) UserinfoEndpoint() Endpoint { +func (o *Provider) UserinfoEndpoint() Endpoint { return o.endpoints.Userinfo } -func (o *openidProvider) RevocationEndpoint() Endpoint { +func (o *Provider) RevocationEndpoint() Endpoint { return o.endpoints.Revocation } -func (o *openidProvider) EndSessionEndpoint() Endpoint { +func (o *Provider) EndSessionEndpoint() Endpoint { return o.endpoints.EndSession } -func (o *openidProvider) KeysEndpoint() Endpoint { +func (o *Provider) KeysEndpoint() Endpoint { return o.endpoints.JwksURI } -func (o *openidProvider) AuthMethodPostSupported() bool { +func (o *Provider) AuthMethodPostSupported() bool { return o.config.AuthMethodPost } -func (o *openidProvider) CodeMethodS256Supported() bool { +func (o *Provider) CodeMethodS256Supported() bool { return o.config.CodeMethodS256 } -func (o *openidProvider) AuthMethodPrivateKeyJWTSupported() bool { +func (o *Provider) AuthMethodPrivateKeyJWTSupported() bool { return o.config.AuthMethodPrivateKeyJWT } -func (o *openidProvider) TokenEndpointSigningAlgorithmsSupported() []string { +func (o *Provider) TokenEndpointSigningAlgorithmsSupported() []string { return []string{"RS256"} } -func (o *openidProvider) GrantTypeRefreshTokenSupported() bool { +func (o *Provider) GrantTypeRefreshTokenSupported() bool { return o.config.GrantTypeRefreshToken } -func (o *openidProvider) GrantTypeTokenExchangeSupported() bool { +func (o *Provider) GrantTypeTokenExchangeSupported() bool { return false } -func (o *openidProvider) GrantTypeJWTAuthorizationSupported() bool { +func (o *Provider) GrantTypeJWTAuthorizationSupported() bool { return true } -func (o *openidProvider) IntrospectionAuthMethodPrivateKeyJWTSupported() bool { +func (o *Provider) IntrospectionAuthMethodPrivateKeyJWTSupported() bool { return true } -func (o *openidProvider) IntrospectionEndpointSigningAlgorithmsSupported() []string { +func (o *Provider) IntrospectionEndpointSigningAlgorithmsSupported() []string { return []string{"RS256"} } -func (o *openidProvider) RevocationAuthMethodPrivateKeyJWTSupported() bool { +func (o *Provider) RevocationAuthMethodPrivateKeyJWTSupported() bool { return true } -func (o *openidProvider) RevocationEndpointSigningAlgorithmsSupported() []string { +func (o *Provider) RevocationEndpointSigningAlgorithmsSupported() []string { return []string{"RS256"} } -func (o *openidProvider) RequestObjectSupported() bool { +func (o *Provider) RequestObjectSupported() bool { return o.config.RequestObjectSupported } -func (o *openidProvider) RequestObjectSigningAlgorithmsSupported() []string { +func (o *Provider) RequestObjectSigningAlgorithmsSupported() []string { return []string{"RS256"} } -func (o *openidProvider) SupportedUILocales() []language.Tag { +func (o *Provider) SupportedUILocales() []language.Tag { return o.config.SupportedUILocales } -func (o *openidProvider) Storage() Storage { +func (o *Provider) Storage() Storage { return o.storage } -func (o *openidProvider) Decoder() httphelper.Decoder { +func (o *Provider) Decoder() httphelper.Decoder { return o.decoder } -func (o *openidProvider) Encoder() httphelper.Encoder { +func (o *Provider) Encoder() httphelper.Encoder { return o.encoder } -func (o *openidProvider) IDTokenHintVerifier() IDTokenHintVerifier { - if o.idTokenHintVerifier == nil { - o.idTokenHintVerifier = NewIDTokenHintVerifier(o.Issuer(), o.openIDKeySet()) - } - return o.idTokenHintVerifier +func (o *Provider) IDTokenHintVerifier(ctx context.Context) IDTokenHintVerifier { + return NewIDTokenHintVerifier(IssuerFromContext(ctx), o.openIDKeySet()) } -func (o *openidProvider) JWTProfileVerifier() JWTProfileVerifier { - if o.jwtProfileVerifier == nil { - o.jwtProfileVerifier = NewJWTProfileVerifier(o.Storage(), o.Issuer(), 1*time.Hour, time.Second) - } - return o.jwtProfileVerifier +func (o *Provider) JWTProfileVerifier(ctx context.Context) JWTProfileVerifier { + return NewJWTProfileVerifier(o.Storage(), IssuerFromContext(ctx), 1*time.Hour, time.Second) } -func (o *openidProvider) AccessTokenVerifier() AccessTokenVerifier { - if o.accessTokenVerifier == nil { - o.accessTokenVerifier = NewAccessTokenVerifier(o.Issuer(), o.openIDKeySet()) - } - return o.accessTokenVerifier +func (o *Provider) AccessTokenVerifier(ctx context.Context) AccessTokenVerifier { + return NewAccessTokenVerifier(IssuerFromContext(ctx), o.openIDKeySet()) } -func (o *openidProvider) openIDKeySet() oidc.KeySet { +func (o *Provider) openIDKeySet() oidc.KeySet { if o.keySet == nil { o.keySet = &openIDKeySet{o.Storage()} } return o.keySet } -func (o *openidProvider) Crypto() Crypto { +func (o *Provider) Crypto() Crypto { return o.crypto } -func (o *openidProvider) DefaultLogoutRedirectURI() string { +func (o *Provider) DefaultLogoutRedirectURI() string { return o.config.DefaultLogoutRedirectURI } -func (o *openidProvider) Signer() Signer { - return o.signer -} - -func (o *openidProvider) Probes() []ProbesFn { +func (o *Provider) Probes() []ProbesFn { return []ProbesFn{ - ReadySigner(o.Signer()), ReadyStorage(o.Storage()), } } -func (o *openidProvider) HttpHandler() http.Handler { +func (o *Provider) HttpHandler() http.Handler { return o.httpHandler } @@ -327,22 +312,31 @@ type openIDKeySet struct { //VerifySignature implements the oidc.KeySet interface //providing an implementation for the keys stored in the OP Storage interface func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { - keySet, err := o.Storage.GetKeySet(ctx) + keySet, err := o.Storage.KeySet(ctx) if err != nil { return nil, fmt.Errorf("error fetching keys: %w", err) } keyID, alg := oidc.GetKeyIDAndAlg(jws) - key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keySet.Keys...) + key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, jsonWebKeySet(keySet).Keys...) if err != nil { return nil, fmt.Errorf("invalid signature: %w", err) } return jws.Verify(&key) } -type Option func(o *openidProvider) error +type Option func(o *Provider) error + +//WithAllowInsecure allows the use of http (instead of https) for issuers +//this is not recommended for production use and violates the OIDC specification +func WithAllowInsecure() Option { + return func(o *Provider) error { + o.insecure = true + return nil + } +} func WithCustomAuthEndpoint(endpoint Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { if err := endpoint.Validate(); err != nil { return err } @@ -352,7 +346,7 @@ func WithCustomAuthEndpoint(endpoint Endpoint) Option { } func WithCustomTokenEndpoint(endpoint Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { if err := endpoint.Validate(); err != nil { return err } @@ -362,7 +356,7 @@ func WithCustomTokenEndpoint(endpoint Endpoint) Option { } func WithCustomIntrospectionEndpoint(endpoint Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { if err := endpoint.Validate(); err != nil { return err } @@ -372,7 +366,7 @@ func WithCustomIntrospectionEndpoint(endpoint Endpoint) Option { } func WithCustomUserinfoEndpoint(endpoint Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { if err := endpoint.Validate(); err != nil { return err } @@ -382,7 +376,7 @@ func WithCustomUserinfoEndpoint(endpoint Endpoint) Option { } func WithCustomRevocationEndpoint(endpoint Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { if err := endpoint.Validate(); err != nil { return err } @@ -392,7 +386,7 @@ func WithCustomRevocationEndpoint(endpoint Endpoint) Option { } func WithCustomEndSessionEndpoint(endpoint Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { if err := endpoint.Validate(); err != nil { return err } @@ -402,7 +396,7 @@ func WithCustomEndSessionEndpoint(endpoint Endpoint) Option { } func WithCustomKeysEndpoint(endpoint Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { if err := endpoint.Validate(); err != nil { return err } @@ -412,7 +406,7 @@ func WithCustomKeysEndpoint(endpoint Endpoint) Option { } func WithCustomEndpoints(auth, token, userInfo, revocation, endSession, keys Endpoint) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { o.endpoints.Authorization = auth o.endpoints.Token = token o.endpoints.Userinfo = userInfo @@ -424,24 +418,23 @@ func WithCustomEndpoints(auth, token, userInfo, revocation, endSession, keys End } func WithHttpInterceptors(interceptors ...HttpInterceptor) Option { - return func(o *openidProvider) error { + return func(o *Provider) error { o.interceptors = append(o.interceptors, interceptors...) return nil } } -func buildInterceptor(interceptors ...HttpInterceptor) func(http.HandlerFunc) http.Handler { - return func(handlerFunc http.HandlerFunc) http.Handler { - handler := handlerFuncToHandler(handlerFunc) +func intercept(i IssuerFromRequest, interceptors ...HttpInterceptor) func(handler http.Handler) http.Handler { + cors := handlers.CORS( + handlers.AllowCredentials(), + handlers.AllowedHeaders([]string{"authorization", "content-type"}), + handlers.AllowedOriginValidator(allowAllOrigins), + ) + issuerInterceptor := NewIssuerInterceptor(i) + return func(handler http.Handler) http.Handler { for i := len(interceptors) - 1; i >= 0; i-- { handler = interceptors[i](handler) } - return handler + return cors(issuerInterceptor.Handler(handler)) } } - -func handlerFuncToHandler(handlerFunc http.HandlerFunc) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - handlerFunc(w, r) - }) -} diff --git a/pkg/op/probes.go b/pkg/op/probes.go index b6fdde2..ee83447 100644 --- a/pkg/op/probes.go +++ b/pkg/op/probes.go @@ -31,14 +31,6 @@ func Readiness(w http.ResponseWriter, r *http.Request, probes ...ProbesFn) { ok(w) } -func ReadySigner(s Signer) ProbesFn { - return func(ctx context.Context) error { - if s == nil { - return errors.New("no signer") - } - return s.Health(ctx) - } -} func ReadyStorage(s Storage) ProbesFn { return func(ctx context.Context) error { if s == nil { diff --git a/pkg/op/session.go b/pkg/op/session.go index 1f9290e..d064387 100644 --- a/pkg/op/session.go +++ b/pkg/op/session.go @@ -11,7 +11,7 @@ import ( type SessionEnder interface { Decoder() httphelper.Decoder Storage() Storage - IDTokenHintVerifier() IDTokenHintVerifier + IDTokenHintVerifier(context.Context) IDTokenHintVerifier DefaultLogoutRedirectURI() string } @@ -62,7 +62,7 @@ func ValidateEndSessionRequest(ctx context.Context, req *oidc.EndSessionRequest, if req.IdTokenHint == "" { return session, nil } - claims, err := VerifyIDTokenHint(ctx, req.IdTokenHint, ender.IDTokenHintVerifier()) + claims, err := VerifyIDTokenHint(ctx, req.IdTokenHint, ender.IDTokenHintVerifier(ctx)) if err != nil { return nil, oidc.ErrInvalidRequest().WithDescription("id_token_hint invalid").WithParent(err) } diff --git a/pkg/op/signer.go b/pkg/op/signer.go index 0dabf67..22ef8ca 100644 --- a/pkg/op/signer.go +++ b/pkg/op/signer.go @@ -1,82 +1,38 @@ package op import ( - "context" "errors" - "github.com/caos/logging" "gopkg.in/square/go-jose.v2" ) -type Signer interface { - Health(ctx context.Context) error - Signer() jose.Signer +var ( + ErrSignerCreationFailed = errors.New("signer creation failed") +) + +type SigningKey interface { SignatureAlgorithm() jose.SignatureAlgorithm + Key() interface{} + ID() string } -type tokenSigner struct { - signer jose.Signer - storage AuthStorage - alg jose.SignatureAlgorithm -} - -func NewSigner(ctx context.Context, storage AuthStorage, keyCh <-chan jose.SigningKey) Signer { - s := &tokenSigner{ - storage: storage, - } - - select { - case <-ctx.Done(): - return nil - case key := <-keyCh: - s.exchangeSigningKey(key) - } - go s.refreshSigningKey(ctx, keyCh) - - return s -} - -func (s *tokenSigner) Health(_ context.Context) error { - if s.signer == nil { - return errors.New("no signer") - } - if string(s.alg) == "" { - return errors.New("no signing algorithm") - } - return nil -} - -func (s *tokenSigner) Signer() jose.Signer { - return s.signer -} - -func (s *tokenSigner) refreshSigningKey(ctx context.Context, keyCh <-chan jose.SigningKey) { - for { - select { - case <-ctx.Done(): - return - case key := <-keyCh: - s.exchangeSigningKey(key) - } - } -} - -func (s *tokenSigner) exchangeSigningKey(key jose.SigningKey) { - s.alg = key.Algorithm - if key.Algorithm == "" || key.Key == nil { - s.signer = nil - logging.Warn("signer has no key") - return - } - var err error - s.signer, err = jose.NewSigner(key, &jose.SignerOptions{}) +func SignerFromKey(key SigningKey) (jose.Signer, error) { + signer, err := jose.NewSigner(jose.SigningKey{ + Algorithm: key.SignatureAlgorithm(), + Key: &jose.JSONWebKey{ + Key: key.Key(), + KeyID: key.ID(), + }, + }, &jose.SignerOptions{}) if err != nil { - logging.New().WithError(err).Error("error creating signer") - return + return nil, ErrSignerCreationFailed //TODO: log / wrap error? } - logging.Info("signer exchanged signing key") + return signer, nil } -func (s *tokenSigner) SignatureAlgorithm() jose.SignatureAlgorithm { - return s.alg +type Key interface { + ID() string + Algorithm() jose.SignatureAlgorithm + Use() string + Key() interface{} } diff --git a/pkg/op/storage.go b/pkg/op/storage.go index 94c2a33..f07ab37 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -23,8 +23,9 @@ type AuthStorage interface { TerminateSession(ctx context.Context, userID string, clientID string) error RevokeToken(ctx context.Context, token string, userID string, clientID string) *oidc.Error - GetSigningKey(context.Context, chan<- jose.SigningKey) - GetKeySet(context.Context) (*jose.JSONWebKeySet, error) + SigningKey(context.Context) (SigningKey, error) + SignatureAlgorithms(context.Context) ([]jose.SignatureAlgorithm, error) + KeySet(context.Context) ([]Key, error) } type OPStorage interface { diff --git a/pkg/op/token.go b/pkg/op/token.go index 3e97360..e2ce00f 100644 --- a/pkg/op/token.go +++ b/pkg/op/token.go @@ -10,8 +10,6 @@ import ( ) type TokenCreator interface { - Issuer() string - Signer() Signer Storage() Storage Crypto() Crypto } @@ -32,7 +30,7 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli return nil, err } } - idToken, err := CreateIDToken(ctx, creator.Issuer(), request, client.IDTokenLifetime(), accessToken, code, creator.Storage(), creator.Signer(), client) + idToken, err := CreateIDToken(ctx, IssuerFromContext(ctx), request, client.IDTokenLifetime(), accessToken, code, creator.Storage(), client) if err != nil { return nil, err } @@ -84,7 +82,7 @@ func CreateAccessToken(ctx context.Context, tokenRequest TokenRequest, accessTok } validity = exp.Add(clockSkew).Sub(time.Now().UTC()) if accessTokenType == AccessTokenTypeJWT { - accessToken, err = CreateJWT(ctx, creator.Issuer(), tokenRequest, exp, id, creator.Signer(), client, creator.Storage()) + accessToken, err = CreateJWT(ctx, IssuerFromContext(ctx), tokenRequest, exp, id, client, creator.Storage()) return } accessToken, err = CreateBearerToken(id, tokenRequest.GetSubject(), creator.Crypto()) @@ -95,7 +93,7 @@ func CreateBearerToken(tokenID, subject string, crypto Crypto) (string, error) { return crypto.Encrypt(tokenID + ":" + subject) } -func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, exp time.Time, id string, signer Signer, client Client, storage Storage) (string, error) { +func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, exp time.Time, id string, client Client, storage Storage) (string, error) { claims := oidc.NewAccessTokenClaims(issuer, tokenRequest.GetSubject(), tokenRequest.GetAudience(), exp, id, client.GetID(), client.ClockSkew()) if client != nil { restrictedScopes := client.RestrictAdditionalAccessTokenScopes()(tokenRequest.GetScopes()) @@ -105,7 +103,15 @@ func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, ex } claims.SetPrivateClaims(privateClaims) } - return crypto.Sign(claims, signer.Signer()) + signingKey, err := storage.SigningKey(ctx) + if err != nil { + return "", err + } + signer, err := SignerFromKey(signingKey) + if err != nil { + return "", err + } + return crypto.Sign(claims, signer) } type IDTokenRequest interface { @@ -117,7 +123,7 @@ type IDTokenRequest interface { GetSubject() string } -func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, validity time.Duration, accessToken, code string, storage Storage, signer Signer, client Client) (string, error) { +func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, validity time.Duration, accessToken, code string, storage Storage, client Client) (string, error) { exp := time.Now().UTC().Add(client.ClockSkew()).Add(validity) var acr, nonce string if authRequest, ok := request.(AuthRequest); ok { @@ -126,8 +132,12 @@ func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, v } claims := oidc.NewIDTokenClaims(issuer, request.GetSubject(), request.GetAudience(), exp, request.GetAuthTime(), nonce, acr, request.GetAMR(), request.GetClientID(), client.ClockSkew()) scopes := client.RestrictAdditionalIdTokenScopes()(request.GetScopes()) + signingKey, err := storage.SigningKey(ctx) + if err != nil { + return "", err + } if accessToken != "" { - atHash, err := oidc.ClaimHash(accessToken, signer.SignatureAlgorithm()) + atHash, err := oidc.ClaimHash(accessToken, signingKey.SignatureAlgorithm()) if err != nil { return "", err } @@ -145,14 +155,17 @@ func CreateIDToken(ctx context.Context, issuer string, request IDTokenRequest, v claims.SetUserinfo(userInfo) } if code != "" { - codeHash, err := oidc.ClaimHash(code, signer.SignatureAlgorithm()) + codeHash, err := oidc.ClaimHash(code, signingKey.SignatureAlgorithm()) if err != nil { return "", err } claims.SetCodeHash(codeHash) } - - return crypto.Sign(claims, signer.Signer()) + signer, err := SignerFromKey(signingKey) + if err != nil { + return "", err + } + return crypto.Sign(claims, signer) } func removeUserinfoScopes(scopes []string) []string { diff --git a/pkg/op/token_intospection.go b/pkg/op/token_intospection.go index 8fd9187..6076e20 100644 --- a/pkg/op/token_intospection.go +++ b/pkg/op/token_intospection.go @@ -1,6 +1,7 @@ package op import ( + "context" "errors" "net/http" "net/url" @@ -13,12 +14,12 @@ type Introspector interface { Decoder() httphelper.Decoder Crypto() Crypto Storage() Storage - AccessTokenVerifier() AccessTokenVerifier + AccessTokenVerifier(context.Context) AccessTokenVerifier } type IntrospectorJWTProfile interface { Introspector - JWTProfileVerifier() JWTProfileVerifier + JWTProfileVerifier(context.Context) JWTProfileVerifier } func introspectionHandler(introspector Introspector) func(http.ResponseWriter, *http.Request) { @@ -62,7 +63,7 @@ func ParseTokenIntrospectionRequest(r *http.Request, introspector Introspector) return "", "", errors.New("unable to parse request") } if introspectorJWTProfile, ok := introspector.(IntrospectorJWTProfile); ok && req.ClientAssertion != "" { - profile, err := VerifyJWTAssertion(r.Context(), req.ClientAssertion, introspectorJWTProfile.JWTProfileVerifier()) + profile, err := VerifyJWTAssertion(r.Context(), req.ClientAssertion, introspectorJWTProfile.JWTProfileVerifier(r.Context())) if err == nil { return req.Token, profile.Issuer, nil } diff --git a/pkg/op/token_jwt_profile.go b/pkg/op/token_jwt_profile.go index 01a1411..e996bce 100644 --- a/pkg/op/token_jwt_profile.go +++ b/pkg/op/token_jwt_profile.go @@ -11,7 +11,7 @@ import ( type JWTAuthorizationGrantExchanger interface { Exchanger - JWTProfileVerifier() JWTProfileVerifier + JWTProfileVerifier(context.Context) JWTProfileVerifier } //JWTProfile handles the OAuth 2.0 JWT Profile Authorization Grant https://tools.ietf.org/html/rfc7523#section-2.1 @@ -21,7 +21,7 @@ func JWTProfile(w http.ResponseWriter, r *http.Request, exchanger JWTAuthorizati RequestError(w, r, err) } - tokenRequest, err := VerifyJWTAssertion(r.Context(), profileRequest.Assertion, exchanger.JWTProfileVerifier()) + tokenRequest, err := VerifyJWTAssertion(r.Context(), profileRequest.Assertion, exchanger.JWTProfileVerifier(r.Context())) if err != nil { RequestError(w, r, err) return diff --git a/pkg/op/token_request.go b/pkg/op/token_request.go index 6732bb1..55a26c0 100644 --- a/pkg/op/token_request.go +++ b/pkg/op/token_request.go @@ -10,10 +10,8 @@ import ( ) type Exchanger interface { - Issuer() string Storage() Storage Decoder() httphelper.Decoder - Signer() Signer Crypto() Crypto AuthMethodPostSupported() bool AuthMethodPrivateKeyJWTSupported() bool @@ -111,7 +109,7 @@ func AuthorizeCodeChallenge(tokenReq *oidc.AccessTokenRequest, challenge *oidc.C //AuthorizePrivateJWTKey authorizes a client by validating the client_assertion's signature with a previously //registered public key (JWT Profile) func AuthorizePrivateJWTKey(ctx context.Context, clientAssertion string, exchanger JWTAuthorizationGrantExchanger) (Client, error) { - jwtReq, err := VerifyJWTAssertion(ctx, clientAssertion, exchanger.JWTProfileVerifier()) + jwtReq, err := VerifyJWTAssertion(ctx, clientAssertion, exchanger.JWTProfileVerifier(ctx)) if err != nil { return nil, err } diff --git a/pkg/op/token_revocation.go b/pkg/op/token_revocation.go index fbaf8b7..55bc461 100644 --- a/pkg/op/token_revocation.go +++ b/pkg/op/token_revocation.go @@ -14,14 +14,14 @@ type Revoker interface { Decoder() httphelper.Decoder Crypto() Crypto Storage() Storage - AccessTokenVerifier() AccessTokenVerifier + AccessTokenVerifier(context.Context) AccessTokenVerifier AuthMethodPrivateKeyJWTSupported() bool AuthMethodPostSupported() bool } type RevokerJWTProfile interface { Revoker - JWTProfileVerifier() JWTProfileVerifier + JWTProfileVerifier(context.Context) JWTProfileVerifier } func revocationHandler(revoker Revoker) func(http.ResponseWriter, *http.Request) { @@ -67,7 +67,7 @@ func ParseTokenRevocationRequest(r *http.Request, revoker Revoker) (token, token if !ok || !revoker.AuthMethodPrivateKeyJWTSupported() { return "", "", "", oidc.ErrInvalidClient().WithDescription("auth_method private_key_jwt not supported") } - profile, err := VerifyJWTAssertion(r.Context(), req.ClientAssertion, revokerJWTProfile.JWTProfileVerifier()) + profile, err := VerifyJWTAssertion(r.Context(), req.ClientAssertion, revokerJWTProfile.JWTProfileVerifier(r.Context())) if err == nil { return req.Token, req.TokenTypeHint, profile.Issuer, nil } @@ -128,7 +128,7 @@ func getTokenIDAndSubjectForRevocation(ctx context.Context, userinfoProvider Use } return splitToken[0], splitToken[1], true } - accessTokenClaims, err := VerifyAccessToken(ctx, accessToken, userinfoProvider.AccessTokenVerifier()) + accessTokenClaims, err := VerifyAccessToken(ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx)) if err != nil { return "", "", false } diff --git a/pkg/op/userinfo.go b/pkg/op/userinfo.go index f07a8bc..9ed33e6 100644 --- a/pkg/op/userinfo.go +++ b/pkg/op/userinfo.go @@ -14,7 +14,7 @@ type UserinfoProvider interface { Decoder() httphelper.Decoder Crypto() Crypto Storage() Storage - AccessTokenVerifier() AccessTokenVerifier + AccessTokenVerifier(context.Context) AccessTokenVerifier } func userinfoHandler(userinfoProvider UserinfoProvider) func(http.ResponseWriter, *http.Request) { @@ -81,7 +81,7 @@ func getTokenIDAndSubject(ctx context.Context, userinfoProvider UserinfoProvider } return splitToken[0], splitToken[1], true } - accessTokenClaims, err := VerifyAccessToken(ctx, accessToken, userinfoProvider.AccessTokenVerifier()) + accessTokenClaims, err := VerifyAccessToken(ctx, accessToken, userinfoProvider.AccessTokenVerifier(ctx)) if err != nil { return "", "", false } From 3dd0d5fc3a738d7e52e0372c2d946e17a20ceffd Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 22 Apr 2022 14:25:27 +0200 Subject: [PATCH 02/15] move example --- example/server/{ => op}/login.go | 0 example/server/{ => op}/op.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename example/server/{ => op}/login.go (100%) rename example/server/{ => op}/op.go (100%) diff --git a/example/server/login.go b/example/server/op/login.go similarity index 100% rename from example/server/login.go rename to example/server/op/login.go diff --git a/example/server/op.go b/example/server/op/op.go similarity index 100% rename from example/server/op.go rename to example/server/op/op.go From 5c5d71640965741c52f3d3812193fea0d640cddb Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 22 Apr 2022 15:02:24 +0200 Subject: [PATCH 03/15] fix examples --- example/server/dynamic/login.go | 118 ++++++++++ example/server/dynamic/op.go | 138 +++++++++++ example/server/internal/storage.go | 6 +- example/server/internal/storage_dynamic.go | 260 +++++++++++++++++++++ example/server/op/login.go | 10 +- example/server/op/op.go | 15 +- 6 files changed, 533 insertions(+), 14 deletions(-) create mode 100644 example/server/dynamic/login.go create mode 100644 example/server/dynamic/op.go create mode 100644 example/server/internal/storage_dynamic.go diff --git a/example/server/dynamic/login.go b/example/server/dynamic/login.go new file mode 100644 index 0000000..a95e4f9 --- /dev/null +++ b/example/server/dynamic/login.go @@ -0,0 +1,118 @@ +package main + +import ( + "context" + "fmt" + "html/template" + "net/http" + + "github.com/gorilla/mux" + + "github.com/caos/oidc/pkg/op" +) + +const ( + queryAuthRequestID = "authRequestID" +) + +var ( + loginTmpl, _ = template.New("login").Parse(` + + + + + Login + + +
+ + + +
+ + +
+ +
+ + +
+ +

{{.Error}}

+ + +
+ + `) +) + +type login struct { + authenticate authenticate + router *mux.Router + callback func(context.Context, string) string +} + +func NewLogin(authenticate authenticate, callback func(context.Context, string) string, issuerInterceptor *op.IssuerInterceptor) *login { + l := &login{ + authenticate: authenticate, + callback: callback, + } + l.createRouter(issuerInterceptor) + return l +} + +func (l *login) createRouter(issuerInterceptor *op.IssuerInterceptor) { + l.router = mux.NewRouter() + l.router.Path("/username").Methods("GET").HandlerFunc(l.loginHandler) + l.router.Path("/username").Methods("POST").HandlerFunc(issuerInterceptor.HandlerFunc(l.checkLoginHandler)) +} + +type authenticate interface { + CheckUsernamePassword(ctx context.Context, username, password, id string) error +} + +func (l *login) loginHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + http.Error(w, fmt.Sprintf("cannot parse form:%s", err), http.StatusInternalServerError) + return + } + //the oidc package will pass the id of the auth request as query parameter + //we will use this id through the login process and therefore pass it to the login page + renderLogin(w, r.FormValue(queryAuthRequestID), nil) +} + +func renderLogin(w http.ResponseWriter, id string, err error) { + var errMsg string + if err != nil { + errMsg = err.Error() + } + data := &struct { + ID string + Error string + }{ + ID: id, + Error: errMsg, + } + err = loginTmpl.Execute(w, data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func (l *login) checkLoginHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + http.Error(w, fmt.Sprintf("cannot parse form:%s", err), http.StatusInternalServerError) + return + } + username := r.FormValue("username") + password := r.FormValue("password") + id := r.FormValue("id") + err = l.authenticate.CheckUsernamePassword(r.Context(), username, password, id) + if err != nil { + renderLogin(w, id, err) + return + } + http.Redirect(w, r, l.callback(r.Context(), id), http.StatusFound) +} diff --git a/example/server/dynamic/op.go b/example/server/dynamic/op.go new file mode 100644 index 0000000..1384e00 --- /dev/null +++ b/example/server/dynamic/op.go @@ -0,0 +1,138 @@ +package main + +import ( + "context" + "crypto/sha256" + "fmt" + "log" + "net/http" + + "github.com/gorilla/mux" + "golang.org/x/text/language" + + "github.com/caos/oidc/example/server/internal" + "github.com/caos/oidc/pkg/op" +) + +const ( + pathLoggedOut = "/logged-out" +) + +var ( + hostnames = []string{ + "localhost", //note that calling 127.0.0.1 / ::1 won't work as the hostname does not match + "oidc.local", //add this to your hosts file (pointing to 127.0.0.1) + //feel free to add more... + } +) + +func init() { + internal.RegisterClients( + internal.NativeClient("native"), + internal.WebClient("web", "secret"), + internal.WebClient("api", "secret"), + ) +} + +func main() { + ctx := context.Background() + + port := "9998" + issuers := make([]string, len(hostnames)) + for i, hostname := range hostnames { + issuers[i] = fmt.Sprintf("http://%s:%s", hostname, port) + } + + //the OpenID Provider requires a 32-byte key for (token) encryption + //be sure to create a proper crypto random key and manage it securely! + key := sha256.Sum256([]byte("test")) + + router := mux.NewRouter() + + //for simplicity, we provide a very small default page for users who have signed out + router.HandleFunc(pathLoggedOut, func(w http.ResponseWriter, req *http.Request) { + _, err := w.Write([]byte("signed out successfully")) + if err != nil { + log.Printf("error serving logged out page: %v", err) + } + }) + + //the OpenIDProvider interface needs a Storage interface handling various checks and state manipulations + //this might be the layer for accessing your database + //in this example it will be handled in-memory + //the NewMultiStorage is able to handle multiple issuers + storage := internal.NewMultiStorage(issuers) + + //creation of the OpenIDProvider with the just created in-memory Storage + provider, err := newDynamicOP(ctx, storage, key) + if err != nil { + log.Fatal(err) + } + + //the provider will only take care of the OpenID Protocol, so there must be some sort of UI for the login process + //for the simplicity of the example this means a simple page with username and password field + //be sure to provide an IssuerInterceptor with the IssuerFromRequest from the OP so the login can select / and pass it to the storage + l := NewLogin(storage, op.AuthCallbackURL(provider), op.NewIssuerInterceptor(provider.IssuerFromRequest)) + + //regardless of how many pages / steps there are in the process, the UI must be registered in the router, + //so we will direct all calls to /login to the login UI + router.PathPrefix("/login/").Handler(http.StripPrefix("/login", l.router)) + + //we register the http handler of the OP on the root, so that the discovery endpoint (/.well-known/openid-configuration) + //is served on the correct path + // + //if your issuer ends with a path (e.g. http://localhost:9998/custom/path/), + //then you would have to set the path prefix (/custom/path/): + //router.PathPrefix("/custom/path/").Handler(http.StripPrefix("/custom/path", provider.HttpHandler())) + router.PathPrefix("/").Handler(provider.HttpHandler()) + + server := &http.Server{ + Addr: ":" + port, + Handler: router, + } + err = server.ListenAndServe() + if err != nil { + log.Fatal(err) + } + <-ctx.Done() +} + +//newDynamicOP will create an OpenID Provider for localhost on a specified port with a given encryption key +//and a predefined default logout uri +//it will enable all options (see descriptions) +func newDynamicOP(ctx context.Context, storage op.Storage, key [32]byte) (*op.Provider, error) { + config := &op.Config{ + CryptoKey: key, + + //will be used if the end_session endpoint is called without a post_logout_redirect_uri + DefaultLogoutRedirectURI: pathLoggedOut, + + //enables code_challenge_method S256 for PKCE (and therefore PKCE in general) + CodeMethodS256: true, + + //enables additional client_id/client_secret authentication by form post (not only HTTP Basic Auth) + AuthMethodPost: true, + + //enables additional authentication by using private_key_jwt + AuthMethodPrivateKeyJWT: true, + + //enables refresh_token grant use + GrantTypeRefreshToken: true, + + //enables use of the `request` Object parameter + RequestObjectSupported: true, + + //this example has only static texts (in English), so we'll set the here accordingly + SupportedUILocales: []language.Tag{language.English}, + } + handler, err := op.NewDynamicOpenIDProvider(ctx, "/", config, storage, + //we must explicitly allow the use of the http issuer + op.WithAllowInsecure(), + //as an example on how to customize an endpoint this will change the authorization_endpoint from /authorize to /auth + op.WithCustomAuthEndpoint(op.NewEndpoint("auth")), + ) + if err != nil { + return nil, err + } + return handler, nil +} diff --git a/example/server/internal/storage.go b/example/server/internal/storage.go index d4d51f8..330d600 100644 --- a/example/server/internal/storage.go +++ b/example/server/internal/storage.go @@ -6,6 +6,7 @@ import ( "crypto/rsa" "fmt" "math/big" + "strings" "time" "github.com/google/uuid" @@ -80,7 +81,8 @@ func (s *publicKey) Key() interface{} { return &s.key.PublicKey } -func NewStorage() *storage { +func NewStorage(issuer string) *storage { + hostname := strings.Split(strings.Split(issuer, "://")[0], ":")[0] key, _ := rsa.GenerateKey(rand.Reader, 2048) return &storage{ authRequests: make(map[string]*AuthRequest), @@ -91,7 +93,7 @@ func NewStorage() *storage { users: map[string]*User{ "id1": { id: "id1", - username: "test-user", + username: "test-user@" + hostname, password: "verysecure", firstname: "Test", lastname: "User", diff --git a/example/server/internal/storage_dynamic.go b/example/server/internal/storage_dynamic.go new file mode 100644 index 0000000..bcb4ce4 --- /dev/null +++ b/example/server/internal/storage_dynamic.go @@ -0,0 +1,260 @@ +package internal + +import ( + "context" + "time" + + "gopkg.in/square/go-jose.v2" + + "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/pkg/op" +) + +type multiStorage struct { + issuers map[string]*storage +} + +//NewMultiStorage implements the op.Storage interface by wrapping multiple storage structs +//and selecting them by the calling issuer +func NewMultiStorage(issuers []string) *multiStorage { + s := make(map[string]*storage) + for _, issuer := range issuers { + s[issuer] = NewStorage(issuer) + } + return &multiStorage{issuers: s} +} + +//CheckUsernamePassword implements the `authenticate` interface of the login +func (s *multiStorage) CheckUsernamePassword(ctx context.Context, username, password, id string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.CheckUsernamePassword(username, password, id) +} + +//CreateAuthRequest implements the op.Storage interface +//it will be called after parsing and validation of the authentication request +func (s *multiStorage) CreateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, userID string) (op.AuthRequest, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.CreateAuthRequest(ctx, authReq, userID) +} + +//AuthRequestByID implements the op.Storage interface +//it will be called after the Login UI redirects back to the OIDC endpoint +func (s *multiStorage) AuthRequestByID(ctx context.Context, id string) (op.AuthRequest, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.AuthRequestByID(ctx, id) +} + +//AuthRequestByCode implements the op.Storage interface +//it will be called after parsing and validation of the token request (in an authorization code flow) +func (s *multiStorage) AuthRequestByCode(ctx context.Context, code string) (op.AuthRequest, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.AuthRequestByCode(ctx, code) +} + +//SaveAuthCode implements the op.Storage interface +//it will be called after the authentication has been successful and before redirecting the user agent to the redirect_uri +//(in an authorization code flow) +func (s *multiStorage) SaveAuthCode(ctx context.Context, id string, code string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.SaveAuthCode(ctx, id, code) +} + +//DeleteAuthRequest implements the op.Storage interface +//it will be called after creating the token response (id and access tokens) for a valid +//- authentication request (in an implicit flow) +//- token request (in an authorization code flow) +func (s *multiStorage) DeleteAuthRequest(ctx context.Context, id string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.DeleteAuthRequest(ctx, id) +} + +//CreateAccessToken implements the op.Storage interface +//it will be called for all requests able to return an access token (Authorization Code Flow, Implicit Flow, JWT Profile, ...) +func (s *multiStorage) CreateAccessToken(ctx context.Context, request op.TokenRequest) (string, time.Time, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return "", time.Time{}, err + } + return storage.CreateAccessToken(ctx, request) +} + +//CreateAccessAndRefreshTokens implements the op.Storage interface +//it will be called for all requests able to return an access and refresh token (Authorization Code Flow, Refresh Token Request) +func (s *multiStorage) CreateAccessAndRefreshTokens(ctx context.Context, request op.TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshToken string, expiration time.Time, err error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return "", "", time.Time{}, err + } + return storage.CreateAccessAndRefreshTokens(ctx, request, currentRefreshToken) +} + +//TokenRequestByRefreshToken implements the op.Storage interface +//it will be called after parsing and validation of the refresh token request +func (s *multiStorage) TokenRequestByRefreshToken(ctx context.Context, refreshToken string) (op.RefreshTokenRequest, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.TokenRequestByRefreshToken(ctx, refreshToken) +} + +//TerminateSession implements the op.Storage interface +//it will be called after the user signed out, therefore the access and refresh token of the user of this client must be removed +func (s *multiStorage) TerminateSession(ctx context.Context, userID string, clientID string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.TerminateSession(ctx, userID, clientID) +} + +//RevokeToken implements the op.Storage interface +//it will be called after parsing and validation of the token revocation request +func (s *multiStorage) RevokeToken(ctx context.Context, token string, userID string, clientID string) *oidc.Error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.RevokeToken(ctx, token, userID, clientID) +} + +//SigningKey implements the op.Storage interface +//it will be called when creating the OpenID Provider +func (s *multiStorage) SigningKey(ctx context.Context) (op.SigningKey, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.SigningKey(ctx) +} + +//SignatureAlgorithms implements the op.Storage interface +//it will be called to get the sign +func (s *multiStorage) SignatureAlgorithms(ctx context.Context) ([]jose.SignatureAlgorithm, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.SignatureAlgorithms(ctx) +} + +//KeySet implements the op.Storage interface +//it will be called to get the current (public) keys, among others for the keys_endpoint or for validating access_tokens on the userinfo_endpoint, ... +func (s *multiStorage) KeySet(ctx context.Context) ([]op.Key, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.KeySet(ctx) +} + +//GetClientByClientID implements the op.Storage interface +//it will be called whenever information (type, redirect_uris, ...) about the client behind the client_id is needed +func (s *multiStorage) GetClientByClientID(ctx context.Context, clientID string) (op.Client, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.GetClientByClientID(ctx, clientID) +} + +//AuthorizeClientIDSecret implements the op.Storage interface +//it will be called for validating the client_id, client_secret on token or introspection requests +func (s *multiStorage) AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.AuthorizeClientIDSecret(ctx, clientID, clientSecret) +} + +//SetUserinfoFromScopes implements the op.Storage interface +//it will be called for the creation of an id_token, so we'll just pass it to the private function without any further check +func (s *multiStorage) SetUserinfoFromScopes(ctx context.Context, userinfo oidc.UserInfoSetter, userID, clientID string, scopes []string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.SetUserinfoFromScopes(ctx, userinfo, userID, clientID, scopes) +} + +//SetUserinfoFromToken implements the op.Storage interface +//it will be called for the userinfo endpoint, so we read the token and pass the information from that to the private function +func (s *multiStorage) SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.SetUserinfoFromToken(ctx, userinfo, tokenID, subject, origin) +} + +//SetIntrospectionFromToken implements the op.Storage interface +//it will be called for the introspection endpoint, so we read the token and pass the information from that to the private function +func (s *multiStorage) SetIntrospectionFromToken(ctx context.Context, introspection oidc.IntrospectionResponse, tokenID, subject, clientID string) error { + storage, err := s.storageFromContext(ctx) + if err != nil { + return err + } + return storage.SetIntrospectionFromToken(ctx, introspection, tokenID, subject, clientID) +} + +//GetPrivateClaimsFromScopes implements the op.Storage interface +//it will be called for the creation of a JWT access token to assert claims for custom scopes +func (s *multiStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (claims map[string]interface{}, err error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.GetPrivateClaimsFromScopes(ctx, userID, clientID, scopes) +} + +//GetKeyByIDAndUserID implements the op.Storage interface +//it will be called to validate the signatures of a JWT (JWT Profile Grant and Authentication) +func (s *multiStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.GetKeyByIDAndUserID(ctx, keyID, userID) +} + +//ValidateJWTProfileScopes implements the op.Storage interface +//it will be called to validate the scopes of a JWT Profile Authorization Grant request +func (s *multiStorage) ValidateJWTProfileScopes(ctx context.Context, userID string, scopes []string) ([]string, error) { + storage, err := s.storageFromContext(ctx) + if err != nil { + return nil, err + } + return storage.ValidateJWTProfileScopes(ctx, userID, scopes) +} + +//Health implements the op.Storage interface +func (s *multiStorage) Health(ctx context.Context) error { + return nil +} + +func (s *multiStorage) storageFromContext(ctx context.Context) (*storage, *oidc.Error) { + storage, ok := s.issuers[op.IssuerFromContext(ctx)] + if !ok { + + } + return storage, nil +} diff --git a/example/server/op/login.go b/example/server/op/login.go index 51168ac..6e69fdb 100644 --- a/example/server/op/login.go +++ b/example/server/op/login.go @@ -7,8 +7,6 @@ import ( "net/http" "github.com/gorilla/mux" - - "github.com/caos/oidc/pkg/op" ) const ( @@ -52,19 +50,19 @@ type login struct { callback func(context.Context, string) string } -func NewLogin(authenticate authenticate, callback func(context.Context, string) string, issuerInterceptor *op.IssuerInterceptor) *login { +func NewLogin(authenticate authenticate, callback func(context.Context, string) string) *login { l := &login{ authenticate: authenticate, callback: callback, } - l.createRouter(issuerInterceptor) + l.createRouter() return l } -func (l *login) createRouter(issuerInterceptor *op.IssuerInterceptor) { +func (l *login) createRouter() { l.router = mux.NewRouter() l.router.Path("/username").Methods("GET").HandlerFunc(l.loginHandler) - l.router.Path("/username").Methods("POST").HandlerFunc(issuerInterceptor.HandlerFunc(l.checkLoginHandler)) + l.router.Path("/username").Methods("POST").HandlerFunc(l.checkLoginHandler) } type authenticate interface { diff --git a/example/server/op/op.go b/example/server/op/op.go index 45717f9..7f5c48d 100644 --- a/example/server/op/op.go +++ b/example/server/op/op.go @@ -3,6 +3,7 @@ package main import ( "context" "crypto/sha256" + "fmt" "log" "net/http" @@ -28,7 +29,10 @@ func init() { func main() { ctx := context.Background() + //we will run on :9998 port := "9998" + //which gives us the issuer: //http://localhost:9998/ + issuer := fmt.Sprintf("http://localhost:%s/", port) //the OpenID Provider requires a 32-byte key for (token) encryption //be sure to create a proper crypto random key and manage it securely! @@ -47,17 +51,17 @@ func main() { //the OpenIDProvider interface needs a Storage interface handling various checks and state manipulations //this might be the layer for accessing your database //in this example it will be handled in-memory - storage := internal.NewStorage() + storage := internal.NewStorage(issuer) //creation of the OpenIDProvider with the just created in-memory Storage - provider, err := newOP(ctx, storage, port, key) + provider, err := newOP(ctx, storage, issuer, key) if err != nil { log.Fatal(err) } //the provider will only take care of the OpenID Protocol, so there must be some sort of UI for the login process //for the simplicity of the example this means a simple page with username and password field - l := NewLogin(storage, op.AuthCallbackURL(provider), op.NewIssuerInterceptor(provider.IssuerFromRequest)) + l := NewLogin(storage, op.AuthCallbackURL(provider)) //regardless of how many pages / steps there are in the process, the UI must be registered in the router, //so we will direct all calls to /login to the login UI @@ -85,7 +89,7 @@ func main() { //newOP will create an OpenID Provider for localhost on a specified port with a given encryption key //and a predefined default logout uri //it will enable all options (see descriptions) -func newOP(ctx context.Context, storage op.Storage, port string, key [32]byte) (*op.Provider, error) { +func newOP(ctx context.Context, storage op.Storage, issuer string, key [32]byte) (*op.Provider, error) { config := &op.Config{ CryptoKey: key, @@ -110,8 +114,7 @@ func newOP(ctx context.Context, storage op.Storage, port string, key [32]byte) ( //this example has only static texts (in English), so we'll set the here accordingly SupportedUILocales: []language.Tag{language.English}, } - //handler, err := op.NewOpenIDProvider(ctx, fmt.Sprintf("http://localhost:%s/", port), config, storage, - handler, err := op.NewDynamicOpenIDProvider(ctx, "/", config, storage, + handler, err := op.NewOpenIDProvider(ctx, issuer, config, storage, //we must explicitly allow the use of the http issuer op.WithAllowInsecure(), //as an example on how to customize an endpoint this will change the authorization_endpoint from /authorize to /auth From 58e1e53c6b2171285d46da2373d0aa272f895fbc Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 22 Apr 2022 15:05:50 +0200 Subject: [PATCH 04/15] fix mocks --- pkg/op/mock/discovery.mock.go | 51 +++++++++++++++++++++++++++++++++++ pkg/op/mock/signer.mock.go | 14 ++++++++++ 2 files changed, 65 insertions(+) create mode 100644 pkg/op/mock/discovery.mock.go diff --git a/pkg/op/mock/discovery.mock.go b/pkg/op/mock/discovery.mock.go new file mode 100644 index 0000000..313449d --- /dev/null +++ b/pkg/op/mock/discovery.mock.go @@ -0,0 +1,51 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/caos/oidc/pkg/op (interfaces: DiscoverStorage) + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + jose "gopkg.in/square/go-jose.v2" +) + +// MockDiscoverStorage is a mock of DiscoverStorage interface. +type MockDiscoverStorage struct { + ctrl *gomock.Controller + recorder *MockDiscoverStorageMockRecorder +} + +// MockDiscoverStorageMockRecorder is the mock recorder for MockDiscoverStorage. +type MockDiscoverStorageMockRecorder struct { + mock *MockDiscoverStorage +} + +// NewMockDiscoverStorage creates a new mock instance. +func NewMockDiscoverStorage(ctrl *gomock.Controller) *MockDiscoverStorage { + mock := &MockDiscoverStorage{ctrl: ctrl} + mock.recorder = &MockDiscoverStorageMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDiscoverStorage) EXPECT() *MockDiscoverStorageMockRecorder { + return m.recorder +} + +// SignatureAlgorithms mocks base method. +func (m *MockDiscoverStorage) SignatureAlgorithms(arg0 context.Context) ([]jose.SignatureAlgorithm, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignatureAlgorithms", arg0) + ret0, _ := ret[0].([]jose.SignatureAlgorithm) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SignatureAlgorithms indicates an expected call of SignatureAlgorithms. +func (mr *MockDiscoverStorageMockRecorder) SignatureAlgorithms(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignatureAlgorithms", reflect.TypeOf((*MockDiscoverStorage)(nil).SignatureAlgorithms), arg0) +} diff --git a/pkg/op/mock/signer.mock.go b/pkg/op/mock/signer.mock.go index 392a118..1ebb349 100644 --- a/pkg/op/mock/signer.mock.go +++ b/pkg/op/mock/signer.mock.go @@ -34,6 +34,20 @@ func (m *MockSigningKey) EXPECT() *MockSigningKeyMockRecorder { return m.recorder } +// ID mocks base method. +func (m *MockSigningKey) ID() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ID") + ret0, _ := ret[0].(string) + return ret0 +} + +// ID indicates an expected call of ID. +func (mr *MockSigningKeyMockRecorder) ID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockSigningKey)(nil).ID)) +} + // Key mocks base method. func (m *MockSigningKey) Key() interface{} { m.ctrl.T.Helper() From 636d0db0339d40d3893659b52589e7798ea4ed63 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 22 Apr 2022 15:13:15 +0200 Subject: [PATCH 05/15] update readme --- README.md | 2 +- example/doc.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b5cd075..8e6f788 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ The most important packages of the library: /app web app / RP demonstrating authorization code flow using various authentication methods (code, PKCE, JWT profile) /github example of the extended OAuth2 library, providing an HTTP client with a reuse token source /service demonstration of JWT Profile Authorization Grant - /server example of an OpenID Provider implementation including some very basic login UI + /server examples of an OpenID Provider implementations (including dynamic) with some very basic login UI ## How To Use It diff --git a/example/doc.go b/example/doc.go index 7212a7d..7c1f41e 100644 --- a/example/doc.go +++ b/example/doc.go @@ -5,7 +5,7 @@ Package example contains some example of the various use of this library: /app web app / RP demonstrating authorization code flow using various authentication methods (code, PKCE, JWT profile) /github example of the extended OAuth2 library, providing an HTTP client with a reuse token source /service demonstration of JWT Profile Authorization Grant -/server example of an OpenID Provider implementation including some very basic login UI +/server examples of an OpenID Provider implementations (including dynamic) with some very basic login UI */ package example From 33a38e9c076fbf5f4b508d6ae99e3396ef3a1575 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 22 Apr 2022 15:42:09 +0200 Subject: [PATCH 06/15] fix examples and update usage --- README.md | 15 +++++++++++++-- example/server/dynamic/op.go | 2 +- example/server/internal/storage.go | 2 +- example/server/internal/storage_dynamic.go | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8e6f788..dc5a96c 100644 --- a/README.md +++ b/README.md @@ -43,16 +43,27 @@ Check the `/example` folder where example code for different scenarios is locate ```bash # start oidc op server # oidc discovery http://localhost:9998/.well-known/openid-configuration -go run github.com/caos/oidc/example/server +go run github.com/caos/oidc/example/server/op # start oidc web client CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://localhost:9998/ SCOPES="openid profile" PORT=9999 go run github.com/caos/oidc/example/client/app ``` - open http://localhost:9999/login in your browser - you will be redirected to op server and the login UI -- login with user `test-user` and password `verysecure` +- login with user `test-user@localhost` and password `verysecure` - the OP will redirect you to the client app, which displays the user info +for the dynamic issuer, just start it with: +```bash +go run github.com/caos/oidc/example/server/dynamic +``` +the oidc web client above will still work, but if you add `oidc.local` (pointing to 127.0.0.1) in your hosts file you can also start it with: +```bash +CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://oidc.local:9998/ SCOPES="openid profile" PORT=9999 go run github.com/caos/oidc/example/client/app +``` + +> Note: Usernames are suffixed with the hostname (`test-user@localhost` or `test-user@oidc.local`) + ## Features | | Code Flow | Implicit Flow | Hybrid Flow | Discovery | PKCE | Token Exchange | mTLS | JWT Profile | Refresh Token | diff --git a/example/server/dynamic/op.go b/example/server/dynamic/op.go index 1384e00..ec15902 100644 --- a/example/server/dynamic/op.go +++ b/example/server/dynamic/op.go @@ -40,7 +40,7 @@ func main() { port := "9998" issuers := make([]string, len(hostnames)) for i, hostname := range hostnames { - issuers[i] = fmt.Sprintf("http://%s:%s", hostname, port) + issuers[i] = fmt.Sprintf("http://%s:%s/", hostname, port) } //the OpenID Provider requires a 32-byte key for (token) encryption diff --git a/example/server/internal/storage.go b/example/server/internal/storage.go index 330d600..b2ac0f7 100644 --- a/example/server/internal/storage.go +++ b/example/server/internal/storage.go @@ -82,7 +82,7 @@ func (s *publicKey) Key() interface{} { } func NewStorage(issuer string) *storage { - hostname := strings.Split(strings.Split(issuer, "://")[0], ":")[0] + hostname := strings.Split(strings.Split(issuer, "://")[1], ":")[0] key, _ := rsa.GenerateKey(rand.Reader, 2048) return &storage{ authRequests: make(map[string]*AuthRequest), diff --git a/example/server/internal/storage_dynamic.go b/example/server/internal/storage_dynamic.go index bcb4ce4..efabb29 100644 --- a/example/server/internal/storage_dynamic.go +++ b/example/server/internal/storage_dynamic.go @@ -254,7 +254,7 @@ func (s *multiStorage) Health(ctx context.Context) error { func (s *multiStorage) storageFromContext(ctx context.Context) (*storage, *oidc.Error) { storage, ok := s.issuers[op.IssuerFromContext(ctx)] if !ok { - + return nil, oidc.ErrInvalidRequest().WithDescription("invalid issuer") } return storage, nil } From 7dd0ea57803864c9574b1e21d63bdaeed4617536 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 22 Apr 2022 16:00:02 +0200 Subject: [PATCH 07/15] update go module version to v2 --- example/client/api/api.go | 4 ++-- example/client/app/app.go | 6 +++--- example/client/github/github.go | 6 +++--- example/client/service/service.go | 2 +- example/server/dynamic/login.go | 2 +- example/server/dynamic/op.go | 4 ++-- example/server/internal/client.go | 4 ++-- example/server/internal/oidc.go | 5 ++--- example/server/internal/storage.go | 4 ++-- example/server/internal/storage_dynamic.go | 4 ++-- example/server/op/op.go | 4 ++-- go.mod | 2 +- pkg/client/client.go | 6 +++--- pkg/client/jwt_profile.go | 4 ++-- pkg/client/profile/jwt_profile.go | 4 ++-- pkg/client/rp/cli/cli.go | 6 +++--- pkg/client/rp/delegation.go | 2 +- pkg/client/rp/jwks.go | 4 ++-- pkg/client/rp/mock/verifier.mock.go | 2 +- pkg/client/rp/relaying_party.go | 6 +++--- pkg/client/rp/tockenexchange.go | 2 +- pkg/client/rp/verifier.go | 2 +- pkg/client/rs/resource_server.go | 6 +++--- pkg/oidc/code_challenge.go | 2 +- pkg/oidc/token.go | 4 ++-- pkg/oidc/verifier.go | 2 +- pkg/op/auth_request.go | 6 +++--- pkg/op/auth_request_test.go | 8 ++++---- pkg/op/client.go | 2 +- pkg/op/crypto.go | 2 +- pkg/op/discovery.go | 4 ++-- pkg/op/discovery_test.go | 6 +++--- pkg/op/endpoint_test.go | 2 +- pkg/op/error.go | 4 ++-- pkg/op/keys.go | 2 +- pkg/op/keys_test.go | 6 +++--- pkg/op/mock/authorizer.mock.go | 5 +++-- pkg/op/mock/authorizer.mock.impl.go | 4 ++-- pkg/op/mock/client.go | 4 ++-- pkg/op/mock/client.mock.go | 5 +++-- pkg/op/mock/configuration.mock.go | 3 ++- pkg/op/mock/key.mock.go | 3 ++- pkg/op/mock/storage.mock.go | 5 +++-- pkg/op/mock/storage.mock.impl.go | 5 ++--- pkg/op/op.go | 4 ++-- pkg/op/probes.go | 2 +- pkg/op/session.go | 4 ++-- pkg/op/storage.go | 2 +- pkg/op/token.go | 6 +++--- pkg/op/token_code.go | 4 ++-- pkg/op/token_intospection.go | 4 ++-- pkg/op/token_jwt_profile.go | 4 ++-- pkg/op/token_refresh.go | 6 +++--- pkg/op/token_request.go | 4 ++-- pkg/op/token_revocation.go | 4 ++-- pkg/op/userinfo.go | 4 ++-- pkg/op/verifier_access_token.go | 2 +- pkg/op/verifier_id_token_hint.go | 2 +- pkg/op/verifier_jwt_profile.go | 2 +- 59 files changed, 116 insertions(+), 113 deletions(-) diff --git a/example/client/api/api.go b/example/client/api/api.go index a3ae85e..54850a5 100644 --- a/example/client/api/api.go +++ b/example/client/api/api.go @@ -12,8 +12,8 @@ import ( "github.com/gorilla/mux" "github.com/sirupsen/logrus" - "github.com/caos/oidc/pkg/client/rs" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/client/rs" + "github.com/caos/oidc/v2/pkg/oidc" ) const ( diff --git a/example/client/app/app.go b/example/client/app/app.go index e0369e3..0b5a5e5 100644 --- a/example/client/app/app.go +++ b/example/client/app/app.go @@ -11,9 +11,9 @@ import ( "github.com/google/uuid" "github.com/sirupsen/logrus" - "github.com/caos/oidc/pkg/client/rp" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/client/rp" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) var ( diff --git a/example/client/github/github.go b/example/client/github/github.go index 45f16c1..b44c028 100644 --- a/example/client/github/github.go +++ b/example/client/github/github.go @@ -10,9 +10,9 @@ import ( "golang.org/x/oauth2" githubOAuth "golang.org/x/oauth2/github" - "github.com/caos/oidc/pkg/client/rp" - "github.com/caos/oidc/pkg/client/rp/cli" - "github.com/caos/oidc/pkg/http" + "github.com/caos/oidc/v2/pkg/client/rp" + "github.com/caos/oidc/v2/pkg/client/rp/cli" + "github.com/caos/oidc/v2/pkg/http" ) var ( diff --git a/example/client/service/service.go b/example/client/service/service.go index 818b481..498fe45 100644 --- a/example/client/service/service.go +++ b/example/client/service/service.go @@ -13,7 +13,7 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/oauth2" - "github.com/caos/oidc/pkg/client/profile" + "github.com/caos/oidc/v2/pkg/client/profile" ) var ( diff --git a/example/server/dynamic/login.go b/example/server/dynamic/login.go index a95e4f9..ff054b8 100644 --- a/example/server/dynamic/login.go +++ b/example/server/dynamic/login.go @@ -8,7 +8,7 @@ import ( "github.com/gorilla/mux" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/op" ) const ( diff --git a/example/server/dynamic/op.go b/example/server/dynamic/op.go index ec15902..8fe71d7 100644 --- a/example/server/dynamic/op.go +++ b/example/server/dynamic/op.go @@ -10,8 +10,8 @@ import ( "github.com/gorilla/mux" "golang.org/x/text/language" - "github.com/caos/oidc/example/server/internal" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/example/server/internal" + "github.com/caos/oidc/v2/pkg/op" ) const ( diff --git a/example/server/internal/client.go b/example/server/internal/client.go index 55425a3..a4dcaca 100644 --- a/example/server/internal/client.go +++ b/example/server/internal/client.go @@ -3,8 +3,8 @@ package internal import ( "time" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" ) var ( diff --git a/example/server/internal/oidc.go b/example/server/internal/oidc.go index 1b3bf52..09f0c99 100644 --- a/example/server/internal/oidc.go +++ b/example/server/internal/oidc.go @@ -5,9 +5,8 @@ import ( "golang.org/x/text/language" - "github.com/caos/oidc/pkg/op" - - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" ) const ( diff --git a/example/server/internal/storage.go b/example/server/internal/storage.go index b2ac0f7..e3d75c1 100644 --- a/example/server/internal/storage.go +++ b/example/server/internal/storage.go @@ -13,8 +13,8 @@ import ( "golang.org/x/text/language" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" ) var ( diff --git a/example/server/internal/storage_dynamic.go b/example/server/internal/storage_dynamic.go index efabb29..2fdfd2b 100644 --- a/example/server/internal/storage_dynamic.go +++ b/example/server/internal/storage_dynamic.go @@ -6,8 +6,8 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" ) type multiStorage struct { diff --git a/example/server/op/op.go b/example/server/op/op.go index 7f5c48d..5613b52 100644 --- a/example/server/op/op.go +++ b/example/server/op/op.go @@ -10,8 +10,8 @@ import ( "github.com/gorilla/mux" "golang.org/x/text/language" - "github.com/caos/oidc/example/server/internal" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/example/server/internal" + "github.com/caos/oidc/v2/pkg/op" ) const ( diff --git a/go.mod b/go.mod index 81b5d4a..059bbf3 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/caos/oidc +module github.com/caos/oidc/v2 go 1.15 diff --git a/pkg/client/client.go b/pkg/client/client.go index ac6cd56..f0040d4 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -10,9 +10,9 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/crypto" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/crypto" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) var ( diff --git a/pkg/client/jwt_profile.go b/pkg/client/jwt_profile.go index e120541..702b17b 100644 --- a/pkg/client/jwt_profile.go +++ b/pkg/client/jwt_profile.go @@ -5,8 +5,8 @@ import ( "golang.org/x/oauth2" - "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) //JWTProfileExchange handles the oauth2 jwt profile exchange diff --git a/pkg/client/profile/jwt_profile.go b/pkg/client/profile/jwt_profile.go index 6b7db2c..935c599 100644 --- a/pkg/client/profile/jwt_profile.go +++ b/pkg/client/profile/jwt_profile.go @@ -7,8 +7,8 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/client" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/client" + "github.com/caos/oidc/v2/pkg/oidc" ) //jwtProfileTokenSource implement the oauth2.TokenSource diff --git a/pkg/client/rp/cli/cli.go b/pkg/client/rp/cli/cli.go index aba1546..dbc7b0f 100644 --- a/pkg/client/rp/cli/cli.go +++ b/pkg/client/rp/cli/cli.go @@ -4,9 +4,9 @@ import ( "context" "net/http" - "github.com/caos/oidc/pkg/client/rp" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/client/rp" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/client/rp/delegation.go b/pkg/client/rp/delegation.go index 73edd96..6e80f6d 100644 --- a/pkg/client/rp/delegation.go +++ b/pkg/client/rp/delegation.go @@ -1,7 +1,7 @@ package rp import ( - "github.com/caos/oidc/pkg/oidc/grants/tokenexchange" + "github.com/caos/oidc/v2/pkg/oidc/grants/tokenexchange" ) //DelegationTokenRequest is an implementation of TokenExchangeRequest diff --git a/pkg/client/rp/jwks.go b/pkg/client/rp/jwks.go index 78f9580..01682ef 100644 --- a/pkg/client/rp/jwks.go +++ b/pkg/client/rp/jwks.go @@ -9,8 +9,8 @@ import ( "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) func NewRemoteKeySet(client *http.Client, jwksURL string, opts ...func(*remoteKeySet)) oidc.KeySet { diff --git a/pkg/client/rp/mock/verifier.mock.go b/pkg/client/rp/mock/verifier.mock.go index 08cf77f..f153580 100644 --- a/pkg/client/rp/mock/verifier.mock.go +++ b/pkg/client/rp/mock/verifier.mock.go @@ -10,7 +10,7 @@ import ( "github.com/golang/mock/gomock" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" ) // MockVerifier is a mock of Verifier interface diff --git a/pkg/client/rp/relaying_party.go b/pkg/client/rp/relaying_party.go index 91622f3..bcbef1c 100644 --- a/pkg/client/rp/relaying_party.go +++ b/pkg/client/rp/relaying_party.go @@ -12,9 +12,9 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/client" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/client" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/client/rp/tockenexchange.go b/pkg/client/rp/tockenexchange.go index d5056ae..ea1ee93 100644 --- a/pkg/client/rp/tockenexchange.go +++ b/pkg/client/rp/tockenexchange.go @@ -5,7 +5,7 @@ import ( "golang.org/x/oauth2" - "github.com/caos/oidc/pkg/oidc/grants/tokenexchange" + "github.com/caos/oidc/v2/pkg/oidc/grants/tokenexchange" ) //TokenExchangeRP extends the `RelyingParty` interface for the *draft* oauth2 `Token Exchange` diff --git a/pkg/client/rp/verifier.go b/pkg/client/rp/verifier.go index 027ca79..da8b160 100644 --- a/pkg/client/rp/verifier.go +++ b/pkg/client/rp/verifier.go @@ -6,7 +6,7 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" ) type IDTokenVerifier interface { diff --git a/pkg/client/rs/resource_server.go b/pkg/client/rs/resource_server.go index 224442f..f622b21 100644 --- a/pkg/client/rs/resource_server.go +++ b/pkg/client/rs/resource_server.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - "github.com/caos/oidc/pkg/client" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/client" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type ResourceServer interface { diff --git a/pkg/oidc/code_challenge.go b/pkg/oidc/code_challenge.go index 4e82feb..d7b0295 100644 --- a/pkg/oidc/code_challenge.go +++ b/pkg/oidc/code_challenge.go @@ -3,7 +3,7 @@ package oidc import ( "crypto/sha256" - "github.com/caos/oidc/pkg/crypto" + "github.com/caos/oidc/v2/pkg/crypto" ) const ( diff --git a/pkg/oidc/token.go b/pkg/oidc/token.go index e34543e..3ecb15e 100644 --- a/pkg/oidc/token.go +++ b/pkg/oidc/token.go @@ -9,8 +9,8 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/crypto" - "github.com/caos/oidc/pkg/http" + "github.com/caos/oidc/v2/pkg/crypto" + "github.com/caos/oidc/v2/pkg/http" ) const ( diff --git a/pkg/oidc/verifier.go b/pkg/oidc/verifier.go index 9f5335d..17ebf50 100644 --- a/pkg/oidc/verifier.go +++ b/pkg/oidc/verifier.go @@ -12,7 +12,7 @@ import ( "gopkg.in/square/go-jose.v2" - str "github.com/caos/oidc/pkg/strings" + str "github.com/caos/oidc/v2/pkg/strings" ) type Claims interface { diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index ff90bd4..28807d0 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -10,9 +10,9 @@ import ( "github.com/gorilla/mux" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" - str "github.com/caos/oidc/pkg/strings" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" + str "github.com/caos/oidc/v2/pkg/strings" ) type AuthRequest interface { diff --git a/pkg/op/auth_request_test.go b/pkg/op/auth_request_test.go index 7259ec7..de47414 100644 --- a/pkg/op/auth_request_test.go +++ b/pkg/op/auth_request_test.go @@ -13,10 +13,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" - "github.com/caos/oidc/pkg/op/mock" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" + "github.com/caos/oidc/v2/pkg/op/mock" ) // diff --git a/pkg/op/client.go b/pkg/op/client.go index f1e18fa..2571363 100644 --- a/pkg/op/client.go +++ b/pkg/op/client.go @@ -3,7 +3,7 @@ package op import ( "time" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/op/crypto.go b/pkg/op/crypto.go index e9dd67b..004ae71 100644 --- a/pkg/op/crypto.go +++ b/pkg/op/crypto.go @@ -1,7 +1,7 @@ package op import ( - "github.com/caos/oidc/pkg/crypto" + "github.com/caos/oidc/v2/pkg/crypto" ) type Crypto interface { diff --git a/pkg/op/discovery.go b/pkg/op/discovery.go index 58b9948..47d9d58 100644 --- a/pkg/op/discovery.go +++ b/pkg/op/discovery.go @@ -6,8 +6,8 @@ import ( "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type DiscoverStorage interface { diff --git a/pkg/op/discovery_test.go b/pkg/op/discovery_test.go index 953d21f..672f83d 100644 --- a/pkg/op/discovery_test.go +++ b/pkg/op/discovery_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" - "github.com/caos/oidc/pkg/op/mock" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" + "github.com/caos/oidc/v2/pkg/op/mock" ) func TestDiscover(t *testing.T) { diff --git a/pkg/op/endpoint_test.go b/pkg/op/endpoint_test.go index fe00326..93562d3 100644 --- a/pkg/op/endpoint_test.go +++ b/pkg/op/endpoint_test.go @@ -3,7 +3,7 @@ package op_test import ( "testing" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/op" ) func TestEndpoint_Path(t *testing.T) { diff --git a/pkg/op/error.go b/pkg/op/error.go index ea8d368..c2ddc26 100644 --- a/pkg/op/error.go +++ b/pkg/op/error.go @@ -3,8 +3,8 @@ package op import ( "net/http" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type ErrAuthRequest interface { diff --git a/pkg/op/keys.go b/pkg/op/keys.go index 7490b5c..5075b27 100644 --- a/pkg/op/keys.go +++ b/pkg/op/keys.go @@ -6,7 +6,7 @@ import ( "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/pkg/http" + httphelper "github.com/caos/oidc/v2/pkg/http" ) type KeyProvider interface { diff --git a/pkg/op/keys_test.go b/pkg/op/keys_test.go index e3592c5..c2e5505 100644 --- a/pkg/op/keys_test.go +++ b/pkg/op/keys_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/assert" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" - "github.com/caos/oidc/pkg/op/mock" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" + "github.com/caos/oidc/v2/pkg/op/mock" ) func TestKeys(t *testing.T) { diff --git a/pkg/op/mock/authorizer.mock.go b/pkg/op/mock/authorizer.mock.go index 29b8aac..b900ad0 100644 --- a/pkg/op/mock/authorizer.mock.go +++ b/pkg/op/mock/authorizer.mock.go @@ -8,9 +8,10 @@ import ( context "context" reflect "reflect" - http "github.com/caos/oidc/pkg/http" - op "github.com/caos/oidc/pkg/op" gomock "github.com/golang/mock/gomock" + + http "github.com/caos/oidc/v2/pkg/http" + op "github.com/caos/oidc/v2/pkg/op" ) // MockAuthorizer is a mock of Authorizer interface. diff --git a/pkg/op/mock/authorizer.mock.impl.go b/pkg/op/mock/authorizer.mock.impl.go index c1afdb5..305bf04 100644 --- a/pkg/op/mock/authorizer.mock.impl.go +++ b/pkg/op/mock/authorizer.mock.impl.go @@ -8,8 +8,8 @@ import ( "github.com/gorilla/schema" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" ) func NewAuthorizer(t *testing.T) op.Authorizer { diff --git a/pkg/op/mock/client.go b/pkg/op/mock/client.go index b7ac3e8..088eafb 100644 --- a/pkg/op/mock/client.go +++ b/pkg/op/mock/client.go @@ -5,8 +5,8 @@ import ( "github.com/golang/mock/gomock" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" ) func NewClient(t *testing.T) op.Client { diff --git a/pkg/op/mock/client.mock.go b/pkg/op/mock/client.mock.go index f78754d..8f1044a 100644 --- a/pkg/op/mock/client.mock.go +++ b/pkg/op/mock/client.mock.go @@ -8,9 +8,10 @@ import ( reflect "reflect" time "time" - oidc "github.com/caos/oidc/pkg/oidc" - op "github.com/caos/oidc/pkg/op" gomock "github.com/golang/mock/gomock" + + oidc "github.com/caos/oidc/v2/pkg/oidc" + op "github.com/caos/oidc/v2/pkg/op" ) // MockClient is a mock of Client interface. diff --git a/pkg/op/mock/configuration.mock.go b/pkg/op/mock/configuration.mock.go index a07dbab..992157b 100644 --- a/pkg/op/mock/configuration.mock.go +++ b/pkg/op/mock/configuration.mock.go @@ -8,9 +8,10 @@ import ( http "net/http" reflect "reflect" - op "github.com/caos/oidc/pkg/op" gomock "github.com/golang/mock/gomock" language "golang.org/x/text/language" + + op "github.com/caos/oidc/v2/pkg/op" ) // MockConfiguration is a mock of Configuration interface. diff --git a/pkg/op/mock/key.mock.go b/pkg/op/mock/key.mock.go index 7d6afec..89def29 100644 --- a/pkg/op/mock/key.mock.go +++ b/pkg/op/mock/key.mock.go @@ -8,8 +8,9 @@ import ( context "context" reflect "reflect" - op "github.com/caos/oidc/pkg/op" gomock "github.com/golang/mock/gomock" + + op "github.com/caos/oidc/v2/pkg/op" ) // MockKeyProvider is a mock of KeyProvider interface. diff --git a/pkg/op/mock/storage.mock.go b/pkg/op/mock/storage.mock.go index 2dcc558..097fb98 100644 --- a/pkg/op/mock/storage.mock.go +++ b/pkg/op/mock/storage.mock.go @@ -9,10 +9,11 @@ import ( reflect "reflect" time "time" - oidc "github.com/caos/oidc/pkg/oidc" - op "github.com/caos/oidc/pkg/op" gomock "github.com/golang/mock/gomock" jose "gopkg.in/square/go-jose.v2" + + oidc "github.com/caos/oidc/v2/pkg/oidc" + op "github.com/caos/oidc/v2/pkg/op" ) // MockStorage is a mock of Storage interface. diff --git a/pkg/op/mock/storage.mock.impl.go b/pkg/op/mock/storage.mock.impl.go index b9f6681..e92a568 100644 --- a/pkg/op/mock/storage.mock.impl.go +++ b/pkg/op/mock/storage.mock.impl.go @@ -6,11 +6,10 @@ import ( "testing" "time" - "github.com/caos/oidc/pkg/oidc" - "github.com/golang/mock/gomock" - "github.com/caos/oidc/pkg/op" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/op" ) func NewStorage(t *testing.T) op.Storage { diff --git a/pkg/op/op.go b/pkg/op/op.go index 393f56a..5e737a6 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -12,8 +12,8 @@ import ( "golang.org/x/text/language" "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/op/probes.go b/pkg/op/probes.go index ee83447..c5e5b57 100644 --- a/pkg/op/probes.go +++ b/pkg/op/probes.go @@ -5,7 +5,7 @@ import ( "errors" "net/http" - httphelper "github.com/caos/oidc/pkg/http" + httphelper "github.com/caos/oidc/v2/pkg/http" ) type ProbesFn func(context.Context) error diff --git a/pkg/op/session.go b/pkg/op/session.go index d064387..f727232 100644 --- a/pkg/op/session.go +++ b/pkg/op/session.go @@ -4,8 +4,8 @@ import ( "context" "net/http" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type SessionEnder interface { diff --git a/pkg/op/storage.go b/pkg/op/storage.go index f07ab37..3fb239f 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -6,7 +6,7 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" ) type AuthStorage interface { diff --git a/pkg/op/token.go b/pkg/op/token.go index e2ce00f..439b93e 100644 --- a/pkg/op/token.go +++ b/pkg/op/token.go @@ -4,9 +4,9 @@ import ( "context" "time" - "github.com/caos/oidc/pkg/crypto" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/strings" + "github.com/caos/oidc/v2/pkg/crypto" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/strings" ) type TokenCreator interface { diff --git a/pkg/op/token_code.go b/pkg/op/token_code.go index 7b5873c..cac5197 100644 --- a/pkg/op/token_code.go +++ b/pkg/op/token_code.go @@ -4,8 +4,8 @@ import ( "context" "net/http" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) //CodeExchange handles the OAuth 2.0 authorization_code grant, including diff --git a/pkg/op/token_intospection.go b/pkg/op/token_intospection.go index 6076e20..5ef50f8 100644 --- a/pkg/op/token_intospection.go +++ b/pkg/op/token_intospection.go @@ -6,8 +6,8 @@ import ( "net/http" "net/url" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type Introspector interface { diff --git a/pkg/op/token_jwt_profile.go b/pkg/op/token_jwt_profile.go index e996bce..4431c74 100644 --- a/pkg/op/token_jwt_profile.go +++ b/pkg/op/token_jwt_profile.go @@ -5,8 +5,8 @@ import ( "net/http" "time" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type JWTAuthorizationGrantExchanger interface { diff --git a/pkg/op/token_refresh.go b/pkg/op/token_refresh.go index 0b6d470..f82c75c 100644 --- a/pkg/op/token_refresh.go +++ b/pkg/op/token_refresh.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" - "github.com/caos/oidc/pkg/strings" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" + "github.com/caos/oidc/v2/pkg/strings" ) type RefreshTokenRequest interface { diff --git a/pkg/op/token_request.go b/pkg/op/token_request.go index 55a26c0..cdc44b1 100644 --- a/pkg/op/token_request.go +++ b/pkg/op/token_request.go @@ -5,8 +5,8 @@ import ( "net/http" "net/url" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type Exchanger interface { diff --git a/pkg/op/token_revocation.go b/pkg/op/token_revocation.go index 55bc461..1372684 100644 --- a/pkg/op/token_revocation.go +++ b/pkg/op/token_revocation.go @@ -6,8 +6,8 @@ import ( "net/url" "strings" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type Revoker interface { diff --git a/pkg/op/userinfo.go b/pkg/op/userinfo.go index 9ed33e6..7af6e93 100644 --- a/pkg/op/userinfo.go +++ b/pkg/op/userinfo.go @@ -6,8 +6,8 @@ import ( "net/http" "strings" - httphelper "github.com/caos/oidc/pkg/http" - "github.com/caos/oidc/pkg/oidc" + httphelper "github.com/caos/oidc/v2/pkg/http" + "github.com/caos/oidc/v2/pkg/oidc" ) type UserinfoProvider interface { diff --git a/pkg/op/verifier_access_token.go b/pkg/op/verifier_access_token.go index 2220244..fa295f7 100644 --- a/pkg/op/verifier_access_token.go +++ b/pkg/op/verifier_access_token.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" ) type AccessTokenVerifier interface { diff --git a/pkg/op/verifier_id_token_hint.go b/pkg/op/verifier_id_token_hint.go index 7baa075..302375c 100644 --- a/pkg/op/verifier_id_token_hint.go +++ b/pkg/op/verifier_id_token_hint.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" ) type IDTokenHintVerifier interface { diff --git a/pkg/op/verifier_jwt_profile.go b/pkg/op/verifier_jwt_profile.go index e7784b5..403bf17 100644 --- a/pkg/op/verifier_jwt_profile.go +++ b/pkg/op/verifier_jwt_profile.go @@ -8,7 +8,7 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/pkg/oidc" + "github.com/caos/oidc/v2/pkg/oidc" ) type JWTProfileVerifier interface { From 826c8b89d4f7ac172610a0bed16ca38e392aecd5 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 25 Apr 2022 08:02:09 +0200 Subject: [PATCH 08/15] build branch --- .releaserc.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.releaserc.js b/.releaserc.js index 6500ace..7b9f1ce 100644 --- a/.releaserc.js +++ b/.releaserc.js @@ -1,5 +1,8 @@ module.exports = { - branches: ["main"], + branches: [ + {name: "main"}, + {name: "dynamic-issuer", prerelease: true}, + ], plugins: [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", From bb4d854efe96086530a40f71dbfbc12fe4da47d6 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Wed, 27 Apr 2022 00:28:09 +0200 Subject: [PATCH 09/15] fix(module): rename caos to zitadel --- example/client/api/api.go | 4 ++-- example/client/app/app.go | 6 +++--- example/client/github/github.go | 6 +++--- example/client/service/service.go | 2 +- example/server/dynamic/login.go | 2 +- example/server/dynamic/op.go | 4 ++-- example/server/internal/client.go | 4 ++-- example/server/internal/oidc.go | 4 ++-- example/server/internal/storage.go | 4 ++-- example/server/internal/storage_dynamic.go | 4 ++-- example/server/op/op.go | 4 ++-- go.mod | 2 +- pkg/client/client.go | 6 +++--- pkg/client/jwt_profile.go | 4 ++-- pkg/client/profile/jwt_profile.go | 4 ++-- pkg/client/rp/cli/cli.go | 6 +++--- pkg/client/rp/delegation.go | 2 +- pkg/client/rp/jwks.go | 4 ++-- pkg/client/rp/mock/generate.go | 2 +- pkg/client/rp/mock/verifier.mock.go | 4 ++-- pkg/client/rp/relaying_party.go | 6 +++--- pkg/client/rp/tockenexchange.go | 2 +- pkg/client/rp/verifier.go | 2 +- pkg/client/rs/resource_server.go | 6 +++--- pkg/oidc/code_challenge.go | 2 +- pkg/oidc/token.go | 4 ++-- pkg/oidc/verifier.go | 2 +- pkg/op/auth_request.go | 6 +++--- pkg/op/auth_request_test.go | 8 ++++---- pkg/op/client.go | 2 +- pkg/op/crypto.go | 2 +- pkg/op/discovery.go | 4 ++-- pkg/op/discovery_test.go | 6 +++--- pkg/op/endpoint_test.go | 2 +- pkg/op/error.go | 4 ++-- pkg/op/keys.go | 2 +- pkg/op/keys_test.go | 6 +++--- pkg/op/mock/authorizer.mock.go | 6 +++--- pkg/op/mock/authorizer.mock.impl.go | 4 ++-- pkg/op/mock/client.go | 4 ++-- pkg/op/mock/client.mock.go | 6 +++--- pkg/op/mock/configuration.mock.go | 4 ++-- pkg/op/mock/discovery.mock.go | 2 +- pkg/op/mock/generate.go | 14 +++++++------- pkg/op/mock/key.mock.go | 4 ++-- pkg/op/mock/signer.mock.go | 2 +- pkg/op/mock/storage.mock.go | 6 +++--- pkg/op/mock/storage.mock.impl.go | 4 ++-- pkg/op/op.go | 4 ++-- pkg/op/probes.go | 2 +- pkg/op/session.go | 4 ++-- pkg/op/storage.go | 2 +- pkg/op/token.go | 6 +++--- pkg/op/token_code.go | 4 ++-- pkg/op/token_intospection.go | 4 ++-- pkg/op/token_jwt_profile.go | 4 ++-- pkg/op/token_refresh.go | 6 +++--- pkg/op/token_request.go | 4 ++-- pkg/op/token_revocation.go | 4 ++-- pkg/op/userinfo.go | 4 ++-- pkg/op/verifier_access_token.go | 2 +- pkg/op/verifier_id_token_hint.go | 2 +- pkg/op/verifier_jwt_profile.go | 2 +- 63 files changed, 127 insertions(+), 127 deletions(-) diff --git a/example/client/api/api.go b/example/client/api/api.go index 54850a5..b7ad2b7 100644 --- a/example/client/api/api.go +++ b/example/client/api/api.go @@ -12,8 +12,8 @@ import ( "github.com/gorilla/mux" "github.com/sirupsen/logrus" - "github.com/caos/oidc/v2/pkg/client/rs" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/client/rs" + "github.com/zitadel/oidc/v2/pkg/oidc" ) const ( diff --git a/example/client/app/app.go b/example/client/app/app.go index 0b5a5e5..567550b 100644 --- a/example/client/app/app.go +++ b/example/client/app/app.go @@ -11,9 +11,9 @@ import ( "github.com/google/uuid" "github.com/sirupsen/logrus" - "github.com/caos/oidc/v2/pkg/client/rp" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/client/rp" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) var ( diff --git a/example/client/github/github.go b/example/client/github/github.go index b44c028..57bb3ae 100644 --- a/example/client/github/github.go +++ b/example/client/github/github.go @@ -10,9 +10,9 @@ import ( "golang.org/x/oauth2" githubOAuth "golang.org/x/oauth2/github" - "github.com/caos/oidc/v2/pkg/client/rp" - "github.com/caos/oidc/v2/pkg/client/rp/cli" - "github.com/caos/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/client/rp" + "github.com/zitadel/oidc/v2/pkg/client/rp/cli" + "github.com/zitadel/oidc/v2/pkg/http" ) var ( diff --git a/example/client/service/service.go b/example/client/service/service.go index 498fe45..bbdc16c 100644 --- a/example/client/service/service.go +++ b/example/client/service/service.go @@ -13,7 +13,7 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/oauth2" - "github.com/caos/oidc/v2/pkg/client/profile" + "github.com/zitadel/oidc/v2/pkg/client/profile" ) var ( diff --git a/example/server/dynamic/login.go b/example/server/dynamic/login.go index ff054b8..618ee52 100644 --- a/example/server/dynamic/login.go +++ b/example/server/dynamic/login.go @@ -8,7 +8,7 @@ import ( "github.com/gorilla/mux" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/op" ) const ( diff --git a/example/server/dynamic/op.go b/example/server/dynamic/op.go index 8fe71d7..669041b 100644 --- a/example/server/dynamic/op.go +++ b/example/server/dynamic/op.go @@ -10,8 +10,8 @@ import ( "github.com/gorilla/mux" "golang.org/x/text/language" - "github.com/caos/oidc/v2/example/server/internal" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/example/server/internal" + "github.com/zitadel/oidc/v2/pkg/op" ) const ( diff --git a/example/server/internal/client.go b/example/server/internal/client.go index a4dcaca..b6854c8 100644 --- a/example/server/internal/client.go +++ b/example/server/internal/client.go @@ -3,8 +3,8 @@ package internal import ( "time" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" ) var ( diff --git a/example/server/internal/oidc.go b/example/server/internal/oidc.go index 09f0c99..b7893a4 100644 --- a/example/server/internal/oidc.go +++ b/example/server/internal/oidc.go @@ -5,8 +5,8 @@ import ( "golang.org/x/text/language" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" ) const ( diff --git a/example/server/internal/storage.go b/example/server/internal/storage.go index e3d75c1..9aa6010 100644 --- a/example/server/internal/storage.go +++ b/example/server/internal/storage.go @@ -13,8 +13,8 @@ import ( "golang.org/x/text/language" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" ) var ( diff --git a/example/server/internal/storage_dynamic.go b/example/server/internal/storage_dynamic.go index 2fdfd2b..7fe0da5 100644 --- a/example/server/internal/storage_dynamic.go +++ b/example/server/internal/storage_dynamic.go @@ -6,8 +6,8 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" ) type multiStorage struct { diff --git a/example/server/op/op.go b/example/server/op/op.go index 5613b52..ed36a79 100644 --- a/example/server/op/op.go +++ b/example/server/op/op.go @@ -10,8 +10,8 @@ import ( "github.com/gorilla/mux" "golang.org/x/text/language" - "github.com/caos/oidc/v2/example/server/internal" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/example/server/internal" + "github.com/zitadel/oidc/v2/pkg/op" ) const ( diff --git a/go.mod b/go.mod index 059bbf3..482ad7b 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/caos/oidc/v2 +module github.com/zitadel/oidc/v2 go 1.15 diff --git a/pkg/client/client.go b/pkg/client/client.go index f0040d4..30b5626 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -10,9 +10,9 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/crypto" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/crypto" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) var ( diff --git a/pkg/client/jwt_profile.go b/pkg/client/jwt_profile.go index 702b17b..b2298f2 100644 --- a/pkg/client/jwt_profile.go +++ b/pkg/client/jwt_profile.go @@ -5,8 +5,8 @@ import ( "golang.org/x/oauth2" - "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) //JWTProfileExchange handles the oauth2 jwt profile exchange diff --git a/pkg/client/profile/jwt_profile.go b/pkg/client/profile/jwt_profile.go index 935c599..db33d07 100644 --- a/pkg/client/profile/jwt_profile.go +++ b/pkg/client/profile/jwt_profile.go @@ -7,8 +7,8 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/client" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/client" + "github.com/zitadel/oidc/v2/pkg/oidc" ) //jwtProfileTokenSource implement the oauth2.TokenSource diff --git a/pkg/client/rp/cli/cli.go b/pkg/client/rp/cli/cli.go index dbc7b0f..936f319 100644 --- a/pkg/client/rp/cli/cli.go +++ b/pkg/client/rp/cli/cli.go @@ -4,9 +4,9 @@ import ( "context" "net/http" - "github.com/caos/oidc/v2/pkg/client/rp" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/client/rp" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/client/rp/delegation.go b/pkg/client/rp/delegation.go index 6e80f6d..286e6a9 100644 --- a/pkg/client/rp/delegation.go +++ b/pkg/client/rp/delegation.go @@ -1,7 +1,7 @@ package rp import ( - "github.com/caos/oidc/v2/pkg/oidc/grants/tokenexchange" + "github.com/zitadel/oidc/v2/pkg/oidc/grants/tokenexchange" ) //DelegationTokenRequest is an implementation of TokenExchangeRequest diff --git a/pkg/client/rp/jwks.go b/pkg/client/rp/jwks.go index 01682ef..786bc68 100644 --- a/pkg/client/rp/jwks.go +++ b/pkg/client/rp/jwks.go @@ -9,8 +9,8 @@ import ( "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) func NewRemoteKeySet(client *http.Client, jwksURL string, opts ...func(*remoteKeySet)) oidc.KeySet { diff --git a/pkg/client/rp/mock/generate.go b/pkg/client/rp/mock/generate.go index 71bc3be..1e05701 100644 --- a/pkg/client/rp/mock/generate.go +++ b/pkg/client/rp/mock/generate.go @@ -1,3 +1,3 @@ package mock -//go:generate mockgen -package mock -destination ./verifier.mock.go github.com/caos/oidc/pkg/rp Verifier +//go:generate mockgen -package mock -destination ./verifier.mock.go github.com/zitadel/oidc/pkg/rp Verifier diff --git a/pkg/client/rp/mock/verifier.mock.go b/pkg/client/rp/mock/verifier.mock.go index f153580..9d1daa1 100644 --- a/pkg/client/rp/mock/verifier.mock.go +++ b/pkg/client/rp/mock/verifier.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/rp (interfaces: Verifier) +// Source: github.com/zitadel/oidc/pkg/rp (interfaces: Verifier) // Package mock is a generated GoMock package. package mock @@ -10,7 +10,7 @@ import ( "github.com/golang/mock/gomock" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/oidc" ) // MockVerifier is a mock of Verifier interface diff --git a/pkg/client/rp/relaying_party.go b/pkg/client/rp/relaying_party.go index bcbef1c..f877ba0 100644 --- a/pkg/client/rp/relaying_party.go +++ b/pkg/client/rp/relaying_party.go @@ -12,9 +12,9 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/client" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/client" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/client/rp/tockenexchange.go b/pkg/client/rp/tockenexchange.go index ea1ee93..1b9845a 100644 --- a/pkg/client/rp/tockenexchange.go +++ b/pkg/client/rp/tockenexchange.go @@ -5,7 +5,7 @@ import ( "golang.org/x/oauth2" - "github.com/caos/oidc/v2/pkg/oidc/grants/tokenexchange" + "github.com/zitadel/oidc/v2/pkg/oidc/grants/tokenexchange" ) //TokenExchangeRP extends the `RelyingParty` interface for the *draft* oauth2 `Token Exchange` diff --git a/pkg/client/rp/verifier.go b/pkg/client/rp/verifier.go index da8b160..a1537a4 100644 --- a/pkg/client/rp/verifier.go +++ b/pkg/client/rp/verifier.go @@ -6,7 +6,7 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type IDTokenVerifier interface { diff --git a/pkg/client/rs/resource_server.go b/pkg/client/rs/resource_server.go index f622b21..edadf62 100644 --- a/pkg/client/rs/resource_server.go +++ b/pkg/client/rs/resource_server.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - "github.com/caos/oidc/v2/pkg/client" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/client" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type ResourceServer interface { diff --git a/pkg/oidc/code_challenge.go b/pkg/oidc/code_challenge.go index d7b0295..37c1783 100644 --- a/pkg/oidc/code_challenge.go +++ b/pkg/oidc/code_challenge.go @@ -3,7 +3,7 @@ package oidc import ( "crypto/sha256" - "github.com/caos/oidc/v2/pkg/crypto" + "github.com/zitadel/oidc/v2/pkg/crypto" ) const ( diff --git a/pkg/oidc/token.go b/pkg/oidc/token.go index 3ecb15e..c621c78 100644 --- a/pkg/oidc/token.go +++ b/pkg/oidc/token.go @@ -9,8 +9,8 @@ import ( "golang.org/x/oauth2" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/crypto" - "github.com/caos/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/crypto" + "github.com/zitadel/oidc/v2/pkg/http" ) const ( diff --git a/pkg/oidc/verifier.go b/pkg/oidc/verifier.go index 17ebf50..68b5c25 100644 --- a/pkg/oidc/verifier.go +++ b/pkg/oidc/verifier.go @@ -12,7 +12,7 @@ import ( "gopkg.in/square/go-jose.v2" - str "github.com/caos/oidc/v2/pkg/strings" + str "github.com/zitadel/oidc/v2/pkg/strings" ) type Claims interface { diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index 28807d0..bf72721 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -10,9 +10,9 @@ import ( "github.com/gorilla/mux" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" - str "github.com/caos/oidc/v2/pkg/strings" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" + str "github.com/zitadel/oidc/v2/pkg/strings" ) type AuthRequest interface { diff --git a/pkg/op/auth_request_test.go b/pkg/op/auth_request_test.go index de47414..c209126 100644 --- a/pkg/op/auth_request_test.go +++ b/pkg/op/auth_request_test.go @@ -13,10 +13,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" - "github.com/caos/oidc/v2/pkg/op/mock" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/op/mock" ) // diff --git a/pkg/op/client.go b/pkg/op/client.go index 2571363..e89fd53 100644 --- a/pkg/op/client.go +++ b/pkg/op/client.go @@ -3,7 +3,7 @@ package op import ( "time" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/op/crypto.go b/pkg/op/crypto.go index 004ae71..6786022 100644 --- a/pkg/op/crypto.go +++ b/pkg/op/crypto.go @@ -1,7 +1,7 @@ package op import ( - "github.com/caos/oidc/v2/pkg/crypto" + "github.com/zitadel/oidc/v2/pkg/crypto" ) type Crypto interface { diff --git a/pkg/op/discovery.go b/pkg/op/discovery.go index 47d9d58..ce3240b 100644 --- a/pkg/op/discovery.go +++ b/pkg/op/discovery.go @@ -6,8 +6,8 @@ import ( "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type DiscoverStorage interface { diff --git a/pkg/op/discovery_test.go b/pkg/op/discovery_test.go index 672f83d..66fb4a5 100644 --- a/pkg/op/discovery_test.go +++ b/pkg/op/discovery_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" - "github.com/caos/oidc/v2/pkg/op/mock" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/op/mock" ) func TestDiscover(t *testing.T) { diff --git a/pkg/op/endpoint_test.go b/pkg/op/endpoint_test.go index 93562d3..9457150 100644 --- a/pkg/op/endpoint_test.go +++ b/pkg/op/endpoint_test.go @@ -3,7 +3,7 @@ package op_test import ( "testing" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/op" ) func TestEndpoint_Path(t *testing.T) { diff --git a/pkg/op/error.go b/pkg/op/error.go index c2ddc26..acca4ab 100644 --- a/pkg/op/error.go +++ b/pkg/op/error.go @@ -3,8 +3,8 @@ package op import ( "net/http" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type ErrAuthRequest interface { diff --git a/pkg/op/keys.go b/pkg/op/keys.go index 5075b27..239ecbd 100644 --- a/pkg/op/keys.go +++ b/pkg/op/keys.go @@ -6,7 +6,7 @@ import ( "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/v2/pkg/http" + httphelper "github.com/zitadel/oidc/v2/pkg/http" ) type KeyProvider interface { diff --git a/pkg/op/keys_test.go b/pkg/op/keys_test.go index c2e5505..2e56b78 100644 --- a/pkg/op/keys_test.go +++ b/pkg/op/keys_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/assert" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" - "github.com/caos/oidc/v2/pkg/op/mock" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/op/mock" ) func TestKeys(t *testing.T) { diff --git a/pkg/op/mock/authorizer.mock.go b/pkg/op/mock/authorizer.mock.go index b900ad0..179f4c4 100644 --- a/pkg/op/mock/authorizer.mock.go +++ b/pkg/op/mock/authorizer.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: Authorizer) +// Source: github.com/zitadel/oidc/pkg/op (interfaces: Authorizer) // Package mock is a generated GoMock package. package mock @@ -10,8 +10,8 @@ import ( gomock "github.com/golang/mock/gomock" - http "github.com/caos/oidc/v2/pkg/http" - op "github.com/caos/oidc/v2/pkg/op" + http "github.com/zitadel/oidc/v2/pkg/http" + op "github.com/zitadel/oidc/v2/pkg/op" ) // MockAuthorizer is a mock of Authorizer interface. diff --git a/pkg/op/mock/authorizer.mock.impl.go b/pkg/op/mock/authorizer.mock.impl.go index 305bf04..a2c5cc2 100644 --- a/pkg/op/mock/authorizer.mock.impl.go +++ b/pkg/op/mock/authorizer.mock.impl.go @@ -8,8 +8,8 @@ import ( "github.com/gorilla/schema" "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" ) func NewAuthorizer(t *testing.T) op.Authorizer { diff --git a/pkg/op/mock/client.go b/pkg/op/mock/client.go index 088eafb..2920192 100644 --- a/pkg/op/mock/client.go +++ b/pkg/op/mock/client.go @@ -5,8 +5,8 @@ import ( "github.com/golang/mock/gomock" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" ) func NewClient(t *testing.T) op.Client { diff --git a/pkg/op/mock/client.mock.go b/pkg/op/mock/client.mock.go index 8f1044a..028a56f 100644 --- a/pkg/op/mock/client.mock.go +++ b/pkg/op/mock/client.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: Client) +// Source: github.com/zitadel/oidc/pkg/op (interfaces: Client) // Package mock is a generated GoMock package. package mock @@ -10,8 +10,8 @@ import ( gomock "github.com/golang/mock/gomock" - oidc "github.com/caos/oidc/v2/pkg/oidc" - op "github.com/caos/oidc/v2/pkg/op" + oidc "github.com/zitadel/oidc/v2/pkg/oidc" + op "github.com/zitadel/oidc/v2/pkg/op" ) // MockClient is a mock of Client interface. diff --git a/pkg/op/mock/configuration.mock.go b/pkg/op/mock/configuration.mock.go index 992157b..df371ce 100644 --- a/pkg/op/mock/configuration.mock.go +++ b/pkg/op/mock/configuration.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: Configuration) +// Source: github.com/zitadel/oidc/pkg/op (interfaces: Configuration) // Package mock is a generated GoMock package. package mock @@ -11,7 +11,7 @@ import ( gomock "github.com/golang/mock/gomock" language "golang.org/x/text/language" - op "github.com/caos/oidc/v2/pkg/op" + op "github.com/zitadel/oidc/v2/pkg/op" ) // MockConfiguration is a mock of Configuration interface. diff --git a/pkg/op/mock/discovery.mock.go b/pkg/op/mock/discovery.mock.go index 313449d..2974c4d 100644 --- a/pkg/op/mock/discovery.mock.go +++ b/pkg/op/mock/discovery.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: DiscoverStorage) +// Source: github.com/zitadel/oidc/pkg/op (interfaces: DiscoverStorage) // Package mock is a generated GoMock package. package mock diff --git a/pkg/op/mock/generate.go b/pkg/op/mock/generate.go index 956cc70..543c6fd 100644 --- a/pkg/op/mock/generate.go +++ b/pkg/op/mock/generate.go @@ -1,9 +1,9 @@ package mock -//go:generate mockgen -package mock -destination ./storage.mock.go github.com/caos/oidc/pkg/op Storage -//go:generate mockgen -package mock -destination ./authorizer.mock.go github.com/caos/oidc/pkg/op Authorizer -//go:generate mockgen -package mock -destination ./client.mock.go github.com/caos/oidc/pkg/op Client -//go:generate mockgen -package mock -destination ./configuration.mock.go github.com/caos/oidc/pkg/op Configuration -//go:generate mockgen -package mock -destination ./discovery.mock.go github.com/caos/oidc/pkg/op DiscoverStorage -//go:generate mockgen -package mock -destination ./signer.mock.go github.com/caos/oidc/pkg/op SigningKey,Key -//go:generate mockgen -package mock -destination ./key.mock.go github.com/caos/oidc/pkg/op KeyProvider +//go:generate mockgen -package mock -destination ./storage.mock.go github.com/zitadel/oidc/pkg/op Storage +//go:generate mockgen -package mock -destination ./authorizer.mock.go github.com/zitadel/oidc/pkg/op Authorizer +//go:generate mockgen -package mock -destination ./client.mock.go github.com/zitadel/oidc/pkg/op Client +//go:generate mockgen -package mock -destination ./configuration.mock.go github.com/zitadel/oidc/pkg/op Configuration +//go:generate mockgen -package mock -destination ./discovery.mock.go github.com/zitadel/oidc/pkg/op DiscoverStorage +//go:generate mockgen -package mock -destination ./signer.mock.go github.com/zitadel/oidc/pkg/op SigningKey,Key +//go:generate mockgen -package mock -destination ./key.mock.go github.com/zitadel/oidc/pkg/op KeyProvider diff --git a/pkg/op/mock/key.mock.go b/pkg/op/mock/key.mock.go index 89def29..a72f412 100644 --- a/pkg/op/mock/key.mock.go +++ b/pkg/op/mock/key.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: KeyProvider) +// Source: github.com/zitadel/oidc/pkg/op (interfaces: KeyProvider) // Package mock is a generated GoMock package. package mock @@ -10,7 +10,7 @@ import ( gomock "github.com/golang/mock/gomock" - op "github.com/caos/oidc/v2/pkg/op" + op "github.com/zitadel/oidc/v2/pkg/op" ) // MockKeyProvider is a mock of KeyProvider interface. diff --git a/pkg/op/mock/signer.mock.go b/pkg/op/mock/signer.mock.go index 1ebb349..35fd8dd 100644 --- a/pkg/op/mock/signer.mock.go +++ b/pkg/op/mock/signer.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: SigningKey,Key) +// Source: github.com/zitadel/oidc/pkg/op (interfaces: SigningKey,Key) // Package mock is a generated GoMock package. package mock diff --git a/pkg/op/mock/storage.mock.go b/pkg/op/mock/storage.mock.go index 097fb98..db38abb 100644 --- a/pkg/op/mock/storage.mock.go +++ b/pkg/op/mock/storage.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/caos/oidc/pkg/op (interfaces: Storage) +// Source: github.com/zitadel/oidc/pkg/op (interfaces: Storage) // Package mock is a generated GoMock package. package mock @@ -12,8 +12,8 @@ import ( gomock "github.com/golang/mock/gomock" jose "gopkg.in/square/go-jose.v2" - oidc "github.com/caos/oidc/v2/pkg/oidc" - op "github.com/caos/oidc/v2/pkg/op" + oidc "github.com/zitadel/oidc/v2/pkg/oidc" + op "github.com/zitadel/oidc/v2/pkg/op" ) // MockStorage is a mock of Storage interface. diff --git a/pkg/op/mock/storage.mock.impl.go b/pkg/op/mock/storage.mock.impl.go index e92a568..fabf18a 100644 --- a/pkg/op/mock/storage.mock.impl.go +++ b/pkg/op/mock/storage.mock.impl.go @@ -8,8 +8,8 @@ import ( "github.com/golang/mock/gomock" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/op" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/op" ) func NewStorage(t *testing.T) op.Storage { diff --git a/pkg/op/op.go b/pkg/op/op.go index 5e737a6..0ff2d31 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -12,8 +12,8 @@ import ( "golang.org/x/text/language" "gopkg.in/square/go-jose.v2" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) const ( diff --git a/pkg/op/probes.go b/pkg/op/probes.go index c5e5b57..a56c92b 100644 --- a/pkg/op/probes.go +++ b/pkg/op/probes.go @@ -5,7 +5,7 @@ import ( "errors" "net/http" - httphelper "github.com/caos/oidc/v2/pkg/http" + httphelper "github.com/zitadel/oidc/v2/pkg/http" ) type ProbesFn func(context.Context) error diff --git a/pkg/op/session.go b/pkg/op/session.go index f727232..e45b859 100644 --- a/pkg/op/session.go +++ b/pkg/op/session.go @@ -4,8 +4,8 @@ import ( "context" "net/http" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type SessionEnder interface { diff --git a/pkg/op/storage.go b/pkg/op/storage.go index 3fb239f..7bd6846 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -6,7 +6,7 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type AuthStorage interface { diff --git a/pkg/op/token.go b/pkg/op/token.go index 439b93e..3ca2d7a 100644 --- a/pkg/op/token.go +++ b/pkg/op/token.go @@ -4,9 +4,9 @@ import ( "context" "time" - "github.com/caos/oidc/v2/pkg/crypto" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/strings" + "github.com/zitadel/oidc/v2/pkg/crypto" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/strings" ) type TokenCreator interface { diff --git a/pkg/op/token_code.go b/pkg/op/token_code.go index cac5197..9b85c5b 100644 --- a/pkg/op/token_code.go +++ b/pkg/op/token_code.go @@ -4,8 +4,8 @@ import ( "context" "net/http" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) //CodeExchange handles the OAuth 2.0 authorization_code grant, including diff --git a/pkg/op/token_intospection.go b/pkg/op/token_intospection.go index 5ef50f8..dfc8954 100644 --- a/pkg/op/token_intospection.go +++ b/pkg/op/token_intospection.go @@ -6,8 +6,8 @@ import ( "net/http" "net/url" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type Introspector interface { diff --git a/pkg/op/token_jwt_profile.go b/pkg/op/token_jwt_profile.go index 4431c74..1c18660 100644 --- a/pkg/op/token_jwt_profile.go +++ b/pkg/op/token_jwt_profile.go @@ -5,8 +5,8 @@ import ( "net/http" "time" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type JWTAuthorizationGrantExchanger interface { diff --git a/pkg/op/token_refresh.go b/pkg/op/token_refresh.go index f82c75c..1d58cc7 100644 --- a/pkg/op/token_refresh.go +++ b/pkg/op/token_refresh.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" - "github.com/caos/oidc/v2/pkg/strings" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/strings" ) type RefreshTokenRequest interface { diff --git a/pkg/op/token_request.go b/pkg/op/token_request.go index cdc44b1..f7d9b93 100644 --- a/pkg/op/token_request.go +++ b/pkg/op/token_request.go @@ -5,8 +5,8 @@ import ( "net/http" "net/url" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type Exchanger interface { diff --git a/pkg/op/token_revocation.go b/pkg/op/token_revocation.go index 1372684..2c4bf5a 100644 --- a/pkg/op/token_revocation.go +++ b/pkg/op/token_revocation.go @@ -6,8 +6,8 @@ import ( "net/url" "strings" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type Revoker interface { diff --git a/pkg/op/userinfo.go b/pkg/op/userinfo.go index 7af6e93..cb8f0ae 100644 --- a/pkg/op/userinfo.go +++ b/pkg/op/userinfo.go @@ -6,8 +6,8 @@ import ( "net/http" "strings" - httphelper "github.com/caos/oidc/v2/pkg/http" - "github.com/caos/oidc/v2/pkg/oidc" + httphelper "github.com/zitadel/oidc/v2/pkg/http" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type UserinfoProvider interface { diff --git a/pkg/op/verifier_access_token.go b/pkg/op/verifier_access_token.go index fa295f7..c1b23cf 100644 --- a/pkg/op/verifier_access_token.go +++ b/pkg/op/verifier_access_token.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type AccessTokenVerifier interface { diff --git a/pkg/op/verifier_id_token_hint.go b/pkg/op/verifier_id_token_hint.go index 302375c..79fb459 100644 --- a/pkg/op/verifier_id_token_hint.go +++ b/pkg/op/verifier_id_token_hint.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type IDTokenHintVerifier interface { diff --git a/pkg/op/verifier_jwt_profile.go b/pkg/op/verifier_jwt_profile.go index 403bf17..e9d1eb1 100644 --- a/pkg/op/verifier_jwt_profile.go +++ b/pkg/op/verifier_jwt_profile.go @@ -8,7 +8,7 @@ import ( "gopkg.in/square/go-jose.v2" - "github.com/caos/oidc/v2/pkg/oidc" + "github.com/zitadel/oidc/v2/pkg/oidc" ) type JWTProfileVerifier interface { From f345ddd0c599da0bccb3b1c87c7233102e8c1b5e Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 17 Jun 2022 09:33:30 +0200 Subject: [PATCH 10/15] fix: add state in access token response (implicit flow) --- pkg/oidc/token.go | 1 + pkg/op/token.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/pkg/oidc/token.go b/pkg/oidc/token.go index c621c78..29d502e 100644 --- a/pkg/oidc/token.go +++ b/pkg/oidc/token.go @@ -396,6 +396,7 @@ type AccessTokenResponse struct { RefreshToken string `json:"refresh_token,omitempty" schema:"refresh_token,omitempty"` ExpiresIn uint64 `json:"expires_in,omitempty" schema:"expires_in,omitempty"` IDToken string `json:"id_token,omitempty" schema:"id_token,omitempty"` + State string `json:"state,omitempty" schema:"state,omitempty"` } type JWTProfileAssertionClaims interface { diff --git a/pkg/op/token.go b/pkg/op/token.go index 3ca2d7a..68e19d7 100644 --- a/pkg/op/token.go +++ b/pkg/op/token.go @@ -35,11 +35,13 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli return nil, err } + var state string if authRequest, ok := request.(AuthRequest); ok { err = creator.Storage().DeleteAuthRequest(ctx, authRequest.GetID()) if err != nil { return nil, err } + state = authRequest.GetState() } exp := uint64(validity.Seconds()) @@ -49,6 +51,7 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli RefreshToken: newRefreshToken, TokenType: oidc.BearerToken, ExpiresIn: exp, + State: state, }, nil } From 9472f2a0095b3e2c15bc342237a5c68c1e4425e0 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 17 Jun 2022 13:01:20 +0200 Subject: [PATCH 11/15] fix: encode auth response correctly (when using query in redirect uri) --- pkg/http/http.go | 7 +++---- pkg/op/auth_request.go | 35 +++++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/pkg/http/http.go b/pkg/http/http.go index 2512707..b0d1ef7 100644 --- a/pkg/http/http.go +++ b/pkg/http/http.go @@ -77,14 +77,13 @@ func HttpRequest(client *http.Client, req *http.Request, response interface{}) e return nil } -func URLEncodeResponse(resp interface{}, encoder Encoder) (string, error) { +func URLEncodeParams(resp interface{}, encoder Encoder) (url.Values, error) { values := make(map[string][]string) err := encoder.Encode(resp, values) if err != nil { - return "", err + return nil, err } - v := url.Values(values) - return v.Encode(), nil + return values, nil } func StartServer(ctx context.Context, port string) { diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index bf72721..4234878 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -464,18 +464,41 @@ func BuildAuthRequestCode(authReq AuthRequest, crypto Crypto) (string, error) { //AuthResponseURL encodes the authorization response (successful and error) and sets it as query or fragment values //depending on the response_mode and response_type func AuthResponseURL(redirectURI string, responseType oidc.ResponseType, responseMode oidc.ResponseMode, response interface{}, encoder httphelper.Encoder) (string, error) { - params, err := httphelper.URLEncodeResponse(response, encoder) + uri, err := url.Parse(redirectURI) if err != nil { return "", oidc.ErrServerError().WithParent(err) } + params, err := httphelper.URLEncodeParams(response, encoder) + if err != nil { + return "", oidc.ErrServerError().WithParent(err) + } + //return explicitly requested mode if responseMode == oidc.ResponseModeQuery { - return redirectURI + "?" + params, nil + return mergeQueryParams(uri, params), nil } if responseMode == oidc.ResponseModeFragment { - return redirectURI + "#" + params, nil + return setFragment(uri, params), nil } - if responseType == "" || responseType == oidc.ResponseTypeCode { - return redirectURI + "?" + params, nil + //implicit must use fragment mode is not specified by client + if responseType == oidc.ResponseTypeIDToken || responseType == oidc.ResponseTypeIDTokenOnly { + return setFragment(uri, params), nil } - return redirectURI + "#" + params, nil + //if we get here it's code flow: defaults to query + return mergeQueryParams(uri, params), nil +} + +func setFragment(uri *url.URL, params url.Values) string { + uri.Fragment = params.Encode() + return uri.String() +} + +func mergeQueryParams(uri *url.URL, params url.Values) string { + queries := uri.Query() + for param, values := range params { + for _, value := range values { + queries.Set(param, value) + } + } + uri.RawQuery = queries.Encode() + return uri.String() } From eee4f9b32b3b545fa0136404d7dadda9afe59a8e Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 17 Jun 2022 14:56:16 +0200 Subject: [PATCH 12/15] fix query param handling --- pkg/op/auth_request.go | 2 +- pkg/op/auth_request_test.go | 84 +++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index 4234878..01b71fd 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -496,7 +496,7 @@ func mergeQueryParams(uri *url.URL, params url.Values) string { queries := uri.Query() for param, values := range params { for _, value := range values { - queries.Set(param, value) + queries.Add(param, value) } } uri.RawQuery = queries.Encode() diff --git a/pkg/op/auth_request_test.go b/pkg/op/auth_request_test.go index c209126..c35d584 100644 --- a/pkg/op/auth_request_test.go +++ b/pkg/op/auth_request_test.go @@ -793,6 +793,90 @@ func TestAuthResponseURL(t *testing.T) { nil, }, }, + { + "with query", + args{ + "uri?param=value", + oidc.ResponseTypeCode, + "", + map[string][]string{"test": {"test"}}, + &mockEncoder{}, + }, + res{ + "uri?param=value&test=test", + nil, + }, + }, + { + "with query response type id token", + args{ + "uri?param=value", + oidc.ResponseTypeIDToken, + "", + map[string][]string{"test": {"test"}}, + &mockEncoder{}, + }, + res{ + "uri?param=value#test=test", + nil, + }, + }, + { + "with existing query", + args{ + "uri?test=value", + oidc.ResponseTypeCode, + "", + map[string][]string{"test": {"test"}}, + &mockEncoder{}, + }, + res{ + "uri?test=value&test=test", + nil, + }, + }, + { + "with existing query response type id token", + args{ + "uri?test=value", + oidc.ResponseTypeIDToken, + "", + map[string][]string{"test": {"test"}}, + &mockEncoder{}, + }, + res{ + "uri?test=value#test=test", + nil, + }, + }, + { + "with existing query and multiple values", + args{ + "uri?test=value", + oidc.ResponseTypeCode, + "", + map[string][]string{"test": {"test", "test2"}}, + &mockEncoder{}, + }, + res{ + "uri?test=value&test=test&test=test2", + nil, + }, + }, + { + "with existing query and multiple values response type id token", + args{ + "uri?test=value", + oidc.ResponseTypeIDToken, + "", + map[string][]string{"test": {"test", "test2"}}, + &mockEncoder{}, + }, + res{ + "uri?test=value#test=test&test=test2", + nil, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From faca98d28d3acb0dde9b2a6ef825b05743d7c53b Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Thu, 21 Jul 2022 09:34:14 +0200 Subject: [PATCH 13/15] feat: add all optional claims of the introspection response --- pkg/oidc/introspection.go | 86 +++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/pkg/oidc/introspection.go b/pkg/oidc/introspection.go index 6ac2986..33ba2cb 100644 --- a/pkg/oidc/introspection.go +++ b/pkg/oidc/introspection.go @@ -19,10 +19,17 @@ type ClientAssertionParams struct { type IntrospectionResponse interface { UserInfoSetter - SetActive(bool) IsActive() bool + SetActive(bool) SetScopes(scopes []string) SetClientID(id string) + SetTokenType(tokenType string) + SetExpiration(exp time.Time) + SetIssuedAt(iat time.Time) + SetNotBefore(nbf time.Time) + SetAudience(audience []string) + SetIssuer(issuer string) + SetJWTID(id string) } func NewIntrospectionResponse() IntrospectionResponse { @@ -30,10 +37,17 @@ func NewIntrospectionResponse() IntrospectionResponse { } type introspectionResponse struct { - Active bool `json:"active"` - Scope SpaceDelimitedArray `json:"scope,omitempty"` - ClientID string `json:"client_id,omitempty"` - Subject string `json:"sub,omitempty"` + Active bool `json:"active"` + Scope SpaceDelimitedArray `json:"scope,omitempty"` + ClientID string `json:"client_id,omitempty"` + TokenType string `json:"token_type,omitempty"` + Expiration Time `json:"exp,omitempty"` + IssuedAt Time `json:"iat,omitempty"` + NotBefore Time `json:"nbf,omitempty"` + Subject string `json:"sub,omitempty"` + Audience Audience `json:"aud,omitempty"` + Issuer string `json:"iss,omitempty"` + JWTID string `json:"jti,omitempty"` userInfoProfile userInfoEmail userInfoPhone @@ -46,14 +60,6 @@ func (i *introspectionResponse) IsActive() bool { return i.Active } -func (i *introspectionResponse) SetScopes(scope []string) { - i.Scope = scope -} - -func (i *introspectionResponse) SetClientID(id string) { - i.ClientID = id -} - func (i *introspectionResponse) GetSubject() string { return i.Subject } @@ -138,6 +144,42 @@ func (i *introspectionResponse) SetActive(active bool) { i.Active = active } +func (i *introspectionResponse) SetScopes(scope []string) { + i.Scope = scope +} + +func (i *introspectionResponse) SetClientID(id string) { + i.ClientID = id +} + +func (i *introspectionResponse) SetTokenType(tokenType string) { + i.TokenType = tokenType +} + +func (i *introspectionResponse) SetExpiration(exp time.Time) { + i.Expiration = Time(exp) +} + +func (i *introspectionResponse) SetIssuedAt(iat time.Time) { + i.IssuedAt = Time(iat) +} + +func (i *introspectionResponse) SetNotBefore(nbf time.Time) { + i.NotBefore = Time(nbf) +} + +func (i *introspectionResponse) SetAudience(audience []string) { + i.Audience = audience +} + +func (i *introspectionResponse) SetIssuer(issuer string) { + i.Issuer = issuer +} + +func (i *introspectionResponse) SetJWTID(id string) { + i.JWTID = id +} + func (i *introspectionResponse) SetSubject(sub string) { i.Subject = sub } @@ -223,9 +265,12 @@ func (i *introspectionResponse) MarshalJSON() ([]byte, error) { type Alias introspectionResponse a := &struct { *Alias - Locale interface{} `json:"locale,omitempty"` - UpdatedAt int64 `json:"updated_at,omitempty"` - Username string `json:"username,omitempty"` + Expiration int64 `json:"exp,omitempty"` + IssuedAt int64 `json:"iat,omitempty"` + NotBefore int64 `json:"nbf,omitempty"` + Locale interface{} `json:"locale,omitempty"` + UpdatedAt int64 `json:"updated_at,omitempty"` + Username string `json:"username,omitempty"` }{ Alias: (*Alias)(i), } @@ -235,6 +280,15 @@ func (i *introspectionResponse) MarshalJSON() ([]byte, error) { if !time.Time(i.UpdatedAt).IsZero() { a.UpdatedAt = time.Time(i.UpdatedAt).Unix() } + if !time.Time(i.Expiration).IsZero() { + a.Expiration = time.Time(i.Expiration).Unix() + } + if !time.Time(i.IssuedAt).IsZero() { + a.IssuedAt = time.Time(i.IssuedAt).Unix() + } + if !time.Time(i.NotBefore).IsZero() { + a.NotBefore = time.Time(i.NotBefore).Unix() + } a.Username = i.PreferredUsername b, err := json.Marshal(a) From f974fbaa7b1e7ec56fbacee861a9209360fae364 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Mon, 25 Jul 2022 14:49:41 +0200 Subject: [PATCH 14/15] fix: use default redirect uri when not passed --- pkg/oidc/session.go | 1 + pkg/op/session.go | 67 ++++++++++++++++++++++++++++----------------- pkg/op/storage.go | 2 +- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/pkg/oidc/session.go b/pkg/oidc/session.go index d6735b4..9aa70c1 100644 --- a/pkg/oidc/session.go +++ b/pkg/oidc/session.go @@ -4,6 +4,7 @@ package oidc //https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout type EndSessionRequest struct { IdTokenHint string `schema:"id_token_hint"` + ClientID string `schema:"client_id"` PostLogoutRedirectURI string `schema:"post_logout_redirect_uri"` State string `schema:"state"` } diff --git a/pkg/op/session.go b/pkg/op/session.go index e45b859..e1cc595 100644 --- a/pkg/op/session.go +++ b/pkg/op/session.go @@ -3,6 +3,7 @@ package op import ( "context" "net/http" + "net/url" httphelper "github.com/zitadel/oidc/v2/pkg/http" "github.com/zitadel/oidc/v2/pkg/oidc" @@ -32,11 +33,7 @@ func EndSession(w http.ResponseWriter, r *http.Request, ender SessionEnder) { RequestError(w, r, err) return } - var clientID string - if session.Client != nil { - clientID = session.Client.GetID() - } - err = ender.Storage().TerminateSession(r.Context(), session.UserID, clientID) + err = ender.Storage().TerminateSession(r.Context(), session.UserID, session.ClientID) if err != nil { RequestError(w, r, oidc.DefaultToServerError(err, "error terminating session")) return @@ -58,28 +55,48 @@ func ParseEndSessionRequest(r *http.Request, decoder httphelper.Decoder) (*oidc. } func ValidateEndSessionRequest(ctx context.Context, req *oidc.EndSessionRequest, ender SessionEnder) (*EndSessionRequest, error) { - session := new(EndSessionRequest) - if req.IdTokenHint == "" { - return session, nil + session := &EndSessionRequest{ + RedirectURI: ender.DefaultLogoutRedirectURI(), } - claims, err := VerifyIDTokenHint(ctx, req.IdTokenHint, ender.IDTokenHintVerifier(ctx)) - if err != nil { - return nil, oidc.ErrInvalidRequest().WithDescription("id_token_hint invalid").WithParent(err) + if req.IdTokenHint != "" { + claims, err := VerifyIDTokenHint(ctx, req.IdTokenHint, ender.IDTokenHintVerifier(ctx)) + if err != nil { + return nil, oidc.ErrInvalidRequest().WithDescription("id_token_hint invalid").WithParent(err) + } + session.UserID = claims.GetSubject() + if req.ClientID != "" && req.ClientID != claims.GetAuthorizedParty() { + return nil, oidc.ErrInvalidRequest().WithDescription("client_id does not match azp of id_token_hint") + } + req.ClientID = claims.GetAuthorizedParty() } - session.UserID = claims.GetSubject() - session.Client, err = ender.Storage().GetClientByClientID(ctx, claims.GetAuthorizedParty()) - if err != nil { - return nil, oidc.DefaultToServerError(err, "") - } - if req.PostLogoutRedirectURI == "" { - session.RedirectURI = ender.DefaultLogoutRedirectURI() - return session, nil - } - for _, uri := range session.Client.PostLogoutRedirectURIs() { - if uri == req.PostLogoutRedirectURI { - session.RedirectURI = uri + "?state=" + req.State - return session, nil + if req.ClientID != "" { + client, err := ender.Storage().GetClientByClientID(ctx, req.ClientID) + if err != nil { + return nil, oidc.DefaultToServerError(err, "") + } + session.ClientID = client.GetID() + if req.PostLogoutRedirectURI != "" { + if err := ValidateEndSessionPostLogoutRedirectURI(req.PostLogoutRedirectURI, client); err != nil { + return nil, err + } + session.RedirectURI = req.PostLogoutRedirectURI } } - return nil, oidc.ErrInvalidRequest().WithDescription("post_logout_redirect_uri invalid") + if req.State != "" { + redirect, err := url.Parse(session.RedirectURI) + if err != nil { + return nil, oidc.DefaultToServerError(err, "") + } + session.RedirectURI = mergeQueryParams(redirect, url.Values{"state": {req.State}}) + } + return session, nil +} + +func ValidateEndSessionPostLogoutRedirectURI(postLogoutRedirectURI string, client Client) error { + for _, uri := range client.PostLogoutRedirectURIs() { + if uri == postLogoutRedirectURI { + return nil + } + } + return oidc.ErrInvalidRequest().WithDescription("post_logout_redirect_uri invalid") } diff --git a/pkg/op/storage.go b/pkg/op/storage.go index 7bd6846..b15bec0 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -51,6 +51,6 @@ type StorageNotFoundError interface { type EndSessionRequest struct { UserID string - Client Client + ClientID string RedirectURI string } From 2574ebc6e73d1f5ee829a61333757481797c29dd Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Mon, 9 Jan 2023 10:39:21 +0100 Subject: [PATCH 15/15] fix: exchange cors library and add `X-Requested-With` to Access-Control-Request-Headers (#261) --- go.mod | 9 +- go.sum | 380 +++++---------------------------------------------- pkg/op/op.go | 37 +++-- 3 files changed, 67 insertions(+), 359 deletions(-) diff --git a/go.mod b/go.mod index 482ad7b..e9281e2 100644 --- a/go.mod +++ b/go.mod @@ -4,19 +4,18 @@ go 1.15 require ( github.com/golang/mock v1.6.0 - github.com/google/go-cmp v0.5.2 // indirect github.com/google/go-github/v31 v31.0.0 github.com/google/uuid v1.3.0 - github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/schema v1.2.0 github.com/gorilla/securecookie v1.1.1 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/rs/cors v1.8.3 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.1 - golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 - golang.org/x/sys v0.0.0-20220207234003-57398862261d // indirect - golang.org/x/text v0.3.7 + golang.org/x/net v0.4.0 // indirect + golang.org/x/oauth2 v0.3.0 + golang.org/x/text v0.5.0 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go.sum b/go.sum index 58bdbae..faa0b23 100644 --- a/go.sum +++ b/go.sum @@ -1,127 +1,29 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v31 v31.0.0 h1:JJUxlP9lFK+ziXKimTCprajMApV1ecWD4NB6CCb0plo= github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -129,286 +31,78 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo= +github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8= +golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220207234003-57398862261d h1:Bm7BNOQt2Qv7ZqysjeLjgCBanX+88Z/OtdvsrEv1Djc= -golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -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.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -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= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/op/op.go b/pkg/op/op.go index 0ff2d31..d41a602 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/gorilla/schema" + "github.com/rs/cors" "golang.org/x/text/language" "gopkg.in/square/go-jose.v2" @@ -39,6 +39,30 @@ var ( EndSession: NewEndpoint(defaultEndSessionEndpoint), JwksURI: NewEndpoint(defaultKeysEndpoint), } + + defaultCORSOptions = cors.Options{ + AllowCredentials: true, + AllowedHeaders: []string{ + "Origin", + "Accept", + "Accept-Language", + "Authorization", + "Content-Type", + "X-Requested-With", + }, + AllowedMethods: []string{ + http.MethodGet, + http.MethodHead, + http.MethodPost, + }, + ExposedHeaders: []string{ + "Location", + "Content-Length", + }, + AllowOriginFunc: func(_ string) bool { + return true + }, + } ) type OpenIDProvider interface { @@ -56,10 +80,6 @@ type OpenIDProvider interface { type HttpInterceptor func(http.Handler) http.Handler -var allowAllOrigins = func(_ string) bool { - return true -} - func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router { router := mux.NewRouter() router.Use(intercept(o.IssuerFromRequest, interceptors...)) @@ -425,16 +445,11 @@ func WithHttpInterceptors(interceptors ...HttpInterceptor) Option { } func intercept(i IssuerFromRequest, interceptors ...HttpInterceptor) func(handler http.Handler) http.Handler { - cors := handlers.CORS( - handlers.AllowCredentials(), - handlers.AllowedHeaders([]string{"authorization", "content-type"}), - handlers.AllowedOriginValidator(allowAllOrigins), - ) issuerInterceptor := NewIssuerInterceptor(i) return func(handler http.Handler) http.Handler { for i := len(interceptors) - 1; i >= 0; i-- { handler = interceptors[i](handler) } - return cors(issuerInterceptor.Handler(handler)) + return cors.New(defaultCORSOptions).Handler(issuerInterceptor.Handler(handler)) } }