fix: check grant types and add refresh token to discovery
This commit is contained in:
parent
8e884bdb9f
commit
14faebbb77
11 changed files with 72 additions and 7 deletions
|
@ -251,6 +251,7 @@ type ConfClient struct {
|
||||||
applicationType op.ApplicationType
|
applicationType op.ApplicationType
|
||||||
authMethod oidc.AuthMethod
|
authMethod oidc.AuthMethod
|
||||||
responseTypes []oidc.ResponseType
|
responseTypes []oidc.ResponseType
|
||||||
|
grantTypes []oidc.GrantType
|
||||||
ID string
|
ID string
|
||||||
accessTokenType op.AccessTokenType
|
accessTokenType op.AccessTokenType
|
||||||
devMode bool
|
devMode bool
|
||||||
|
@ -295,6 +296,9 @@ func (c *ConfClient) AccessTokenType() op.AccessTokenType {
|
||||||
func (c *ConfClient) ResponseTypes() []oidc.ResponseType {
|
func (c *ConfClient) ResponseTypes() []oidc.ResponseType {
|
||||||
return c.responseTypes
|
return c.responseTypes
|
||||||
}
|
}
|
||||||
|
func (c *ConfClient) GrantTypes() []oidc.GrantType {
|
||||||
|
return c.grantTypes
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ConfClient) DevMode() bool {
|
func (c *ConfClient) DevMode() bool {
|
||||||
return c.devMode
|
return c.devMode
|
||||||
|
|
|
@ -30,6 +30,7 @@ type Client interface {
|
||||||
ApplicationType() ApplicationType
|
ApplicationType() ApplicationType
|
||||||
AuthMethod() oidc.AuthMethod
|
AuthMethod() oidc.AuthMethod
|
||||||
ResponseTypes() []oidc.ResponseType
|
ResponseTypes() []oidc.ResponseType
|
||||||
|
GrantTypes() []oidc.GrantType
|
||||||
LoginURL(string) string
|
LoginURL(string) string
|
||||||
AccessTokenType() AccessTokenType
|
AccessTokenType() AccessTokenType
|
||||||
IDTokenLifetime() time.Duration
|
IDTokenLifetime() time.Duration
|
||||||
|
|
|
@ -21,6 +21,7 @@ type Configuration interface {
|
||||||
AuthMethodPostSupported() bool
|
AuthMethodPostSupported() bool
|
||||||
CodeMethodS256Supported() bool
|
CodeMethodS256Supported() bool
|
||||||
AuthMethodPrivateKeyJWTSupported() bool
|
AuthMethodPrivateKeyJWTSupported() bool
|
||||||
|
GrantTypeRefreshTokenSupported() bool
|
||||||
GrantTypeTokenExchangeSupported() bool
|
GrantTypeTokenExchangeSupported() bool
|
||||||
GrantTypeJWTAuthorizationSupported() bool
|
GrantTypeJWTAuthorizationSupported() bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,9 @@ func GrantTypes(c Configuration) []oidc.GrantType {
|
||||||
oidc.GrantTypeCode,
|
oidc.GrantTypeCode,
|
||||||
oidc.GrantTypeImplicit,
|
oidc.GrantTypeImplicit,
|
||||||
}
|
}
|
||||||
|
if c.GrantTypeRefreshTokenSupported() {
|
||||||
|
grantTypes = append(grantTypes, oidc.GrantTypeRefreshToken)
|
||||||
|
}
|
||||||
if c.GrantTypeTokenExchangeSupported() {
|
if c.GrantTypeTokenExchangeSupported() {
|
||||||
grantTypes = append(grantTypes, oidc.GrantTypeTokenExchange)
|
grantTypes = append(grantTypes, oidc.GrantTypeTokenExchange)
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,20 @@ func (mr *MockClientMockRecorder) GetID() *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetID", reflect.TypeOf((*MockClient)(nil).GetID))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetID", reflect.TypeOf((*MockClient)(nil).GetID))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GrantTypes mocks base method.
|
||||||
|
func (m *MockClient) GrantTypes() []oidc.GrantType {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GrantTypes")
|
||||||
|
ret0, _ := ret[0].([]oidc.GrantType)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrantTypes indicates an expected call of GrantTypes.
|
||||||
|
func (mr *MockClientMockRecorder) GrantTypes() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantTypes", reflect.TypeOf((*MockClient)(nil).GrantTypes))
|
||||||
|
}
|
||||||
|
|
||||||
// IDTokenLifetime mocks base method.
|
// IDTokenLifetime mocks base method.
|
||||||
func (m *MockClient) IDTokenLifetime() time.Duration {
|
func (m *MockClient) IDTokenLifetime() time.Duration {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|
|
@ -118,6 +118,20 @@ func (mr *MockConfigurationMockRecorder) GrantTypeJWTAuthorizationSupported() *g
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantTypeJWTAuthorizationSupported", reflect.TypeOf((*MockConfiguration)(nil).GrantTypeJWTAuthorizationSupported))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantTypeJWTAuthorizationSupported", reflect.TypeOf((*MockConfiguration)(nil).GrantTypeJWTAuthorizationSupported))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GrantTypeRefreshTokenSupported mocks base method.
|
||||||
|
func (m *MockConfiguration) GrantTypeRefreshTokenSupported() bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GrantTypeRefreshTokenSupported")
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GrantTypeRefreshTokenSupported indicates an expected call of GrantTypeRefreshTokenSupported.
|
||||||
|
func (mr *MockConfigurationMockRecorder) GrantTypeRefreshTokenSupported() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantTypeRefreshTokenSupported", reflect.TypeOf((*MockConfiguration)(nil).GrantTypeRefreshTokenSupported))
|
||||||
|
}
|
||||||
|
|
||||||
// GrantTypeTokenExchangeSupported mocks base method.
|
// GrantTypeTokenExchangeSupported mocks base method.
|
||||||
func (m *MockConfiguration) GrantTypeTokenExchangeSupported() bool {
|
func (m *MockConfiguration) GrantTypeTokenExchangeSupported() bool {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|
|
@ -107,6 +107,7 @@ type ConfClient struct {
|
||||||
authMethod oidc.AuthMethod
|
authMethod oidc.AuthMethod
|
||||||
accessTokenType op.AccessTokenType
|
accessTokenType op.AccessTokenType
|
||||||
responseTypes []oidc.ResponseType
|
responseTypes []oidc.ResponseType
|
||||||
|
grantTypes []oidc.GrantType
|
||||||
devMode bool
|
devMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,6 +151,9 @@ func (c *ConfClient) AccessTokenType() op.AccessTokenType {
|
||||||
func (c *ConfClient) ResponseTypes() []oidc.ResponseType {
|
func (c *ConfClient) ResponseTypes() []oidc.ResponseType {
|
||||||
return c.responseTypes
|
return c.responseTypes
|
||||||
}
|
}
|
||||||
|
func (c *ConfClient) GrantTypes() []oidc.GrantType {
|
||||||
|
return c.grantTypes
|
||||||
|
}
|
||||||
func (c *ConfClient) DevMode() bool {
|
func (c *ConfClient) DevMode() bool {
|
||||||
return c.devMode
|
return c.devMode
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ type Config struct {
|
||||||
DefaultLogoutRedirectURI string
|
DefaultLogoutRedirectURI string
|
||||||
CodeMethodS256 bool
|
CodeMethodS256 bool
|
||||||
AuthMethodPrivateKeyJWT bool
|
AuthMethodPrivateKeyJWT bool
|
||||||
|
GrantTypeRefreshToken bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type endpoints struct {
|
type endpoints struct {
|
||||||
|
@ -189,6 +190,10 @@ func (o *openidProvider) AuthMethodPrivateKeyJWTSupported() bool {
|
||||||
return o.config.AuthMethodPrivateKeyJWT
|
return o.config.AuthMethodPrivateKeyJWT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *openidProvider) GrantTypeRefreshTokenSupported() bool {
|
||||||
|
return o.config.GrantTypeRefreshToken
|
||||||
|
}
|
||||||
|
|
||||||
func (o *openidProvider) GrantTypeTokenExchangeSupported() bool {
|
func (o *openidProvider) GrantTypeTokenExchangeSupported() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,18 +53,18 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storage, refreshToken string) (id, newRefreshToken string, exp time.Time, err error) {
|
func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storage, refreshToken string, client Client) (id, newRefreshToken string, exp time.Time, err error) {
|
||||||
if needsRefreshToken(tokenRequest) {
|
if needsRefreshToken(tokenRequest, client) {
|
||||||
return storage.CreateAccessAndRefreshTokens(ctx, tokenRequest, refreshToken)
|
return storage.CreateAccessAndRefreshTokens(ctx, tokenRequest, refreshToken)
|
||||||
}
|
}
|
||||||
id, exp, err = storage.CreateAccessToken(ctx, tokenRequest)
|
id, exp, err = storage.CreateAccessToken(ctx, tokenRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func needsRefreshToken(tokenRequest TokenRequest) bool {
|
func needsRefreshToken(tokenRequest TokenRequest, client Client) bool {
|
||||||
switch req := tokenRequest.(type) {
|
switch req := tokenRequest.(type) {
|
||||||
case AuthRequest:
|
case AuthRequest:
|
||||||
return utils.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && req.GetResponseType() == oidc.ResponseTypeCode
|
return utils.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && req.GetResponseType() == oidc.ResponseTypeCode && ValidateGrantType(client, oidc.GrantTypeRefreshToken)
|
||||||
case RefreshTokenRequest:
|
case RefreshTokenRequest:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
|
@ -73,7 +73,7 @@ func needsRefreshToken(tokenRequest TokenRequest) 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 Client, refreshToken string) (accessToken, newRefreshToken string, validity time.Duration, err error) {
|
||||||
id, newRefreshToken, exp, err := createTokens(ctx, tokenRequest, creator.Storage(), refreshToken)
|
id, newRefreshToken, exp, err := createTokens(ctx, tokenRequest, creator.Storage(), refreshToken, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, err
|
return "", "", 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,9 @@ func ValidateAccessTokenRequest(ctx context.Context, tokenReq *oidc.AccessTokenR
|
||||||
if client.GetID() != authReq.GetClientID() {
|
if client.GetID() != authReq.GetClientID() {
|
||||||
return nil, nil, ErrInvalidRequest("invalid auth code")
|
return nil, nil, ErrInvalidRequest("invalid auth code")
|
||||||
}
|
}
|
||||||
|
if !ValidateGrantType(client, oidc.GrantTypeCode) {
|
||||||
|
return nil, nil, ErrInvalidRequest("invalid_grant")
|
||||||
|
}
|
||||||
if tokenReq.RedirectURI != authReq.GetRedirectURI() {
|
if tokenReq.RedirectURI != authReq.GetRedirectURI() {
|
||||||
return nil, nil, ErrInvalidRequest("redirect_uri does not correspond")
|
return nil, nil, ErrInvalidRequest("redirect_uri does not correspond")
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ type Exchanger interface {
|
||||||
Crypto() Crypto
|
Crypto() Crypto
|
||||||
AuthMethodPostSupported() bool
|
AuthMethodPostSupported() bool
|
||||||
AuthMethodPrivateKeyJWTSupported() bool
|
AuthMethodPrivateKeyJWTSupported() bool
|
||||||
|
GrantTypeRefreshTokenSupported() bool
|
||||||
GrantTypeTokenExchangeSupported() bool
|
GrantTypeTokenExchangeSupported() bool
|
||||||
GrantTypeJWTAuthorizationSupported() bool
|
GrantTypeJWTAuthorizationSupported() bool
|
||||||
}
|
}
|
||||||
|
@ -28,8 +29,10 @@ func tokenHandler(exchanger Exchanger) func(w http.ResponseWriter, r *http.Reque
|
||||||
CodeExchange(w, r, exchanger)
|
CodeExchange(w, r, exchanger)
|
||||||
return
|
return
|
||||||
case string(oidc.GrantTypeRefreshToken):
|
case string(oidc.GrantTypeRefreshToken):
|
||||||
RefreshTokenExchange(w, r, exchanger)
|
if exchanger.GrantTypeRefreshTokenSupported() {
|
||||||
return
|
RefreshTokenExchange(w, r, exchanger)
|
||||||
|
return
|
||||||
|
}
|
||||||
case string(oidc.GrantTypeBearer):
|
case string(oidc.GrantTypeBearer):
|
||||||
if ex, ok := exchanger.(JWTAuthorizationGrantExchanger); ok && exchanger.GrantTypeJWTAuthorizationSupported() {
|
if ex, ok := exchanger.(JWTAuthorizationGrantExchanger); ok && exchanger.GrantTypeJWTAuthorizationSupported() {
|
||||||
JWTProfile(w, r, ex)
|
JWTProfile(w, r, ex)
|
||||||
|
@ -119,3 +122,16 @@ func AuthorizePrivateJWTKey(ctx context.Context, clientAssertion string, exchang
|
||||||
}
|
}
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ValidateGrantType ensures that the requested grant_type is allowed by the Client
|
||||||
|
func ValidateGrantType(client Client, grantType oidc.GrantType) bool {
|
||||||
|
if client == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, grant := range client.GrantTypes() {
|
||||||
|
if grantType == grant {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue