introspect and client assertion

This commit is contained in:
Livio Amstutz 2021-01-28 08:41:36 +01:00
parent a1a21f0d59
commit 50ab51bb46
15 changed files with 171 additions and 60 deletions

View file

@ -184,22 +184,22 @@ func (s *AuthStorage) GetClientByClientID(_ context.Context, id string) (op.Clie
return nil, errors.New("not found") return nil, errors.New("not found")
} }
var appType op.ApplicationType var appType op.ApplicationType
var authMethod op.AuthMethod var authMethod oidc.AuthMethod
var accessTokenType op.AccessTokenType var accessTokenType op.AccessTokenType
var responseTypes []oidc.ResponseType var responseTypes []oidc.ResponseType
if id == "web" { if id == "web" {
appType = op.ApplicationTypeWeb appType = op.ApplicationTypeWeb
authMethod = op.AuthMethodBasic authMethod = oidc.AuthMethodBasic
accessTokenType = op.AccessTokenTypeBearer accessTokenType = op.AccessTokenTypeBearer
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode} responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode}
} else if id == "native" { } else if id == "native" {
appType = op.ApplicationTypeNative appType = op.ApplicationTypeNative
authMethod = op.AuthMethodNone authMethod = oidc.AuthMethodNone
accessTokenType = op.AccessTokenTypeBearer accessTokenType = op.AccessTokenTypeBearer
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode} responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode}
} else { } else {
appType = op.ApplicationTypeUserAgent appType = op.ApplicationTypeUserAgent
authMethod = op.AuthMethodNone authMethod = oidc.AuthMethodNone
accessTokenType = op.AccessTokenTypeJWT accessTokenType = op.AccessTokenTypeJWT
responseTypes = []oidc.ResponseType{oidc.ResponseTypeIDToken, oidc.ResponseTypeIDTokenOnly} responseTypes = []oidc.ResponseType{oidc.ResponseTypeIDToken, oidc.ResponseTypeIDTokenOnly}
} }
@ -229,7 +229,7 @@ func (s *AuthStorage) GetPrivateClaimsFromScopes(_ context.Context, _, _ string,
type ConfClient struct { type ConfClient struct {
applicationType op.ApplicationType applicationType op.ApplicationType
authMethod op.AuthMethod authMethod oidc.AuthMethod
responseTypes []oidc.ResponseType responseTypes []oidc.ResponseType
ID string ID string
accessTokenType op.AccessTokenType accessTokenType op.AccessTokenType
@ -262,7 +262,7 @@ func (c *ConfClient) ApplicationType() op.ApplicationType {
return c.applicationType return c.applicationType
} }
func (c *ConfClient) AuthMethod() op.AuthMethod { func (c *ConfClient) AuthMethod() oidc.AuthMethod {
return c.authMethod return c.authMethod
} }

View file

@ -19,7 +19,16 @@ type DiscoveryConfiguration struct {
GrantTypesSupported []string `json:"grant_types_supported,omitempty"` GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"` SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"` IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"` TokenEndpointAuthMethodsSupported []AuthMethod `json:"token_endpoint_auth_methods_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"` CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
ClaimsSupported []string `json:"claims_supported,omitempty"` ClaimsSupported []string `json:"claims_supported,omitempty"`
} }
type AuthMethod string
const (
AuthMethodBasic AuthMethod = "client_secret_basic"
AuthMethodPost AuthMethod = "client_secret_post"
AuthMethodNone AuthMethod = "none"
AuthMethodPrivateKeyJWT AuthMethod = "private_key_jwt"
)

View file

@ -209,7 +209,7 @@ func (i *introspectionResponse) MarshalJSON() ([]byte, error) {
*Alias *Alias
Locale interface{} `json:"locale,omitempty"` Locale interface{} `json:"locale,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"` UpdatedAt int64 `json:"updated_at,omitempty"`
PreferredUsername string `json:"username,omitempty"` Username string `json:"username,omitempty"`
}{ }{
Alias: (*Alias)(i), Alias: (*Alias)(i),
} }
@ -219,8 +219,7 @@ func (i *introspectionResponse) MarshalJSON() ([]byte, error) {
if !time.Time(i.UpdatedAt).IsZero() { if !time.Time(i.UpdatedAt).IsZero() {
a.UpdatedAt = time.Time(i.UpdatedAt).Unix() a.UpdatedAt = time.Time(i.UpdatedAt).Unix()
} }
a.PreferredUsername = i.PreferredUsername a.Username = i.PreferredUsername
i.PreferredUsername = ""
b, err := json.Marshal(a) b, err := json.Marshal(a)
if err != nil { if err != nil {

View file

@ -15,6 +15,10 @@ const (
//GrantTypeTokenExchange defines the grant_type `urn:ietf:params:oauth:grant-type:token-exchange` used for the OAuth Token Exchange Grant //GrantTypeTokenExchange defines the grant_type `urn:ietf:params:oauth:grant-type:token-exchange` used for the OAuth Token Exchange Grant
GrantTypeTokenExchange GrantType = "urn:ietf:params:oauth:grant-type:token-exchange" GrantTypeTokenExchange GrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
//ClientAssertionTypeJWTAssertion defines the client_assertion_type `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`
//used for the OAuth JWT Profile Client Authentication
ClientAssertionTypeJWTAssertion = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
) )
type GrantType string type GrantType string
@ -32,6 +36,8 @@ type AccessTokenRequest struct {
ClientID string `schema:"client_id"` ClientID string `schema:"client_id"`
ClientSecret string `schema:"client_secret"` ClientSecret string `schema:"client_secret"`
CodeVerifier string `schema:"code_verifier"` CodeVerifier string `schema:"code_verifier"`
ClientAssertion string `schema:"client_assertion"`
ClientAssertionType string `schema:"client_assertion_type"`
} }
func (a *AccessTokenRequest) GrantType() GrantType { func (a *AccessTokenRequest) GrantType() GrantType {

View file

@ -28,7 +28,7 @@ type Client interface {
RedirectURIs() []string RedirectURIs() []string
PostLogoutRedirectURIs() []string PostLogoutRedirectURIs() []string
ApplicationType() ApplicationType ApplicationType() ApplicationType
AuthMethod() AuthMethod AuthMethod() oidc.AuthMethod
ResponseTypes() []oidc.ResponseType ResponseTypes() []oidc.ResponseType
LoginURL(string) string LoginURL(string) string
AccessTokenType() AccessTokenType AccessTokenType() AccessTokenType

View file

@ -20,6 +20,7 @@ type Configuration interface {
AuthMethodPostSupported() bool AuthMethodPostSupported() bool
CodeMethodS256Supported() bool CodeMethodS256Supported() bool
AuthMethodPrivateKeyJWTSupported() bool
GrantTypeTokenExchangeSupported() bool GrantTypeTokenExchangeSupported() bool
GrantTypeJWTAuthorizationSupported() bool GrantTypeJWTAuthorizationSupported() bool
} }

View file

@ -108,12 +108,16 @@ func SubjectTypes(c Configuration) []string {
return []string{"public"} //TODO: config return []string{"public"} //TODO: config
} }
func AuthMethods(c Configuration) []string { func AuthMethods(c Configuration) []oidc.AuthMethod {
authMethods := []string{ authMethods := []oidc.AuthMethod{
string(AuthMethodBasic), oidc.AuthMethodNone,
oidc.AuthMethodBasic,
} }
if c.AuthMethodPostSupported() { if c.AuthMethodPostSupported() {
authMethods = append(authMethods, string(AuthMethodPost)) authMethods = append(authMethods, oidc.AuthMethodPost)
}
if c.AuthMethodPrivateKeyJWTSupported() {
authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT)
} }
return authMethods return authMethods
} }

View file

@ -64,10 +64,10 @@ func (mr *MockClientMockRecorder) ApplicationType() *gomock.Call {
} }
// AuthMethod mocks base method // AuthMethod mocks base method
func (m *MockClient) AuthMethod() op.AuthMethod { func (m *MockClient) AuthMethod() oidc.AuthMethod {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AuthMethod") ret := m.ctrl.Call(m, "AuthMethod")
ret0, _ := ret[0].(op.AuthMethod) ret0, _ := ret[0].(oidc.AuthMethod)
return ret0 return ret0
} }

View file

@ -47,6 +47,20 @@ func (mr *MockConfigurationMockRecorder) AuthMethodPostSupported() *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthMethodPostSupported", reflect.TypeOf((*MockConfiguration)(nil).AuthMethodPostSupported)) 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 // AuthorizationEndpoint mocks base method
func (m *MockConfiguration) AuthorizationEndpoint() op.Endpoint { func (m *MockConfiguration) AuthorizationEndpoint() op.Endpoint {
m.ctrl.T.Helper() 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)) 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 // Issuer mocks base method
func (m *MockConfiguration) Issuer() string { func (m *MockConfiguration) Issuer() string {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View file

@ -270,6 +270,34 @@ func (mr *MockStorageMockRecorder) SaveNewKeyPair(arg0 interface{}) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveNewKeyPair", reflect.TypeOf((*MockStorage)(nil).SaveNewKeyPair), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveNewKeyPair", reflect.TypeOf((*MockStorage)(nil).SaveNewKeyPair), arg0)
} }
// 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 // TerminateSession mocks base method
func (m *MockStorage) TerminateSession(arg0 context.Context, arg1, arg2 string) error { func (m *MockStorage) TerminateSession(arg0 context.Context, arg1, arg2 string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View file

@ -65,23 +65,23 @@ func ExpectValidClientID(s op.Storage) {
mockS.EXPECT().GetClientByClientID(gomock.Any(), gomock.Any()).DoAndReturn( mockS.EXPECT().GetClientByClientID(gomock.Any(), gomock.Any()).DoAndReturn(
func(_ context.Context, id string) (op.Client, error) { func(_ context.Context, id string) (op.Client, error) {
var appType op.ApplicationType var appType op.ApplicationType
var authMethod op.AuthMethod var authMethod oidc.AuthMethod
var accessTokenType op.AccessTokenType var accessTokenType op.AccessTokenType
var responseTypes []oidc.ResponseType var responseTypes []oidc.ResponseType
switch id { switch id {
case "web_client": case "web_client":
appType = op.ApplicationTypeWeb appType = op.ApplicationTypeWeb
authMethod = op.AuthMethodBasic authMethod = oidc.AuthMethodBasic
accessTokenType = op.AccessTokenTypeBearer accessTokenType = op.AccessTokenTypeBearer
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode} responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode}
case "native_client": case "native_client":
appType = op.ApplicationTypeNative appType = op.ApplicationTypeNative
authMethod = op.AuthMethodNone authMethod = oidc.AuthMethodNone
accessTokenType = op.AccessTokenTypeBearer accessTokenType = op.AccessTokenTypeBearer
responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode} responseTypes = []oidc.ResponseType{oidc.ResponseTypeCode}
case "useragent_client": case "useragent_client":
appType = op.ApplicationTypeUserAgent appType = op.ApplicationTypeUserAgent
authMethod = op.AuthMethodBasic authMethod = oidc.AuthMethodBasic
accessTokenType = op.AccessTokenTypeJWT accessTokenType = op.AccessTokenTypeJWT
responseTypes = []oidc.ResponseType{oidc.ResponseTypeIDToken} responseTypes = []oidc.ResponseType{oidc.ResponseTypeIDToken}
} }
@ -119,7 +119,7 @@ func ExpectSigningKey(s op.Storage) {
type ConfClient struct { type ConfClient struct {
id string id string
appType op.ApplicationType appType op.ApplicationType
authMethod op.AuthMethod authMethod oidc.AuthMethod
accessTokenType op.AccessTokenType accessTokenType op.AccessTokenType
responseTypes []oidc.ResponseType responseTypes []oidc.ResponseType
devMode bool devMode bool
@ -145,7 +145,7 @@ func (c *ConfClient) ApplicationType() op.ApplicationType {
return c.appType return c.appType
} }
func (c *ConfClient) AuthMethod() op.AuthMethod { func (c *ConfClient) AuthMethod() oidc.AuthMethod {
return c.authMethod return c.authMethod
} }

View file

@ -29,6 +29,7 @@ const (
AuthMethodBasic AuthMethod = "client_secret_basic" AuthMethodBasic AuthMethod = "client_secret_basic"
AuthMethodPost AuthMethod = "client_secret_post" AuthMethodPost AuthMethod = "client_secret_post"
AuthMethodNone AuthMethod = "none" AuthMethodNone AuthMethod = "none"
AuthMethodPrivateKeyJWT AuthMethod = "private_key_jwt"
CodeMethodS256 = "S256" CodeMethodS256 = "S256"
) )
@ -90,6 +91,7 @@ type Config struct {
CryptoKey [32]byte CryptoKey [32]byte
DefaultLogoutRedirectURI string DefaultLogoutRedirectURI string
CodeMethodS256 bool CodeMethodS256 bool
AuthMethodPrivateKeyJWT bool
} }
type endpoints struct { type endpoints struct {
@ -191,6 +193,10 @@ func (o *openidProvider) CodeMethodS256Supported() bool {
return o.config.CodeMethodS256 return o.config.CodeMethodS256
} }
func (o *openidProvider) AuthMethodPrivateKeyJWTSupported() bool {
return o.config.AuthMethodPrivateKeyJWT
}
func (o *openidProvider) GrantTypeTokenExchangeSupported() bool { func (o *openidProvider) GrantTypeTokenExchangeSupported() bool {
return false return false
} }

View file

@ -32,6 +32,7 @@ type OPStorage interface {
SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error
GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (map[string]interface{}, error) GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (map[string]interface{}, error)
GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error)
//ValidateJWTProfileScopes(ctx context.Context, userID string, scope oidc.Scopes) (oidc.Scopes, error)
//deprecated: use GetUserinfoFromScopes instead //deprecated: use GetUserinfoFromScopes instead
GetUserinfoFromScopes(ctx context.Context, userID, clientID string, scopes []string) (oidc.UserInfo, error) GetUserinfoFromScopes(ctx context.Context, userID, clientID string, scopes []string) (oidc.UserInfo, error)

View file

@ -18,6 +18,7 @@ type Exchanger interface {
Signer() Signer Signer() Signer
Crypto() Crypto Crypto() Crypto
AuthMethodPostSupported() bool AuthMethodPostSupported() bool
AuthMethodPrivateKeyJWTSupported() bool
GrantTypeTokenExchangeSupported() bool GrantTypeTokenExchangeSupported() bool
GrantTypeJWTAuthorizationSupported() bool GrantTypeJWTAuthorizationSupported() bool
} }
@ -112,18 +113,30 @@ func ValidateAccessTokenRequest(ctx context.Context, tokenReq *oidc.AccessTokenR
} }
func AuthorizeClient(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (AuthRequest, Client, error) { 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) client, err := exchanger.Storage().GetClientByClientID(ctx, tokenReq.ClientID)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if client.AuthMethod() == AuthMethodNone { if client.AuthMethod() == oidc.AuthMethodNone {
authReq, err := AuthorizeCodeChallenge(ctx, tokenReq, exchanger) authReq, err := AuthorizeCodeChallenge(ctx, tokenReq, exchanger)
return authReq, client, err 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") 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -131,11 +144,26 @@ func AuthorizeClient(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exc
if err != nil { if err != nil {
return nil, nil, ErrInvalidRequest("invalid code") 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 return authReq, client, nil
} }
func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string, storage OPStorage) error { func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret, code string, storage Storage) (AuthRequest, error) {
return storage.AuthorizeClientIDSecret(ctx, clientID, clientSecret) 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) { func AuthorizeCodeChallenge(ctx context.Context, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (AuthRequest, error) {
@ -158,12 +186,15 @@ func JWTProfile(w http.ResponseWriter, r *http.Request, exchanger JWTAuthorizati
RequestError(w, r, err) RequestError(w, r, err)
} }
tokenRequest, err := VerifyJWTAssertion(r.Context(), profileRequest, exchanger.JWTProfileVerifier()) tokenRequest, err := VerifyJWTAssertion(r.Context(), profileRequest.Assertion, exchanger.JWTProfileVerifier())
if err != nil { if err != nil {
RequestError(w, r, err) RequestError(w, r, err)
return return
} }
//TODO: filter scopes
tokenRequest.Scopes = profileRequest.Scope
resp, err := CreateJWTTokenResponse(r.Context(), tokenRequest, exchanger) resp, err := CreateJWTTokenResponse(r.Context(), tokenRequest, exchanger)
if err != nil { if err != nil {
RequestError(w, r, err) RequestError(w, r, err)

View file

@ -8,7 +8,6 @@ import (
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
"github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/oidc/grants/tokenexchange"
) )
type JWTProfileVerifier interface { type JWTProfileVerifier interface {
@ -48,9 +47,9 @@ func (v *jwtProfileVerifier) Offset() time.Duration {
return v.offset 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) request := new(oidc.JWTTokenRequest)
payload, err := oidc.ParseToken(profileRequest.Assertion, request) payload, err := oidc.ParseToken(assertion, request)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -73,10 +72,9 @@ func VerifyJWTAssertion(ctx context.Context, profileRequest *tokenexchange.JWTPr
keySet := &jwtProfileKeySet{v.Storage(), request.Subject} keySet := &jwtProfileKeySet{v.Storage(), request.Subject}
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 return nil, err
} }
request.Scopes = profileRequest.Scope
return request, nil return request, nil
} }