Go API Documentation

github.com/TykTechnologies/tyk/ee/middleware/upstreamoauth

No package summary is available.

Package

Files: 6. Third party imports: 3. Imports from organisation: 0. Tests: 0. Benchmarks: 0.

Constants

const (
	MiddlewareName	= "UpstreamOAuth"

	ClientCredentialsAuthorizeType	= "clientCredentials"
	PasswordAuthorizeType		= "password"
)

Vars

var (
	ctxData	= httpctx.NewValue[map[string]any](ctx.ContextData)

	CtxGetData	= ctxData.Get
	CtxSetData	= ctxData.Set
)

Middleware implements model.Middleware.

var _ model.Middleware = &Middleware{}

Types

BaseMiddleware

BaseMiddleware is the subset of BaseMiddleware APIs that the middleware uses.

Field name Field type Comment
type

any

No comment on field.
type BaseMiddleware interface {
	model.LoggerProvider
	FireEvent(name apidef.TykEvent, meta interface{})
}

Cache

This type doesn't have documentation.

Field name Field type Comment
type

any

No comment on field.
type Cache interface {
	// GetToken returns the token from cache or issues a request to obtain it from the OAuth provider.
	GetToken(r *http.Request) (string, error)
	// ObtainToken issues a request to obtain the token from the OAuth provider.
	ObtainToken(ctx context.Context) (*oauth2.Token, error)
}

ClientCredentialsClient

This type doesn't have documentation.

Field name Field type Comment
mw

*Middleware

No comment on field.
type ClientCredentialsClient struct {
	mw *Middleware
}

ClientCredentialsOAuthProvider

This type doesn't have documentation.

type ClientCredentialsOAuthProvider struct{}

EventUpstreamOAuthMeta

EventUpstreamOAuthMeta is the metadata structure for an upstream OAuth event

Field name Field type Comment

model.EventMetaDefault

No comment on field.
APIID

string

No comment on field.
type EventUpstreamOAuthMeta struct {
	model.EventMetaDefault
	APIID	string
}

Gateway

Gateway is the subset of Gateway APIs that the middleware uses.

Field name Field type Comment
type

any

No comment on field.
type Gateway interface {
	model.ConfigProvider
}

Middleware

Middleware implements upstream OAuth middleware.

Field name Field type Comment
Spec

model.MergedAPI

No comment on field.
Gw

Gateway

No comment on field.
Base

BaseMiddleware

No comment on field.
clientCredentialsStorageHandler

Storage

No comment on field.
passwordStorageHandler

Storage

No comment on field.
type Middleware struct {
	Spec	model.MergedAPI
	Gw	Gateway

	Base	BaseMiddleware

	clientCredentialsStorageHandler	Storage
	passwordStorageHandler		Storage
}

OAuthHeaderProvider

This type doesn't have documentation.

Field name Field type Comment
type

any

No comment on field.
type OAuthHeaderProvider interface {
	// getOAuthToken returns the OAuth token for the request.
	getOAuthToken(r *http.Request, mw *Middleware) (string, error)
	// getHeaderName returns the header name for the OAuth token.
	getHeaderName(mw *Middleware) string
	//
	headerEnabled(mw *Middleware) bool
}

PasswordClient

This type doesn't have documentation.

Field name Field type Comment
mw

*Middleware

No comment on field.
type PasswordClient struct {
	mw *Middleware
}

PasswordOAuthProvider

This type doesn't have documentation.

type PasswordOAuthProvider struct{}

PerAPIClientCredentialsOAuthProvider

This type doesn't have documentation.

type PerAPIClientCredentialsOAuthProvider struct{}

Provider

Provider implements upstream auth provider.

Field name Field type Comment
Logger

*logrus.Entry

Logger is the logger to be used.

HeaderName

string

HeaderName is the header name to be used to fill upstream auth with.

AuthValue

string

AuthValue is the value of auth header.

type Provider struct {
	// Logger is the logger to be used.
	Logger	*logrus.Entry
	// HeaderName is the header name to be used to fill upstream auth with.
	HeaderName	string
	// AuthValue is the value of auth header.
	AuthValue	string
}

Storage

Type Storage is a subset of storage.RedisCluster

Field name Field type Comment
type

any

No comment on field.
type Storage interface {
	GetKey(key string) (string, error)
	SetKey(string, string, int64) error
	Lock(key string, timeout time.Duration) (bool, error)
}

TokenData

This type doesn't have documentation.

Field name Field type Comment
Token

string

No comment on field.
ExtraMetadata

map[string]any

No comment on field.
type TokenData struct {
	Token		string			`json:"token"`
	ExtraMetadata	map[string]interface{}	`json:"extra_metadata"`
}

Functions

func BuildMetadataMap

func BuildMetadataMap(token *oauth2.Token, extraMetadataKeys []string) map[string]interface{} {
	metadataMap := make(map[string]interface{})
	for _, key := range extraMetadataKeys {
		if val := token.Extra(key); val != "" && val != nil {
			metadataMap[key] = val
		}
	}
	return metadataMap
}

Cognitive complexity: 7, Cyclomatic complexity: 4

func CreateTokenDataBytes

func CreateTokenDataBytes(encryptedToken string, token *oauth2.Token, extraMetadataKeys []string) ([]byte, error) {
	td := TokenData{
		Token:		encryptedToken,
		ExtraMetadata:	BuildMetadataMap(token, extraMetadataKeys),
	}
	return json.Marshal(td)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: json.Marshal.

func NewMiddleware

NewMiddleware returns a new instance of Middleware.

func NewMiddleware(gw Gateway, mw BaseMiddleware, spec model.MergedAPI, ccStorageHandler Storage, pwStorageHandler Storage) *Middleware {
	return &Middleware{
		Base:					mw,
		Gw:					gw,
		Spec:					spec,
		clientCredentialsStorageHandler:	ccStorageHandler,
		passwordStorageHandler:			pwStorageHandler,
	}
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func NewOAuthHeaderProvider

func NewOAuthHeaderProvider(oauthConfig apidef.UpstreamOAuth) (OAuthHeaderProvider, error) {
	if !oauthConfig.IsEnabled() {
		return nil, fmt.Errorf("upstream OAuth is not enabled")
	}

	switch {
	case len(oauthConfig.AllowedAuthorizeTypes) == 0:
		return nil, fmt.Errorf("no OAuth configuration selected")
	case len(oauthConfig.AllowedAuthorizeTypes) > 1:
		return nil, fmt.Errorf("both client credentials and password authentication are provided")
	case oauthConfig.AllowedAuthorizeTypes[0] == ClientCredentialsAuthorizeType:
		return &ClientCredentialsOAuthProvider{}, nil
	case oauthConfig.AllowedAuthorizeTypes[0] == PasswordAuthorizeType:
		return &PasswordOAuthProvider{}, nil
	default:
		return nil, fmt.Errorf("no valid OAuth configuration provided")
	}
}

Cognitive complexity: 10, Cyclomatic complexity: 7

Uses: fmt.Errorf.

func SetExtraMetadata

func SetExtraMetadata(r *http.Request, keyList []string, metadata map[string]interface{}) {
	contextDataObject := CtxGetData(r)
	if contextDataObject == nil {
		contextDataObject = make(map[string]interface{})
	}
	for _, key := range keyList {
		if val, ok := metadata[key]; ok && val != "" {
			contextDataObject[key] = val
		}
	}
	CtxSetData(r, contextDataObject)
}

Cognitive complexity: 9, Cyclomatic complexity: 5

func UnmarshalTokenData

func UnmarshalTokenData(tokenData string) (TokenData, error) {
	var tokenContents TokenData
	err := json.Unmarshal([]byte(tokenData), &tokenContents)
	if err != nil {
		return TokenData{}, fmt.Errorf("failed to unmarshal token data: %w", err)
	}
	return tokenContents, nil
}

Cognitive complexity: 3, Cyclomatic complexity: 2

Uses: fmt.Errorf, json.Unmarshal.

func (*ClientCredentialsClient) GetToken

func (cache *ClientCredentialsClient) GetToken(r *http.Request) (string, error) {
	cacheKey := generateClientCredentialsCacheKey(cache.mw.Spec.UpstreamAuth.OAuth, cache.mw.Spec.APIID)
	secret := cache.mw.Gw.GetConfig().Secret
	extraMetadata := cache.mw.Spec.UpstreamAuth.OAuth.ClientCredentials.ExtraMetadata

	obtainTokenFunc := func(ctx context.Context) (*oauth2.Token, error) {
		return cache.ObtainToken(ctx)
	}

	return getToken(r, cacheKey, obtainTokenFunc, secret, extraMetadata, cache.mw.clientCredentialsStorageHandler)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: context.Context, oauth2.Token.

func (*ClientCredentialsClient) ObtainToken

func (cache *ClientCredentialsClient) ObtainToken(ctx context.Context) (*oauth2.Token, error) {
	cfg := newOAuth2ClientCredentialsConfig(cache.mw)
	tokenSource := cfg.TokenSource(ctx)
	return tokenSource.Token()
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (*Middleware) EnabledForSpec

EnabledForSpec checks if streaming is enabled on the config.

func (m *Middleware) EnabledForSpec() bool {
	if !m.Spec.UpstreamAuth.IsEnabled() {
		return false
	}

	if !m.Spec.UpstreamAuth.OAuth.Enabled {
		return false
	}

	return true
}

Cognitive complexity: 4, Cyclomatic complexity: 3

func (*Middleware) FireEvent

FireEvent emits an upstream OAuth event with an optional custom message.

func (mw *Middleware) FireEvent(r *http.Request, e event.Event, message string, apiId string) {
	if message == "" {
		message = event.String(e)
	}
	mw.Base.FireEvent(e, EventUpstreamOAuthMeta{
		EventMetaDefault:	model.NewEventMetaDefault(r, message),
		APIID:			apiId,
	})
}

Cognitive complexity: 3, Cyclomatic complexity: 2

Uses: event.String, model.NewEventMetaDefault.

func (*Middleware) Init

Init initializes the middleware.

func (m *Middleware) Init() {
	m.Logger().Debug("Initializing Upstream basic auth Middleware")
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (*Middleware) Logger

Logger returns a logger with middleware filled out.

func (m *Middleware) Logger() *logrus.Entry {
	return m.Base.Logger().WithField("mw", m.Name())
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (*Middleware) Name

Name returns the name for the middleware.

func (m *Middleware) Name() string {
	return MiddlewareName
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (*Middleware) ProcessRequest

ProcessRequest will handle upstream OAuth.

func (m *Middleware) ProcessRequest(_ http.ResponseWriter, r *http.Request, _ interface{}) (error, int) {
	provider, err := NewOAuthHeaderProvider(m.Spec.UpstreamAuth.OAuth)
	if err != nil {
		return fmt.Errorf("failed to get OAuth header provider: %w", err), http.StatusInternalServerError
	}

	payload, err := provider.getOAuthToken(r, m)
	if err != nil {
		return fmt.Errorf("failed to get OAuth token: %w", err), http.StatusInternalServerError
	}

	upstreamOAuthProvider := Provider{
		HeaderName:	header.Authorization,
		AuthValue:	payload,
		Logger:		m.Logger(),
	}

	headerName := provider.getHeaderName(m)
	if headerName != "" {
		upstreamOAuthProvider.HeaderName = headerName
	}

	if provider.headerEnabled(m) {
		headerName := provider.getHeaderName(m)
		if headerName != "" {
			upstreamOAuthProvider.HeaderName = headerName
		}
	}

	core.SetUpstreamAuth(r, upstreamOAuthProvider)
	return nil, http.StatusOK
}

Cognitive complexity: 12, Cyclomatic complexity: 6

Uses: core.SetUpstreamAuth, fmt.Errorf, header.Authorization, http.StatusInternalServerError, http.StatusOK.

func (*Middleware) Unload

func (m *Middleware) Unload() {
	// nothing to do here
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (*PasswordClient) GetToken

func (cache *PasswordClient) GetToken(r *http.Request) (string, error) {
	cacheKey := generatePasswordOAuthCacheKey(cache.mw.Spec.UpstreamAuth.OAuth, cache.mw.Spec.APIID)
	secret := cache.mw.Gw.GetConfig().Secret
	extraMetadata := cache.mw.Spec.UpstreamAuth.OAuth.PasswordAuthentication.ExtraMetadata

	obtainTokenFunc := func(ctx context.Context) (*oauth2.Token, error) {
		return cache.ObtainToken(ctx)
	}

	return getToken(r, cacheKey, obtainTokenFunc, secret, extraMetadata, cache.mw.passwordStorageHandler)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: context.Context, oauth2.Token.

func (*PasswordClient) ObtainToken

func (cache *PasswordClient) ObtainToken(ctx context.Context) (*oauth2.Token, error) {
	cfg := newOAuth2PasswordConfig(cache.mw)
	return cfg.PasswordCredentialsToken(ctx, cache.mw.Spec.UpstreamAuth.OAuth.PasswordAuthentication.Username, cache.mw.Spec.UpstreamAuth.OAuth.PasswordAuthentication.Password)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (Provider) Fill

Fill sets the request's HeaderName with AuthValue

func (u Provider) Fill(r *http.Request) {
	if r.Header.Get(u.HeaderName) != "" {
		u.Logger.WithFields(logrus.Fields{
			"header": u.HeaderName,
		}).Info("Authorization header conflict detected: Client header overwritten by Gateway upstream authentication header.")
	}
	r.Header.Set(u.HeaderName, u.AuthValue)
}

Cognitive complexity: 3, Cyclomatic complexity: 2

Uses: logrus.Fields.

Private functions

func generateClientCredentialsCacheKey

generateClientCredentialsCacheKey (config apidef.UpstreamOAuth, apiId string) string
References: fmt.Sprintf, hex.EncodeToString, sha256.New, strings.Join.

func generatePasswordOAuthCacheKey

generatePasswordOAuthCacheKey (config apidef.UpstreamOAuth, apiId string) string
References: fmt.Sprintf, hex.EncodeToString, sha256.New, strings.Join.

func getToken

getToken (r *http.Request, cacheKey string, obtainTokenFunc func(context.Context) (*oauth2.Token, error), secret string, extraMetadata []string, cache Storage) (string, error)
References: crypto.Decrypt, crypto.Encrypt, crypto.GetPaddedString, time.Until.

func handleOAuthError

handleOAuthError (r *http.Request, mw *Middleware, err error) (string, error)
References: event.UpstreamOAuthError.

func newOAuth2ClientCredentialsConfig

newOAuth2ClientCredentialsConfig (OAuthSpec *Middleware) oauth2clientcredentials.Config
References: oauth2clientcredentials.Config.

func newOAuth2PasswordConfig

newOAuth2PasswordConfig (OAuthSpec *Middleware) oauth2.Config
References: oauth2.Config, oauth2.Endpoint.

func retryGetKeyAndLock

retryGetKeyAndLock (cacheKey string, cache Storage) (string, error)
References: fmt.Errorf, time.Millisecond, time.Second, time.Sleep.

func setTokenInCache

setTokenInCache (cache Storage, cacheKey string, token string, ttl time.Duration) error
References: time.Now, time.Until.

func getHeaderName

getHeaderName (OAuthSpec *Middleware) string

func getOAuthToken

getOAuthToken (r *http.Request, mw *Middleware) (string, error)
References: fmt.Sprintf.

func headerEnabled

headerEnabled (OAuthSpec *Middleware) bool

func getHeaderName

getHeaderName (OAuthSpec *Middleware) string

func getOAuthToken

getOAuthToken (r *http.Request, mw *Middleware) (string, error)
References: fmt.Sprintf.

func headerEnabled

headerEnabled (OAuthSpec *Middleware) bool


Tests

Files: 1. Third party imports: 2. Imports from organisation: 0. Tests: 6. Benchmarks: 0.

Constants

const ClientCredentialsAuthorizeType = upstreamoauth.ClientCredentialsAuthorizeType
const PasswordAuthorizeType = upstreamoauth.PasswordAuthorizeType

Vars

var StartTest = gateway.StartTest

Types

APISpec

This type doesn't have documentation.

Field name Field type Comment
type

gateway.APISpec

No comment on field.
type APISpec = gateway.APISpec

Test functions

TestBuildMetadataMap

References: assert.Equal, assert.NotContains, oauth2.Token, time.Hour, time.Now, upstreamoauth.BuildMetadataMap.

TestCreateTokenDataBytes

References: assert.Equal, assert.NoError, assert.NotContains, json.Unmarshal, oauth2.Token, time.Hour, time.Now, upstreamoauth.CreateTokenDataBytes, upstreamoauth.TokenData.

TestProvider_ClientCredentialsAuthorizeType

References: apidef.AuthSource, apidef.ClientAuthData, apidef.ClientCredentials, apidef.OAuthAuthorizationTypeClientCredentials, apidef.UpstreamAuth, apidef.UpstreamOAuth, assert.Contains, assert.Equal, assert.Fail, assert.NoError, assert.NotEmpty, header.Authorization, http.HandlerFunc, http.Request, http.ResponseWriter, http.StatusOK, httptest.NewServer, io.ReadAll, json.Unmarshal, test.TestCases.

TestProvider_PasswordAuthorizeType

References: apidef.AuthSource, apidef.ClientAuthData, apidef.OAuthAuthorizationTypePassword, apidef.PasswordAuthentication, apidef.UpstreamAuth, apidef.UpstreamOAuth, assert.Contains, assert.Equal, assert.Fail, assert.NoError, assert.NotEmpty, header.Authorization, http.HandlerFunc, http.Request, http.ResponseWriter, http.StatusOK, httptest.NewServer, io.ReadAll, json.Unmarshal, test.TestCases.

TestSetExtraMetadata

References: assert.Equal, assert.NotContains, http.MethodGet, httptest.NewRequest, upstreamoauth.CtxGetData, upstreamoauth.SetExtraMetadata.

TestUnmarshalTokenData

References: assert.Equal, assert.NoError, json.Marshal, upstreamoauth.TokenData, upstreamoauth.UnmarshalTokenData.