feat: token introspection (#83)
* introspect * introspect and client assertion * introspect and client assertion * scopes * token introspection * introspect * refactoring * fixes * clenaup * Update example/internal/mock/storage.go Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * clenaup Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
parent
fa92a20615
commit
1518c843de
46 changed files with 1672 additions and 570 deletions
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
|
@ -12,6 +13,23 @@ import (
|
|||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
type AuthRequest interface {
|
||||
GetID() string
|
||||
GetACR() string
|
||||
GetAMR() []string
|
||||
GetAudience() []string
|
||||
GetAuthTime() time.Time
|
||||
GetClientID() string
|
||||
GetCodeChallenge() *oidc.CodeChallenge
|
||||
GetNonce() string
|
||||
GetRedirectURI() string
|
||||
GetResponseType() oidc.ResponseType
|
||||
GetScopes() []string
|
||||
GetState() string
|
||||
GetSubject() string
|
||||
Done() bool
|
||||
}
|
||||
|
||||
type Authorizer interface {
|
||||
Storage() Storage
|
||||
Decoder() utils.Decoder
|
||||
|
|
|
@ -28,7 +28,7 @@ type Client interface {
|
|||
RedirectURIs() []string
|
||||
PostLogoutRedirectURIs() []string
|
||||
ApplicationType() ApplicationType
|
||||
AuthMethod() AuthMethod
|
||||
AuthMethod() oidc.AuthMethod
|
||||
ResponseTypes() []oidc.ResponseType
|
||||
LoginURL(string) string
|
||||
AccessTokenType() AccessTokenType
|
||||
|
|
|
@ -13,12 +13,14 @@ type Configuration interface {
|
|||
Issuer() string
|
||||
AuthorizationEndpoint() Endpoint
|
||||
TokenEndpoint() Endpoint
|
||||
IntrospectionEndpoint() Endpoint
|
||||
UserinfoEndpoint() Endpoint
|
||||
EndSessionEndpoint() Endpoint
|
||||
KeysEndpoint() Endpoint
|
||||
|
||||
AuthMethodPostSupported() bool
|
||||
CodeMethodS256Supported() bool
|
||||
AuthMethodPrivateKeyJWTSupported() bool
|
||||
GrantTypeTokenExchangeSupported() bool
|
||||
GrantTypeJWTAuthorizationSupported() bool
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package op
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
@ -19,22 +21,23 @@ func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) {
|
|||
|
||||
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.Intro().Absolute(c.Issuer()),
|
||||
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()),
|
||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()),
|
||||
// CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe),
|
||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
||||
ScopesSupported: Scopes(c),
|
||||
ResponseTypesSupported: ResponseTypes(c),
|
||||
GrantTypesSupported: GrantTypes(c),
|
||||
ClaimsSupported: SupportedClaims(c),
|
||||
IDTokenSigningAlgValuesSupported: SigAlgorithms(s),
|
||||
SubjectTypesSupported: SubjectTypes(c),
|
||||
TokenEndpointAuthMethodsSupported: AuthMethods(c),
|
||||
CodeChallengeMethodsSupported: CodeChallengeMethods(c),
|
||||
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()),
|
||||
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),
|
||||
TokenEndpointAuthMethodsSupported: AuthMethodsTokenEndpoint(c),
|
||||
IntrospectionEndpointAuthMethodsSupported: AuthMethodsIntrospectionEndpoint(c),
|
||||
ClaimsSupported: SupportedClaims(c),
|
||||
CodeChallengeMethodsSupported: CodeChallengeMethods(c),
|
||||
UILocalesSupported: UILocales(c),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,15 +61,16 @@ func ResponseTypes(c Configuration) []string {
|
|||
} //TODO: ok for now, check later if dynamic needed
|
||||
}
|
||||
|
||||
func GrantTypes(c Configuration) []string {
|
||||
grantTypes := []string{
|
||||
string(oidc.GrantTypeCode),
|
||||
func GrantTypes(c Configuration) []oidc.GrantType {
|
||||
grantTypes := []oidc.GrantType{
|
||||
oidc.GrantTypeCode,
|
||||
oidc.GrantTypeImplicit,
|
||||
}
|
||||
if c.GrantTypeTokenExchangeSupported() {
|
||||
grantTypes = append(grantTypes, string(oidc.GrantTypeTokenExchange))
|
||||
grantTypes = append(grantTypes, oidc.GrantTypeTokenExchange)
|
||||
}
|
||||
if c.GrantTypeJWTAuthorizationSupported() {
|
||||
grantTypes = append(grantTypes, string(oidc.GrantTypeBearer))
|
||||
grantTypes = append(grantTypes, oidc.GrantTypeBearer)
|
||||
}
|
||||
return grantTypes
|
||||
}
|
||||
|
@ -108,20 +112,41 @@ func SubjectTypes(c Configuration) []string {
|
|||
return []string{"public"} //TODO: config
|
||||
}
|
||||
|
||||
func AuthMethods(c Configuration) []string {
|
||||
authMethods := []string{
|
||||
string(AuthMethodBasic),
|
||||
func AuthMethodsTokenEndpoint(c Configuration) []oidc.AuthMethod {
|
||||
authMethods := []oidc.AuthMethod{
|
||||
oidc.AuthMethodNone,
|
||||
oidc.AuthMethodBasic,
|
||||
}
|
||||
if c.AuthMethodPostSupported() {
|
||||
authMethods = append(authMethods, string(AuthMethodPost))
|
||||
authMethods = append(authMethods, oidc.AuthMethodPost)
|
||||
}
|
||||
if c.AuthMethodPrivateKeyJWTSupported() {
|
||||
authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT)
|
||||
}
|
||||
return authMethods
|
||||
}
|
||||
|
||||
func CodeChallengeMethods(c Configuration) []string {
|
||||
codeMethods := make([]string, 0, 1)
|
||||
func AuthMethodsIntrospectionEndpoint(c Configuration) []oidc.AuthMethod {
|
||||
authMethods := []oidc.AuthMethod{
|
||||
oidc.AuthMethodBasic,
|
||||
}
|
||||
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() {
|
||||
codeMethods = append(codeMethods, CodeMethodS256)
|
||||
codeMethods = append(codeMethods, oidc.CodeChallengeMethodS256)
|
||||
}
|
||||
return codeMethods
|
||||
}
|
||||
|
||||
func UILocales(c Configuration) []language.Tag {
|
||||
return []language.Tag{
|
||||
language.English,
|
||||
language.German,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestDiscover(t *testing.T) {
|
|||
op.Discover(tt.args.w, tt.args.config)
|
||||
rec := tt.args.w.(*httptest.ResponseRecorder)
|
||||
require.Equal(t, http.StatusOK, rec.Code)
|
||||
require.Equal(t, `{"issuer":"https://issuer.com"}`, rec.Body.String())
|
||||
require.Equal(t, `{"issuer":"https://issuer.com","request_uri_parameter_supported":false}`, rec.Body.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -199,36 +199,49 @@ func Test_SubjectTypes(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_AuthMethods(t *testing.T) {
|
||||
m := mock.NewMockConfiguration(gomock.NewController(t))
|
||||
func Test_AuthMethodsTokenEndpoint(t *testing.T) {
|
||||
type args struct {
|
||||
c op.Configuration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
want []oidc.AuthMethod
|
||||
}{
|
||||
{
|
||||
"imlicit basic",
|
||||
"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
|
||||
}()},
|
||||
[]string{string(op.AuthMethodBasic)},
|
||||
[]oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic},
|
||||
},
|
||||
{
|
||||
"basic and post",
|
||||
"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
|
||||
}()},
|
||||
[]string{string(op.AuthMethodBasic), string(op.AuthMethodPost)},
|
||||
[]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) {
|
||||
if got := op.AuthMethods(tt.args.c); !reflect.DeepEqual(got, tt.want) {
|
||||
if got := op.AuthMethodsTokenEndpoint(tt.args.c); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("authMethods() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -64,10 +64,10 @@ func (mr *MockClientMockRecorder) ApplicationType() *gomock.Call {
|
|||
}
|
||||
|
||||
// AuthMethod mocks base method
|
||||
func (m *MockClient) AuthMethod() op.AuthMethod {
|
||||
func (m *MockClient) AuthMethod() oidc.AuthMethod {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AuthMethod")
|
||||
ret0, _ := ret[0].(op.AuthMethod)
|
||||
ret0, _ := ret[0].(oidc.AuthMethod)
|
||||
return ret0
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,20 @@ func (mr *MockConfigurationMockRecorder) AuthMethodPostSupported() *gomock.Call
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthMethodPostSupported", reflect.TypeOf((*MockConfiguration)(nil).AuthMethodPostSupported))
|
||||
}
|
||||
|
||||
// AuthMethodPrivateKeyJWTSupported mocks base method
|
||||
func (m *MockConfiguration) AuthMethodPrivateKeyJWTSupported() bool {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AuthMethodPrivateKeyJWTSupported")
|
||||
ret0, _ := ret[0].(bool)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AuthMethodPrivateKeyJWTSupported indicates an expected call of AuthMethodPrivateKeyJWTSupported
|
||||
func (mr *MockConfigurationMockRecorder) AuthMethodPrivateKeyJWTSupported() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthMethodPrivateKeyJWTSupported", reflect.TypeOf((*MockConfiguration)(nil).AuthMethodPrivateKeyJWTSupported))
|
||||
}
|
||||
|
||||
// AuthorizationEndpoint mocks base method
|
||||
func (m *MockConfiguration) AuthorizationEndpoint() op.Endpoint {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -117,6 +131,20 @@ func (mr *MockConfigurationMockRecorder) GrantTypeTokenExchangeSupported() *gomo
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantTypeTokenExchangeSupported", reflect.TypeOf((*MockConfiguration)(nil).GrantTypeTokenExchangeSupported))
|
||||
}
|
||||
|
||||
// IntrospectionEndpoint mocks base method
|
||||
func (m *MockConfiguration) IntrospectionEndpoint() op.Endpoint {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "IntrospectionEndpoint")
|
||||
ret0, _ := ret[0].(op.Endpoint)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// IntrospectionEndpoint indicates an expected call of IntrospectionEndpoint
|
||||
func (mr *MockConfigurationMockRecorder) IntrospectionEndpoint() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IntrospectionEndpoint", reflect.TypeOf((*MockConfiguration)(nil).IntrospectionEndpoint))
|
||||
}
|
||||
|
||||
// Issuer mocks base method
|
||||
func (m *MockConfiguration) Issuer() string {
|
||||
m.ctrl.T.Helper()
|
||||
|
|
|
@ -198,36 +198,6 @@ func (mr *MockStorageMockRecorder) GetSigningKey(arg0, arg1, arg2, arg3 interfac
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSigningKey", reflect.TypeOf((*MockStorage)(nil).GetSigningKey), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetUserinfoFromScopes mocks base method
|
||||
func (m *MockStorage) GetUserinfoFromScopes(arg0 context.Context, arg1, arg2 string, arg3 []string) (oidc.UserInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserinfoFromScopes", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(oidc.UserInfo)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserinfoFromScopes indicates an expected call of GetUserinfoFromScopes
|
||||
func (mr *MockStorageMockRecorder) GetUserinfoFromScopes(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserinfoFromScopes", reflect.TypeOf((*MockStorage)(nil).GetUserinfoFromScopes), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// GetUserinfoFromToken mocks base method
|
||||
func (m *MockStorage) GetUserinfoFromToken(arg0 context.Context, arg1, arg2, arg3 string) (oidc.UserInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserinfoFromToken", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].(oidc.UserInfo)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserinfoFromToken indicates an expected call of GetUserinfoFromToken
|
||||
func (mr *MockStorageMockRecorder) GetUserinfoFromToken(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserinfoFromToken", reflect.TypeOf((*MockStorage)(nil).GetUserinfoFromToken), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// Health mocks base method
|
||||
func (m *MockStorage) Health(arg0 context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -270,6 +240,48 @@ func (mr *MockStorageMockRecorder) SaveNewKeyPair(arg0 interface{}) *gomock.Call
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveNewKeyPair", reflect.TypeOf((*MockStorage)(nil).SaveNewKeyPair), arg0)
|
||||
}
|
||||
|
||||
// SetIntrospectionFromToken mocks base method
|
||||
func (m *MockStorage) SetIntrospectionFromToken(arg0 context.Context, arg1 oidc.IntrospectionResponse, arg2, arg3, arg4 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetIntrospectionFromToken", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetIntrospectionFromToken indicates an expected call of SetIntrospectionFromToken
|
||||
func (mr *MockStorageMockRecorder) SetIntrospectionFromToken(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetIntrospectionFromToken", reflect.TypeOf((*MockStorage)(nil).SetIntrospectionFromToken), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// SetUserinfoFromScopes mocks base method
|
||||
func (m *MockStorage) SetUserinfoFromScopes(arg0 context.Context, arg1 oidc.UserInfoSetter, arg2, arg3 string, arg4 []string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetUserinfoFromScopes", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetUserinfoFromScopes indicates an expected call of SetUserinfoFromScopes
|
||||
func (mr *MockStorageMockRecorder) SetUserinfoFromScopes(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUserinfoFromScopes", reflect.TypeOf((*MockStorage)(nil).SetUserinfoFromScopes), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// SetUserinfoFromToken mocks base method
|
||||
func (m *MockStorage) SetUserinfoFromToken(arg0 context.Context, arg1 oidc.UserInfoSetter, arg2, arg3, arg4 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SetUserinfoFromToken", arg0, arg1, arg2, arg3, arg4)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SetUserinfoFromToken indicates an expected call of SetUserinfoFromToken
|
||||
func (mr *MockStorageMockRecorder) SetUserinfoFromToken(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetUserinfoFromToken", reflect.TypeOf((*MockStorage)(nil).SetUserinfoFromToken), arg0, arg1, arg2, arg3, arg4)
|
||||
}
|
||||
|
||||
// TerminateSession mocks base method
|
||||
func (m *MockStorage) TerminateSession(arg0 context.Context, arg1, arg2 string) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -283,3 +295,18 @@ func (mr *MockStorageMockRecorder) TerminateSession(arg0, arg1, arg2 interface{}
|
|||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TerminateSession", reflect.TypeOf((*MockStorage)(nil).TerminateSession), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// ValidateJWTProfileScopes mocks base method
|
||||
func (m *MockStorage) ValidateJWTProfileScopes(arg0 context.Context, arg1 string, arg2 oidc.Scopes) (oidc.Scopes, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ValidateJWTProfileScopes", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(oidc.Scopes)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ValidateJWTProfileScopes indicates an expected call of ValidateJWTProfileScopes
|
||||
func (mr *MockStorageMockRecorder) ValidateJWTProfileScopes(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateJWTProfileScopes", reflect.TypeOf((*MockStorage)(nil).ValidateJWTProfileScopes), arg0, arg1, arg2)
|
||||
}
|
||||
|
|
|
@ -65,23 +65,23 @@ func ExpectValidClientID(s op.Storage) {
|
|||
mockS.EXPECT().GetClientByClientID(gomock.Any(), gomock.Any()).DoAndReturn(
|
||||
func(_ context.Context, id string) (op.Client, error) {
|
||||
var appType op.ApplicationType
|
||||
var authMethod op.AuthMethod
|
||||
var authMethod oidc.AuthMethod
|
||||
var accessTokenType op.AccessTokenType
|
||||
var responseTypes []oidc.ResponseType
|
||||
switch id {
|
||||
case "web_client":
|
||||
appType = op.ApplicationTypeWeb
|
||||
authMethod = op.AuthMethodBasic
|
||||
authMethod = oidc.AuthMethodBasic
|
||||
accessTokenType = op.AccessTokenTypeBearer
|
||||
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode}
|
||||
case "native_client":
|
||||
appType = op.ApplicationTypeNative
|
||||
authMethod = op.AuthMethodNone
|
||||
authMethod = oidc.AuthMethodNone
|
||||
accessTokenType = op.AccessTokenTypeBearer
|
||||
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode}
|
||||
case "useragent_client":
|
||||
appType = op.ApplicationTypeUserAgent
|
||||
authMethod = op.AuthMethodBasic
|
||||
authMethod = oidc.AuthMethodBasic
|
||||
accessTokenType = op.AccessTokenTypeJWT
|
||||
responseTypes = []oidc.ResponseType{oidc.ResponseTypeIDToken}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func ExpectSigningKey(s op.Storage) {
|
|||
type ConfClient struct {
|
||||
id string
|
||||
appType op.ApplicationType
|
||||
authMethod op.AuthMethod
|
||||
authMethod oidc.AuthMethod
|
||||
accessTokenType op.AccessTokenType
|
||||
responseTypes []oidc.ResponseType
|
||||
devMode bool
|
||||
|
@ -145,7 +145,7 @@ func (c *ConfClient) ApplicationType() op.ApplicationType {
|
|||
return c.appType
|
||||
}
|
||||
|
||||
func (c *ConfClient) AuthMethod() op.AuthMethod {
|
||||
func (c *ConfClient) AuthMethod() oidc.AuthMethod {
|
||||
return c.authMethod
|
||||
}
|
||||
|
||||
|
|
36
pkg/op/op.go
36
pkg/op/op.go
|
@ -17,26 +17,20 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
healthzEndpoint = "/healthz"
|
||||
healthEndpoint = "/healthz"
|
||||
readinessEndpoint = "/ready"
|
||||
defaultAuthorizationEndpoint = "authorize"
|
||||
defaulTokenEndpoint = "oauth/token"
|
||||
defaultIntrospectEndpoint = "introspect"
|
||||
defaultTokenEndpoint = "oauth/token"
|
||||
defaultIntrospectEndpoint = "oauth/introspect"
|
||||
defaultUserinfoEndpoint = "userinfo"
|
||||
defaultEndSessionEndpoint = "end_session"
|
||||
defaultKeysEndpoint = "keys"
|
||||
|
||||
AuthMethodBasic AuthMethod = "client_secret_basic"
|
||||
AuthMethodPost AuthMethod = "client_secret_post"
|
||||
AuthMethodNone AuthMethod = "none"
|
||||
|
||||
CodeMethodS256 = "S256"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultEndpoints = &endpoints{
|
||||
Authorization: NewEndpoint(defaultAuthorizationEndpoint),
|
||||
Token: NewEndpoint(defaulTokenEndpoint),
|
||||
Token: NewEndpoint(defaultTokenEndpoint),
|
||||
Introspection: NewEndpoint(defaultIntrospectEndpoint),
|
||||
Userinfo: NewEndpoint(defaultUserinfoEndpoint),
|
||||
EndSession: NewEndpoint(defaultEndSessionEndpoint),
|
||||
|
@ -72,12 +66,13 @@ func CreateRouter(o OpenIDProvider, interceptors ...HttpInterceptor) *mux.Router
|
|||
handlers.AllowedHeaders([]string{"authorization", "content-type"}),
|
||||
handlers.AllowedOriginValidator(allowAllOrigins),
|
||||
))
|
||||
router.HandleFunc(healthzEndpoint, healthzHandler)
|
||||
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(o.AuthorizationEndpoint().Relative()+"/callback").Queries("id", "{id}").Handler(intercept(authorizeCallbackHandler(o)))
|
||||
router.Handle(o.TokenEndpoint().Relative(), intercept(tokenHandler(o)))
|
||||
router.HandleFunc(o.IntrospectionEndpoint().Relative(), introspectionHandler(o))
|
||||
router.HandleFunc(o.UserinfoEndpoint().Relative(), userinfoHandler(o))
|
||||
router.Handle(o.EndSessionEndpoint().Relative(), intercept(endSessionHandler(o)))
|
||||
router.HandleFunc(o.KeysEndpoint().Relative(), keysHandler(o))
|
||||
|
@ -89,6 +84,7 @@ type Config struct {
|
|||
CryptoKey [32]byte
|
||||
DefaultLogoutRedirectURI string
|
||||
CodeMethodS256 bool
|
||||
AuthMethodPrivateKeyJWT bool
|
||||
}
|
||||
|
||||
type endpoints struct {
|
||||
|
@ -166,6 +162,10 @@ func (o *openidProvider) TokenEndpoint() Endpoint {
|
|||
return o.endpoints.Token
|
||||
}
|
||||
|
||||
func (o *openidProvider) IntrospectionEndpoint() Endpoint {
|
||||
return o.endpoints.Introspection
|
||||
}
|
||||
|
||||
func (o *openidProvider) UserinfoEndpoint() Endpoint {
|
||||
return o.endpoints.Userinfo
|
||||
}
|
||||
|
@ -186,6 +186,10 @@ func (o *openidProvider) CodeMethodS256Supported() bool {
|
|||
return o.config.CodeMethodS256
|
||||
}
|
||||
|
||||
func (o *openidProvider) AuthMethodPrivateKeyJWTSupported() bool {
|
||||
return o.config.AuthMethodPrivateKeyJWT
|
||||
}
|
||||
|
||||
func (o *openidProvider) GrantTypeTokenExchangeSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
@ -332,6 +336,16 @@ func WithCustomTokenEndpoint(endpoint Endpoint) Option {
|
|||
}
|
||||
}
|
||||
|
||||
func WithCustomIntrospectionEndpoint(endpoint Endpoint) Option {
|
||||
return func(o *openidProvider) error {
|
||||
if err := endpoint.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
o.endpoints.Introspection = endpoint
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithCustomUserinfoEndpoint(endpoint Endpoint) Option {
|
||||
return func(o *openidProvider) error {
|
||||
if err := endpoint.Validate(); err != nil {
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
type ProbesFn func(context.Context) error
|
||||
|
||||
func healthzHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ok(w)
|
||||
}
|
||||
|
||||
|
|
|
@ -28,10 +28,12 @@ type AuthStorage interface {
|
|||
type OPStorage interface {
|
||||
GetClientByClientID(ctx context.Context, clientID string) (Client, error)
|
||||
AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error
|
||||
GetUserinfoFromScopes(ctx context.Context, userID, clientID string, scopes []string) (oidc.UserInfo, error)
|
||||
GetUserinfoFromToken(ctx context.Context, tokenID, subject, origin string) (oidc.UserInfo, error)
|
||||
SetUserinfoFromScopes(ctx context.Context, userinfo oidc.UserInfoSetter, userID, clientID string, scopes []string) error
|
||||
SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error
|
||||
SetIntrospectionFromToken(ctx context.Context, userinfo oidc.IntrospectionResponse, tokenID, subject, clientID string) error
|
||||
GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (map[string]interface{}, error)
|
||||
GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error)
|
||||
ValidateJWTProfileScopes(ctx context.Context, userID string, scope oidc.Scopes) (oidc.Scopes, error)
|
||||
}
|
||||
|
||||
type Storage interface {
|
||||
|
@ -44,23 +46,6 @@ type StorageNotFoundError interface {
|
|||
IsNotFound()
|
||||
}
|
||||
|
||||
type AuthRequest interface {
|
||||
GetID() string
|
||||
GetACR() string
|
||||
GetAMR() []string
|
||||
GetAudience() []string
|
||||
GetAuthTime() time.Time
|
||||
GetClientID() string
|
||||
GetCodeChallenge() *oidc.CodeChallenge
|
||||
GetNonce() string
|
||||
GetRedirectURI() string
|
||||
GetResponseType() oidc.ResponseType
|
||||
GetScopes() []string
|
||||
GetState() string
|
||||
GetSubject() string
|
||||
Done() bool
|
||||
}
|
||||
|
||||
type EndSessionRequest struct {
|
||||
UserID string
|
||||
Client Client
|
||||
|
|
|
@ -114,7 +114,8 @@ func CreateIDToken(ctx context.Context, issuer string, authReq AuthRequest, vali
|
|||
}
|
||||
}
|
||||
if len(scopes) > 0 {
|
||||
userInfo, err := storage.GetUserinfoFromScopes(ctx, authReq.GetSubject(), authReq.GetClientID(), scopes)
|
||||
userInfo := oidc.NewUserInfo()
|
||||
err := storage.SetUserinfoFromScopes(ctx, userInfo, authReq.GetSubject(), authReq.GetClientID(), scopes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
77
pkg/op/token_intospection.go
Normal file
77
pkg/op/token_intospection.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package op
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
type Introspector interface {
|
||||
Decoder() utils.Decoder
|
||||
Crypto() Crypto
|
||||
Storage() Storage
|
||||
AccessTokenVerifier() AccessTokenVerifier
|
||||
}
|
||||
|
||||
type IntrospectorJWTProfile interface {
|
||||
Introspector
|
||||
JWTProfileVerifier() JWTProfileVerifier
|
||||
}
|
||||
|
||||
func introspectionHandler(introspector Introspector) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
Introspect(w, r, introspector)
|
||||
}
|
||||
}
|
||||
|
||||
func Introspect(w http.ResponseWriter, r *http.Request, introspector Introspector) {
|
||||
response := oidc.NewIntrospectionResponse()
|
||||
token, clientID, err := ParseTokenIntrospectionRequest(r, introspector)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
tokenID, subject, ok := getTokenIDAndSubject(r.Context(), introspector, token)
|
||||
if !ok {
|
||||
utils.MarshalJSON(w, response)
|
||||
return
|
||||
}
|
||||
err = introspector.Storage().SetIntrospectionFromToken(r.Context(), response, tokenID, subject, clientID)
|
||||
if err != nil {
|
||||
utils.MarshalJSON(w, response)
|
||||
return
|
||||
}
|
||||
response.SetActive(true)
|
||||
utils.MarshalJSON(w, response)
|
||||
}
|
||||
|
||||
func ParseTokenIntrospectionRequest(r *http.Request, introspector Introspector) (token, clientID string, err error) {
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
return "", "", errors.New("unable to parse request")
|
||||
}
|
||||
req := new(struct {
|
||||
oidc.IntrospectionRequest
|
||||
oidc.ClientAssertionParams
|
||||
})
|
||||
err = introspector.Decoder().Decode(req, r.Form)
|
||||
if err != nil {
|
||||
return "", "", errors.New("unable to parse request")
|
||||
}
|
||||
if introspectorJWTProfile, ok := introspector.(IntrospectorJWTProfile); ok && req.ClientAssertion != "" {
|
||||
profile, err := VerifyJWTAssertion(r.Context(), req.ClientAssertion, introspectorJWTProfile.JWTProfileVerifier())
|
||||
if err == nil {
|
||||
return req.Token, profile.Issuer, nil
|
||||
}
|
||||
}
|
||||
clientID, clientSecret, ok := r.BasicAuth()
|
||||
if ok {
|
||||
if err := introspector.Storage().AuthorizeClientIDSecret(r.Context(), clientID, clientSecret); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return req.Token, clientID, nil
|
||||
}
|
||||
return "", "", errors.New("invalid authorization")
|
||||
}
|
|
@ -7,7 +7,6 @@ import (
|
|||
"net/url"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/oidc/grants/tokenexchange"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
|
@ -18,6 +17,7 @@ type Exchanger interface {
|
|||
Signer() Signer
|
||||
Crypto() Crypto
|
||||
AuthMethodPostSupported() bool
|
||||
AuthMethodPrivateKeyJWTSupported() bool
|
||||
GrantTypeTokenExchangeSupported() bool
|
||||
GrantTypeJWTAuthorizationSupported() bool
|
||||
}
|
||||
|
@ -112,18 +112,33 @@ func ValidateAccessTokenRequest(ctx context.Context, tokenReq *oidc.AccessTokenR
|
|||
}
|
||||
|
||||
func AuthorizeClient(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (AuthRequest, Client, error) {
|
||||
if tokenReq.ClientAssertionType == oidc.ClientAssertionTypeJWTAssertion {
|
||||
jwtExchanger, ok := exchanger.(JWTAuthorizationGrantExchanger)
|
||||
if !ok || !exchanger.AuthMethodPrivateKeyJWTSupported() {
|
||||
return nil, nil, errors.New("auth_method private_key_jwt not supported")
|
||||
}
|
||||
return AuthorizePrivateJWTKey(ctx, tokenReq, jwtExchanger)
|
||||
}
|
||||
client, err := exchanger.Storage().GetClientByClientID(ctx, tokenReq.ClientID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if client.AuthMethod() == AuthMethodNone {
|
||||
if client.AuthMethod() == oidc.AuthMethodPrivateKeyJWT {
|
||||
return nil, nil, errors.New("invalid_grant")
|
||||
}
|
||||
if client.AuthMethod() == oidc.AuthMethodNone {
|
||||
authReq, err := AuthorizeCodeChallenge(ctx, tokenReq, exchanger)
|
||||
return authReq, client, err
|
||||
}
|
||||
if client.AuthMethod() == AuthMethodPost && !exchanger.AuthMethodPostSupported() {
|
||||
if client.AuthMethod() == oidc.AuthMethodPost && !exchanger.AuthMethodPostSupported() {
|
||||
return nil, nil, errors.New("auth_method post not supported")
|
||||
}
|
||||
err = AuthorizeClientIDSecret(ctx, tokenReq.ClientID, tokenReq.ClientSecret, exchanger.Storage())
|
||||
authReq, err := AuthorizeClientIDSecret(ctx, tokenReq.ClientID, tokenReq.ClientSecret, tokenReq.Code, exchanger.Storage())
|
||||
return authReq, client, err
|
||||
}
|
||||
|
||||
func AuthorizePrivateJWTKey(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger JWTAuthorizationGrantExchanger) (AuthRequest, Client, error) {
|
||||
jwtReq, err := VerifyJWTAssertion(ctx, tokenReq.ClientAssertion, exchanger.JWTProfileVerifier())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -131,11 +146,26 @@ func AuthorizeClient(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exc
|
|||
if err != nil {
|
||||
return nil, nil, ErrInvalidRequest("invalid code")
|
||||
}
|
||||
client, err := exchanger.Storage().GetClientByClientID(ctx, jwtReq.Issuer)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if client.AuthMethod() != oidc.AuthMethodPrivateKeyJWT {
|
||||
return nil, nil, ErrInvalidRequest("invalid_client")
|
||||
}
|
||||
return authReq, client, nil
|
||||
}
|
||||
|
||||
func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string, storage OPStorage) error {
|
||||
return storage.AuthorizeClientIDSecret(ctx, clientID, clientSecret)
|
||||
func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret, code string, storage Storage) (AuthRequest, error) {
|
||||
err := storage.AuthorizeClientIDSecret(ctx, clientID, clientSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authReq, err := storage.AuthRequestByCode(ctx, code)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidRequest("invalid code")
|
||||
}
|
||||
return authReq, nil
|
||||
}
|
||||
|
||||
func AuthorizeCodeChallenge(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (AuthRequest, error) {
|
||||
|
@ -158,12 +188,17 @@ func JWTProfile(w http.ResponseWriter, r *http.Request, exchanger JWTAuthorizati
|
|||
RequestError(w, r, err)
|
||||
}
|
||||
|
||||
tokenRequest, err := VerifyJWTAssertion(r.Context(), profileRequest, exchanger.JWTProfileVerifier())
|
||||
tokenRequest, err := VerifyJWTAssertion(r.Context(), profileRequest.Assertion, exchanger.JWTProfileVerifier())
|
||||
if err != nil {
|
||||
RequestError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
tokenRequest.Scopes, err = exchanger.Storage().ValidateJWTProfileScopes(r.Context(), tokenRequest.Issuer, profileRequest.Scope)
|
||||
if err != nil {
|
||||
RequestError(w, r, err)
|
||||
return
|
||||
}
|
||||
resp, err := CreateJWTTokenResponse(r.Context(), tokenRequest, exchanger)
|
||||
if err != nil {
|
||||
RequestError(w, r, err)
|
||||
|
@ -172,12 +207,12 @@ func JWTProfile(w http.ResponseWriter, r *http.Request, exchanger JWTAuthorizati
|
|||
utils.MarshalJSON(w, resp)
|
||||
}
|
||||
|
||||
func ParseJWTProfileRequest(r *http.Request, decoder utils.Decoder) (*tokenexchange.JWTProfileRequest, error) {
|
||||
func ParseJWTProfileRequest(r *http.Request, decoder utils.Decoder) (*oidc.JWTProfileGrantRequest, error) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
return nil, ErrInvalidRequest("error parsing form")
|
||||
}
|
||||
tokenReq := new(tokenexchange.JWTProfileRequest)
|
||||
tokenReq := new(oidc.JWTProfileGrantRequest)
|
||||
err = decoder.Decode(tokenReq, r.Form)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidRequest("error decoding form")
|
||||
|
|
|
@ -24,7 +24,7 @@ func userinfoHandler(userinfoProvider UserinfoProvider) func(http.ResponseWriter
|
|||
}
|
||||
|
||||
func Userinfo(w http.ResponseWriter, r *http.Request, userinfoProvider UserinfoProvider) {
|
||||
accessToken, err := getAccessToken(r, userinfoProvider.Decoder())
|
||||
accessToken, err := ParseUserinfoRequest(r, userinfoProvider.Decoder())
|
||||
if err != nil {
|
||||
http.Error(w, "access token missing", http.StatusUnauthorized)
|
||||
return
|
||||
|
@ -34,7 +34,8 @@ func Userinfo(w http.ResponseWriter, r *http.Request, userinfoProvider UserinfoP
|
|||
http.Error(w, "access token invalid", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
info, err := userinfoProvider.Storage().GetUserinfoFromToken(r.Context(), tokenID, subject, r.Header.Get("origin"))
|
||||
info := oidc.NewUserInfo()
|
||||
err = userinfoProvider.Storage().SetUserinfoFromToken(r.Context(), info, tokenID, subject, r.Header.Get("origin"))
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
utils.MarshalJSON(w, err)
|
||||
|
@ -43,16 +44,12 @@ func Userinfo(w http.ResponseWriter, r *http.Request, userinfoProvider UserinfoP
|
|||
utils.MarshalJSON(w, info)
|
||||
}
|
||||
|
||||
func getAccessToken(r *http.Request, decoder utils.Decoder) (string, error) {
|
||||
authHeader := r.Header.Get("authorization")
|
||||
if authHeader != "" {
|
||||
parts := strings.Split(authHeader, "Bearer ")
|
||||
if len(parts) != 2 {
|
||||
return "", errors.New("invalid auth header")
|
||||
}
|
||||
return parts[1], nil
|
||||
func ParseUserinfoRequest(r *http.Request, decoder utils.Decoder) (string, error) {
|
||||
accessToken, err := getAccessToken(r)
|
||||
if err == nil {
|
||||
return accessToken, nil
|
||||
}
|
||||
err := r.ParseForm()
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
return "", errors.New("unable to parse request")
|
||||
}
|
||||
|
@ -64,6 +61,18 @@ func getAccessToken(r *http.Request, decoder utils.Decoder) (string, error) {
|
|||
return req.AccessToken, nil
|
||||
}
|
||||
|
||||
func getAccessToken(r *http.Request) (string, error) {
|
||||
authHeader := r.Header.Get("authorization")
|
||||
if authHeader == "" {
|
||||
return "", errors.New("no auth header")
|
||||
}
|
||||
parts := strings.Split(authHeader, "Bearer ")
|
||||
if len(parts) != 2 {
|
||||
return "", errors.New("invalid auth header")
|
||||
}
|
||||
return parts[1], nil
|
||||
}
|
||||
|
||||
func getTokenIDAndSubject(ctx context.Context, userinfoProvider UserinfoProvider, accessToken string) (string, string, bool) {
|
||||
tokenIDSubject, err := userinfoProvider.Crypto().Decrypt(accessToken)
|
||||
if err == nil {
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/oidc/grants/tokenexchange"
|
||||
)
|
||||
|
||||
type JWTProfileVerifier interface {
|
||||
|
@ -48,9 +47,9 @@ func (v *jwtProfileVerifier) Offset() time.Duration {
|
|||
return v.offset
|
||||
}
|
||||
|
||||
func VerifyJWTAssertion(ctx context.Context, profileRequest *tokenexchange.JWTProfileRequest, v JWTProfileVerifier) (*oidc.JWTTokenRequest, error) {
|
||||
func VerifyJWTAssertion(ctx context.Context, assertion string, v JWTProfileVerifier) (*oidc.JWTTokenRequest, error) {
|
||||
request := new(oidc.JWTTokenRequest)
|
||||
payload, err := oidc.ParseToken(profileRequest.Assertion, request)
|
||||
payload, err := oidc.ParseToken(assertion, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -71,12 +70,11 @@ func VerifyJWTAssertion(ctx context.Context, profileRequest *tokenexchange.JWTPr
|
|||
//TODO: implement delegation (openid core / oauth rfc)
|
||||
}
|
||||
|
||||
keySet := &jwtProfileKeySet{v.Storage(), request.Subject}
|
||||
keySet := &jwtProfileKeySet{v.Storage(), request.Issuer}
|
||||
|
||||
if err = oidc.CheckSignature(ctx, profileRequest.Assertion, payload, request, nil, keySet); err != nil {
|
||||
if err = oidc.CheckSignature(ctx, assertion, payload, request, nil, keySet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Scopes = profileRequest.Scope
|
||||
return request, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue