feat(op): dynamic issuer depending on request / host (#278)
* 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 * move example * fix examples * fix mocks * update readme * fix examples and update usage * update go module version to v2 * build branch * fix(module): rename caos to zitadel * fix: add state in access token response (implicit flow) * fix: encode auth response correctly (when using query in redirect uri) * fix query param handling * feat: add all optional claims of the introspection response * fix: use default redirect uri when not passed * fix: exchange cors library and add `X-Requested-With` to Access-Control-Request-Headers (#261) * feat(op): add support for client credentials * fix mocks and test * feat: allow to specify token type of JWT Profile Grant * document JWTProfileTokenStorage * cleanup * rp: fix integration test test username needed to be suffixed by issuer domain * chore(deps): bump golang.org/x/text from 0.5.0 to 0.6.0 Bumps [golang.org/x/text](https://github.com/golang/text) from 0.5.0 to 0.6.0. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.5.0...v0.6.0) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * op: mock: cleanup commented code * op: remove duplicate code code duplication caused by merge conflict selections --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: adlerhurst <silvan.reusser@gmail.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
a34d7a1630
commit
1165d88c69
77 changed files with 2349 additions and 913 deletions
|
@ -11,9 +11,9 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
str "github.com/zitadel/oidc/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 {
|
||||
|
@ -38,10 +38,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
|
||||
}
|
||||
|
||||
|
@ -72,8 +70,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
|
||||
|
@ -91,7 +90,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
|
||||
|
@ -100,12 +99,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
|
||||
|
|
|
@ -13,10 +13,10 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/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"
|
||||
)
|
||||
|
||||
//
|
||||
|
|
|
@ -3,7 +3,7 @@ package op
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
//go:generate go get github.com/dmarkham/enumer
|
||||
|
|
|
@ -2,20 +2,24 @@ package op
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
const (
|
||||
OidcDevMode = "ZITADEL_OIDC_DEV"
|
||||
// deprecated: use OidcDevMode (ZITADEL_OIDC_DEV=true)
|
||||
devMode = "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
|
||||
|
@ -42,36 +46,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 {
|
||||
// check the old / current env var as well
|
||||
_, b = os.LookupEnv(devMode)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
49
pkg/op/context.go
Normal file
49
pkg/op/context.go
Normal file
|
@ -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)
|
||||
}
|
76
pkg/op/context_test.go
Normal file
76
pkg/op/context_test.go
Normal file
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package op
|
||||
|
||||
import (
|
||||
"github.com/zitadel/oidc/pkg/crypto"
|
||||
"github.com/zitadel/oidc/v2/pkg/crypto"
|
||||
)
|
||||
|
||||
type Crypto interface {
|
||||
|
|
|
@ -1,49 +1,17 @@
|
|||
package op
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/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
|
||||
}
|
||||
|
@ -87,6 +95,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",
|
||||
|
@ -116,59 +206,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() {
|
||||
|
@ -176,24 +213,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()
|
||||
}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
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"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/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) {
|
||||
|
@ -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,51 @@ 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)
|
||||
c.EXPECT().GrantTypeClientCredentialsSupported().Return(false)
|
||||
return c
|
||||
}(),
|
||||
},
|
||||
[]oidc.GrantType{
|
||||
oidc.GrantTypeCode,
|
||||
oidc.GrantTypeImplicit,
|
||||
},
|
||||
},
|
||||
{
|
||||
"code, implicit flow, refresh token, token exchange, jwt profile, client_credentials",
|
||||
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)
|
||||
c.EXPECT().GrantTypeClientCredentialsSupported().Return(true)
|
||||
return c
|
||||
}(),
|
||||
},
|
||||
[]oidc.GrantType{
|
||||
oidc.GrantTypeCode,
|
||||
oidc.GrantTypeImplicit,
|
||||
oidc.GrantTypeRefreshToken,
|
||||
oidc.GrantTypeClientCredentials,
|
||||
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 +186,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 +306,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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package op_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
)
|
||||
|
||||
func TestEndpoint_Path(t *testing.T) {
|
||||
|
|
|
@ -3,8 +3,8 @@ package op
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
type ErrAuthRequest interface {
|
||||
|
|
|
@ -6,11 +6,11 @@ import (
|
|||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
)
|
||||
|
||||
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}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/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) {
|
||||
|
@ -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"}]}
|
||||
`,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/oidc/pkg/op (interfaces: Authorizer)
|
||||
// Source: github.com/zitadel/oidc/v2/pkg/op (interfaces: Authorizer)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
http "github.com/zitadel/oidc/pkg/http"
|
||||
op "github.com/zitadel/oidc/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.
|
||||
|
@ -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()
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/gorilla/schema"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
)
|
||||
|
||||
func NewAuthorizer(t *testing.T) op.Authorizer {
|
||||
|
@ -20,23 +20,13 @@ 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)
|
||||
return m
|
||||
}
|
||||
|
||||
// func NewAuthorizerExpectDecoderFails(t *testing.T) op.Authorizer {
|
||||
// m := NewAuthorizer(t)
|
||||
// ExpectDecoderFails(m)
|
||||
// ExpectEncoder(m)
|
||||
// ExpectSigner(m, t)
|
||||
// ExpectStorage(m, t)
|
||||
// ExpectErrorHandler(m, t)
|
||||
// return m
|
||||
// }
|
||||
|
||||
func ExpectDecoder(a op.Authorizer) {
|
||||
mockA := a.(*MockAuthorizer)
|
||||
mockA.EXPECT().Decoder().AnyTimes().Return(schema.NewDecoder())
|
||||
|
@ -47,17 +37,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)
|
||||
})
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
)
|
||||
|
||||
func NewClient(t *testing.T) op.Client {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/oidc/pkg/op (interfaces: Client)
|
||||
// Source: github.com/zitadel/oidc/v2/pkg/op (interfaces: Client)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
@ -9,8 +9,8 @@ import (
|
|||
time "time"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
oidc "github.com/zitadel/oidc/pkg/oidc"
|
||||
op "github.com/zitadel/oidc/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.
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/oidc/pkg/op (interfaces: Configuration)
|
||||
// Source: github.com/zitadel/oidc/v2/pkg/op (interfaces: Configuration)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
||||
import (
|
||||
http "net/http"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
op "github.com/zitadel/oidc/pkg/op"
|
||||
op "github.com/zitadel/oidc/v2/pkg/op"
|
||||
language "golang.org/x/text/language"
|
||||
)
|
||||
|
||||
|
@ -161,6 +162,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()
|
||||
|
@ -203,18 +218,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.
|
||||
|
|
51
pkg/op/mock/discovery.mock.go
Normal file
51
pkg/op/mock/discovery.mock.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/oidc/v2/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)
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
package mock
|
||||
|
||||
//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 ./signer.mock.go github.com/zitadel/oidc/pkg/op Signer
|
||||
//go:generate mockgen -package mock -destination ./key.mock.go github.com/zitadel/oidc/pkg/op KeyProvider
|
||||
//go:generate mockgen -package mock -destination ./storage.mock.go github.com/zitadel/oidc/v2/pkg/op Storage
|
||||
//go:generate mockgen -package mock -destination ./authorizer.mock.go github.com/zitadel/oidc/v2/pkg/op Authorizer
|
||||
//go:generate mockgen -package mock -destination ./client.mock.go github.com/zitadel/oidc/v2/pkg/op Client
|
||||
//go:generate mockgen -package mock -destination ./configuration.mock.go github.com/zitadel/oidc/v2/pkg/op Configuration
|
||||
//go:generate mockgen -package mock -destination ./discovery.mock.go github.com/zitadel/oidc/v2/pkg/op DiscoverStorage
|
||||
//go:generate mockgen -package mock -destination ./signer.mock.go github.com/zitadel/oidc/v2/pkg/op SigningKey,Key
|
||||
//go:generate mockgen -package mock -destination ./key.mock.go github.com/zitadel/oidc/v2/pkg/op KeyProvider
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/oidc/pkg/op (interfaces: KeyProvider)
|
||||
// Source: github.com/zitadel/oidc/v2/pkg/op (interfaces: KeyProvider)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
@ -9,7 +9,7 @@ import (
|
|||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
jose "gopkg.in/square/go-jose.v2"
|
||||
op "github.com/zitadel/oidc/v2/pkg/op"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -1,56 +1,69 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/oidc/pkg/op (interfaces: Signer)
|
||||
// Source: github.com/zitadel/oidc/v2/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 {
|
||||
// ID mocks base method.
|
||||
func (m *MockSigningKey) ID() string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Health", arg0)
|
||||
ret0, _ := ret[0].(error)
|
||||
ret := m.ctrl.Call(m, "ID")
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Health indicates an expected call of Health.
|
||||
func (mr *MockSignerMockRecorder) Health(arg0 interface{}) *gomock.Call {
|
||||
// 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, "Health", reflect.TypeOf((*MockSigner)(nil).Health), arg0)
|
||||
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()
|
||||
ret := m.ctrl.Call(m, "Key")
|
||||
ret0, _ := ret[0].(interface{})
|
||||
return ret0
|
||||
}
|
||||
|
||||
// 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, "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 +71,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))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/zitadel/oidc/pkg/op (interfaces: Storage)
|
||||
// Source: github.com/zitadel/oidc/v2/pkg/op (interfaces: Storage)
|
||||
|
||||
// Package mock is a generated GoMock package.
|
||||
package mock
|
||||
|
@ -10,8 +10,8 @@ import (
|
|||
time "time"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
oidc "github.com/zitadel/oidc/pkg/oidc"
|
||||
op "github.com/zitadel/oidc/pkg/op"
|
||||
oidc "github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
op "github.com/zitadel/oidc/v2/pkg/op"
|
||||
jose "gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
|
@ -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()
|
||||
|
|
|
@ -6,13 +6,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/op"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
)
|
||||
|
||||
func NewStorage(t *testing.T) op.Storage {
|
||||
|
@ -41,13 +38,13 @@ 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
|
||||
}
|
||||
|
||||
|
@ -85,24 +82,6 @@ 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")}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type ConfClient struct {
|
||||
id string
|
||||
appType op.ApplicationType
|
||||
|
|
312
pkg/op/op.go
312
pkg/op/op.go
|
@ -12,8 +12,8 @@ import (
|
|||
"golang.org/x/text/language"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -29,78 +29,79 @@ const (
|
|||
defaultKeysEndpoint = "keys"
|
||||
)
|
||||
|
||||
var DefaultEndpoints = &endpoints{
|
||||
Authorization: NewEndpoint(defaultAuthorizationEndpoint),
|
||||
Token: NewEndpoint(defaultTokenEndpoint),
|
||||
Introspection: NewEndpoint(defaultIntrospectEndpoint),
|
||||
Userinfo: NewEndpoint(defaultUserinfoEndpoint),
|
||||
Revocation: NewEndpoint(defaultRevocationEndpoint),
|
||||
EndSession: NewEndpoint(defaultEndSessionEndpoint),
|
||||
JwksURI: NewEndpoint(defaultKeysEndpoint),
|
||||
}
|
||||
var (
|
||||
DefaultEndpoints = &endpoints{
|
||||
Authorization: NewEndpoint(defaultAuthorizationEndpoint),
|
||||
Token: NewEndpoint(defaultTokenEndpoint),
|
||||
Introspection: NewEndpoint(defaultIntrospectEndpoint),
|
||||
Userinfo: NewEndpoint(defaultUserinfoEndpoint),
|
||||
Revocation: NewEndpoint(defaultRevocationEndpoint),
|
||||
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 {
|
||||
Configuration
|
||||
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
|
||||
}
|
||||
|
||||
type HttpInterceptor func(http.Handler) http.Handler
|
||||
|
||||
var 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
|
||||
},
|
||||
}
|
||||
|
||||
func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router {
|
||||
intercept := buildInterceptor(interceptors...)
|
||||
router := mux.NewRouter()
|
||||
router.Use(cors.New(defaultCORSOptions).Handler)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,6 @@ func authCallbackPath(o OpenIDProvider) string {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
Issuer string
|
||||
CryptoKey [32]byte
|
||||
DefaultLogoutRedirectURI string
|
||||
CodeMethodS256 bool
|
||||
|
@ -133,29 +133,34 @@ type endpoints struct {
|
|||
|
||||
// NewOpenIDProvider creates a provider. The provider provides (with HttpHandler())
|
||||
// a http.Router that handles a suite of endpoints (some paths can be overridden):
|
||||
// /healthz
|
||||
// /ready
|
||||
// /.well-known/openid-configuration
|
||||
// /oauth/token
|
||||
// /oauth/introspect
|
||||
// /callback
|
||||
// /authorize
|
||||
// /userinfo
|
||||
// /revoke
|
||||
// /end_session
|
||||
// /keys
|
||||
//
|
||||
// /healthz
|
||||
// /ready
|
||||
// /.well-known/openid-configuration
|
||||
// /oauth/token
|
||||
// /oauth/introspect
|
||||
// /callback
|
||||
// /authorize
|
||||
// /userinfo
|
||||
// /revoke
|
||||
// /end_session
|
||||
// /keys
|
||||
//
|
||||
// This does not include login. Login is handled with a redirect that includes the
|
||||
// request ID. The redirect for logins is specified per-client by Client.LoginURL().
|
||||
// Successful logins should mark the request as authorized and redirect back to to
|
||||
// op.AuthCallbackURL(provider) which is probably /callback. On the redirect back
|
||||
// to the AuthCallbackURL, the request id should be passed as the "id" parameter.
|
||||
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,
|
||||
|
@ -168,9 +173,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...)
|
||||
|
||||
|
@ -182,22 +188,17 @@ func NewOpenIDProvider(ctx context.Context, config *Config, storage Storage, opO
|
|||
o.crypto = NewAESCrypto(config.CryptoKey)
|
||||
|
||||
// Avoid potential race conditions by calling these early
|
||||
_ = o.AccessTokenVerifier() // sets accessTokenVerifier
|
||||
_ = o.IDTokenHintVerifier() // sets idTokenHintVerifier
|
||||
_ = o.JWTProfileVerifier() // sets jwtProfileVerifier
|
||||
_ = o.openIDKeySet() // sets keySet
|
||||
_ = o.openIDKeySet() // sets keySet
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
type openidProvider struct {
|
||||
type Provider struct {
|
||||
config *Config
|
||||
issuer IssuerFromRequest
|
||||
insecure bool
|
||||
endpoints *endpoints
|
||||
storage Storage
|
||||
signer Signer
|
||||
idTokenHintVerifier IDTokenHintVerifier
|
||||
jwtProfileVerifier JWTProfileVerifier
|
||||
accessTokenVerifier AccessTokenVerifier
|
||||
keySet *openIDKeySet
|
||||
crypto Crypto
|
||||
httpHandler http.Handler
|
||||
|
@ -209,159 +210,149 @@ type openidProvider struct {
|
|||
idTokenHintVerifierOpts []IDTokenHintVerifierOpt
|
||||
}
|
||||
|
||||
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) GrantTypeClientCredentialsSupported() bool {
|
||||
func (o *Provider) IntrospectionAuthMethodPrivateKeyJWTSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *Provider) IntrospectionEndpointSigningAlgorithmsSupported() []string {
|
||||
return []string{"RS256"}
|
||||
}
|
||||
|
||||
func (o *Provider) GrantTypeClientCredentialsSupported() bool {
|
||||
_, ok := o.storage.(ClientCredentialsStorage)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (o *openidProvider) IntrospectionAuthMethodPrivateKeyJWTSupported() bool {
|
||||
func (o *Provider) RevocationAuthMethodPrivateKeyJWTSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *openidProvider) IntrospectionEndpointSigningAlgorithmsSupported() []string {
|
||||
func (o *Provider) RevocationEndpointSigningAlgorithmsSupported() []string {
|
||||
return []string{"RS256"}
|
||||
}
|
||||
|
||||
func (o *openidProvider) RevocationAuthMethodPrivateKeyJWTSupported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *openidProvider) 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(), o.idTokenHintVerifierOpts...)
|
||||
}
|
||||
return o.idTokenHintVerifier
|
||||
func (o *Provider) IDTokenHintVerifier(ctx context.Context) IDTokenHintVerifier {
|
||||
return NewIDTokenHintVerifier(IssuerFromContext(ctx), o.openIDKeySet(), o.idTokenHintVerifierOpts...)
|
||||
}
|
||||
|
||||
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(), o.accessTokenVerifierOpts...)
|
||||
}
|
||||
return o.accessTokenVerifier
|
||||
func (o *Provider) AccessTokenVerifier(ctx context.Context) AccessTokenVerifier {
|
||||
return NewAccessTokenVerifier(IssuerFromContext(ctx), o.openIDKeySet(), o.accessTokenVerifierOpts...)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -372,22 +363,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
|
||||
}
|
||||
|
@ -397,7 +397,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
|
||||
}
|
||||
|
@ -407,7 +407,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
|
||||
}
|
||||
|
@ -417,7 +417,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
|
||||
}
|
||||
|
@ -427,7 +427,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
|
||||
}
|
||||
|
@ -437,7 +437,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
|
||||
}
|
||||
|
@ -447,7 +447,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
|
||||
}
|
||||
|
@ -457,7 +457,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
|
||||
|
@ -469,38 +469,32 @@ 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 WithAccessTokenVerifierOpts(opts ...AccessTokenVerifierOpt) Option {
|
||||
return func(o *openidProvider) error {
|
||||
return func(o *Provider) error {
|
||||
o.accessTokenVerifierOpts = opts
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithIDTokenHintVerifierOpts(opts ...IDTokenHintVerifierOpt) Option {
|
||||
return func(o *openidProvider) error {
|
||||
return func(o *Provider) error {
|
||||
o.idTokenHintVerifierOpts = opts
|
||||
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 {
|
||||
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.New(defaultCORSOptions).Handler(issuerInterceptor.Handler(handler))
|
||||
}
|
||||
}
|
||||
|
||||
func handlerFuncToHandler(handlerFunc http.HandlerFunc) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handlerFunc(w, r)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"errors"
|
||||
"net/http"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
)
|
||||
|
||||
type ProbesFn func(context.Context) error
|
||||
|
@ -31,15 +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 {
|
||||
|
|
|
@ -5,14 +5,14 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
type SessionEnder interface {
|
||||
Decoder() httphelper.Decoder
|
||||
Storage() Storage
|
||||
IDTokenHintVerifier() IDTokenHintVerifier
|
||||
IDTokenHintVerifier(context.Context) IDTokenHintVerifier
|
||||
DefaultLogoutRedirectURI() string
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ func ValidateEndSessionRequest(ctx context.Context, req *oidc.EndSessionRequest,
|
|||
RedirectURI: ender.DefaultLogoutRedirectURI(),
|
||||
}
|
||||
if req.IdTokenHint != "" {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -1,88 +1,38 @@
|
|||
package op
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/zitadel/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
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
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 {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
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.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
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{}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
type AuthStorage interface {
|
||||
|
@ -47,8 +47,14 @@ type AuthStorage interface {
|
|||
// tokenOrTokenID will be the refresh token, not its ID.
|
||||
RevokeToken(ctx context.Context, tokenOrTokenID 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 ClientCredentialsStorage interface {
|
||||
ClientCredentials(ctx context.Context, clientID, clientSecret string) (Client, error)
|
||||
ClientCredentialsTokenRequest(ctx context.Context, clientID string, scopes []string) (TokenRequest, error)
|
||||
}
|
||||
|
||||
// CanRefreshTokenInfo is an optional additional interface that Storage can support.
|
||||
|
@ -62,10 +68,6 @@ type CanRefreshTokenInfo interface {
|
|||
|
||||
var ErrInvalidRefreshToken = errors.New("invalid_refresh_token")
|
||||
|
||||
type ClientCredentialsStorage interface {
|
||||
ClientCredentialsTokenRequest(ctx context.Context, clientID string, scopes []string) (TokenRequest, error)
|
||||
}
|
||||
|
||||
type OPStorage interface {
|
||||
GetClientByClientID(ctx context.Context, clientID string) (Client, error)
|
||||
AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error
|
||||
|
@ -80,6 +82,12 @@ type OPStorage interface {
|
|||
ValidateJWTProfileScopes(ctx context.Context, userID string, scopes []string) ([]string, error)
|
||||
}
|
||||
|
||||
// JWTProfileTokenStorage is an additional, optional storage to implement
|
||||
// implementing it, allows specifying the [AccessTokenType] of the access_token returned form the JWT Profile TokenRequest
|
||||
type JWTProfileTokenStorage interface {
|
||||
JWTProfileTokenType(ctx context.Context, request TokenRequest) (AccessTokenType, error)
|
||||
}
|
||||
|
||||
// Storage is a required parameter for NewOpenIDProvider(). In addition to the
|
||||
// embedded interfaces below, if the passed Storage implements ClientCredentialsStorage
|
||||
// then the grant type "client_credentials" will be supported. In that case, the access
|
||||
|
|
|
@ -4,14 +4,12 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/crypto"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/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 {
|
||||
Issuer() string
|
||||
Signer() Signer
|
||||
Storage() Storage
|
||||
Crypto() Crypto
|
||||
}
|
||||
|
@ -22,6 +20,13 @@ type TokenRequest interface {
|
|||
GetScopes() []string
|
||||
}
|
||||
|
||||
type AccessTokenClient interface {
|
||||
GetID() string
|
||||
ClockSkew() time.Duration
|
||||
RestrictAdditionalAccessTokenScopes() func(scopes []string) []string
|
||||
GrantTypes() []oidc.GrantType
|
||||
}
|
||||
|
||||
func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Client, creator TokenCreator, createAccessToken bool, code, refreshToken string) (*oidc.AccessTokenResponse, error) {
|
||||
var accessToken, newRefreshToken string
|
||||
var validity time.Duration
|
||||
|
@ -32,7 +37,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
|
||||
}
|
||||
|
@ -57,7 +62,7 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli
|
|||
}, nil
|
||||
}
|
||||
|
||||
func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storage, refreshToken string, client Client) (id, newRefreshToken string, exp time.Time, err error) {
|
||||
func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storage, refreshToken string, client AccessTokenClient) (id, newRefreshToken string, exp time.Time, err error) {
|
||||
if needsRefreshToken(tokenRequest, client) {
|
||||
return storage.CreateAccessAndRefreshTokens(ctx, tokenRequest, refreshToken)
|
||||
}
|
||||
|
@ -65,7 +70,7 @@ func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storag
|
|||
return
|
||||
}
|
||||
|
||||
func needsRefreshToken(tokenRequest TokenRequest, client Client) bool {
|
||||
func needsRefreshToken(tokenRequest TokenRequest, client AccessTokenClient) bool {
|
||||
switch req := tokenRequest.(type) {
|
||||
case AuthRequest:
|
||||
return strings.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && req.GetResponseType() == oidc.ResponseTypeCode && ValidateGrantType(client, oidc.GrantTypeRefreshToken)
|
||||
|
@ -76,7 +81,7 @@ func needsRefreshToken(tokenRequest TokenRequest, client Client) bool {
|
|||
}
|
||||
}
|
||||
|
||||
func CreateAccessToken(ctx context.Context, tokenRequest TokenRequest, accessTokenType AccessTokenType, creator TokenCreator, client Client, refreshToken string) (accessToken, newRefreshToken string, validity time.Duration, err error) {
|
||||
func CreateAccessToken(ctx context.Context, tokenRequest TokenRequest, accessTokenType AccessTokenType, creator TokenCreator, client AccessTokenClient, refreshToken string) (accessToken, newRefreshToken string, validity time.Duration, err error) {
|
||||
id, newRefreshToken, exp, err := createTokens(ctx, tokenRequest, creator.Storage(), refreshToken, client)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
|
@ -87,7 +92,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())
|
||||
|
@ -98,7 +103,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 AccessTokenClient, 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())
|
||||
|
@ -108,7 +113,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 {
|
||||
|
@ -120,7 +133,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 {
|
||||
|
@ -129,8 +142,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
|
||||
}
|
||||
|
@ -148,14 +165,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 {
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
// ClientCredentialsExchange handles the OAuth 2.0 client_credentials grant, including
|
||||
|
@ -63,15 +63,15 @@ func ParseClientCredentialsRequest(r *http.Request, decoder httphelper.Decoder)
|
|||
return request, nil
|
||||
}
|
||||
|
||||
// ValidateClientCredentialsRequest validates the refresh_token request parameters including authorization check of the client
|
||||
// and returns the data representing the original auth request corresponding to the refresh_token
|
||||
// ValidateClientCredentialsRequest validates the client_credentials request parameters including authorization check of the client
|
||||
// and returns a TokenRequest and Client implementation to be used in the client_credentials response, resp. creation of the corresponding access_token.
|
||||
func ValidateClientCredentialsRequest(ctx context.Context, request *oidc.ClientCredentialsRequest, exchanger Exchanger) (TokenRequest, Client, error) {
|
||||
storage, ok := exchanger.Storage().(ClientCredentialsStorage)
|
||||
if !ok {
|
||||
return nil, nil, oidc.ErrUnsupportedGrantType().WithDescription("client_credentials grant not supported")
|
||||
}
|
||||
|
||||
client, err := AuthorizeClientCredentialsClient(ctx, request, exchanger)
|
||||
client, err := AuthorizeClientCredentialsClient(ctx, request, storage)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -84,12 +84,8 @@ func ValidateClientCredentialsRequest(ctx context.Context, request *oidc.ClientC
|
|||
return tokenRequest, client, nil
|
||||
}
|
||||
|
||||
func AuthorizeClientCredentialsClient(ctx context.Context, request *oidc.ClientCredentialsRequest, exchanger Exchanger) (Client, error) {
|
||||
if err := AuthorizeClientIDSecret(ctx, request.ClientID, request.ClientSecret, exchanger.Storage()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := exchanger.Storage().GetClientByClientID(ctx, request.ClientID)
|
||||
func AuthorizeClientCredentialsClient(ctx context.Context, request *oidc.ClientCredentialsRequest, storage ClientCredentialsStorage) (Client, error) {
|
||||
client, err := storage.ClientCredentials(ctx, request.ClientID, request.ClientSecret)
|
||||
if err != nil {
|
||||
return nil, oidc.ErrInvalidClient().WithParent(err)
|
||||
}
|
||||
|
@ -102,7 +98,7 @@ func AuthorizeClientCredentialsClient(ctx context.Context, request *oidc.ClientC
|
|||
}
|
||||
|
||||
func CreateClientCredentialsTokenResponse(ctx context.Context, tokenRequest TokenRequest, creator TokenCreator, client Client) (*oidc.AccessTokenResponse, error) {
|
||||
accessToken, _, validity, err := CreateAccessToken(ctx, tokenRequest, AccessTokenTypeJWT, creator, client, "")
|
||||
accessToken, _, validity, err := CreateAccessToken(ctx, tokenRequest, client.AccessTokenType(), creator, client, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/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
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
package op
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
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
|
||||
|
@ -53,27 +53,65 @@ func ParseJWTProfileGrantRequest(r *http.Request, decoder httphelper.Decoder) (*
|
|||
return tokenReq, nil
|
||||
}
|
||||
|
||||
// CreateJWTTokenResponse creates
|
||||
// CreateJWTTokenResponse creates an access_token response for a JWT Profile Grant request
|
||||
// by default the access_token is an opaque string, but can be specified by implementing the JWTProfileTokenStorage interface
|
||||
func CreateJWTTokenResponse(ctx context.Context, tokenRequest TokenRequest, creator TokenCreator) (*oidc.AccessTokenResponse, error) {
|
||||
id, exp, err := creator.Storage().CreateAccessToken(ctx, tokenRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accessToken, err := CreateBearerToken(id, tokenRequest.GetSubject(), creator.Crypto())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// return an opaque token as default to not break current implementations
|
||||
tokenType := AccessTokenTypeBearer
|
||||
|
||||
// the current CreateAccessToken function, esp. CreateJWT requires an implementation of an AccessTokenClient
|
||||
client := &jwtProfileClient{
|
||||
id: tokenRequest.GetSubject(),
|
||||
}
|
||||
|
||||
// by implementing the JWTProfileTokenStorage the storage can specify the AccessTokenType to be returned
|
||||
tokenStorage, ok := creator.Storage().(JWTProfileTokenStorage)
|
||||
if ok {
|
||||
var err error
|
||||
tokenType, err = tokenStorage.JWTProfileTokenType(ctx, tokenRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
accessToken, _, validity, err := CreateAccessToken(ctx, tokenRequest, tokenType, creator, client, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &oidc.AccessTokenResponse{
|
||||
AccessToken: accessToken,
|
||||
TokenType: oidc.BearerToken,
|
||||
ExpiresIn: uint64(exp.Sub(time.Now().UTC()).Seconds()),
|
||||
ExpiresIn: uint64(validity.Seconds()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseJWTProfileRequest has been renamed to ParseJWTProfileGrantRequest
|
||||
//
|
||||
//deprecated: use ParseJWTProfileGrantRequest
|
||||
// deprecated: use ParseJWTProfileGrantRequest
|
||||
func ParseJWTProfileRequest(r *http.Request, decoder httphelper.Decoder) (*oidc.JWTProfileGrantRequest, error) {
|
||||
return ParseJWTProfileGrantRequest(r, decoder)
|
||||
}
|
||||
|
||||
type jwtProfileClient struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func (j *jwtProfileClient) GetID() string {
|
||||
return j.id
|
||||
}
|
||||
|
||||
func (j *jwtProfileClient) ClockSkew() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (j *jwtProfileClient) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string {
|
||||
return func(scopes []string) []string {
|
||||
return scopes
|
||||
}
|
||||
}
|
||||
|
||||
func (j *jwtProfileClient) GrantTypes() []oidc.GrantType {
|
||||
return []oidc.GrantType{
|
||||
oidc.GrantTypeBearer,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/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 {
|
||||
|
|
|
@ -5,15 +5,13 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
type Exchanger interface {
|
||||
Issuer() string
|
||||
Storage() Storage
|
||||
Decoder() httphelper.Decoder
|
||||
Signer() Signer
|
||||
Crypto() Crypto
|
||||
AuthMethodPostSupported() bool
|
||||
AuthMethodPrivateKeyJWTSupported() bool
|
||||
|
@ -122,7 +120,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
|
||||
}
|
||||
|
@ -136,8 +134,8 @@ func AuthorizePrivateJWTKey(ctx context.Context, clientAssertion string, exchang
|
|||
return client, nil
|
||||
}
|
||||
|
||||
// ValidateGrantType ensures that the requested grant_type is allowed by the Client
|
||||
func ValidateGrantType(client Client, grantType oidc.GrantType) bool {
|
||||
// ValidateGrantType ensures that the requested grant_type is allowed by the client
|
||||
func ValidateGrantType(client interface{ GrantTypes() []oidc.GrantType }, grantType oidc.GrantType) bool {
|
||||
if client == nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -7,22 +7,22 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
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) {
|
||||
|
@ -87,7 +87,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
|
||||
}
|
||||
|
@ -151,7 +151,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
|
||||
}
|
||||
|
|
|
@ -6,15 +6,15 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
httphelper "github.com/zitadel/oidc/pkg/http"
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
type AccessTokenVerifier interface {
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
type IDTokenHintVerifier interface {
|
||||
|
@ -73,7 +73,7 @@ func NewIDTokenHintVerifier(issuer string, keySet oidc.KeySet, opts ...IDTokenHi
|
|||
}
|
||||
|
||||
// VerifyIDTokenHint validates the id token according to
|
||||
//https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
||||
func VerifyIDTokenHint(ctx context.Context, token string, v IDTokenHintVerifier) (oidc.IDTokenClaims, error) {
|
||||
claims := oidc.EmptyIDTokenClaims()
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/zitadel/oidc/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
)
|
||||
|
||||
type JWTProfileVerifier interface {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue