-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TT-13185] Implement Password Flow OAuth #6649
Open
andrei-tyk
wants to merge
33
commits into
master
Choose a base branch
from
TT-13185-implement-oauth-password-flow
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 30 commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
9805eb2
TT-13184, working version, some refactoring to be done
andrei-tyk 040aca6
TT-13184, applied some code review feedback from the retry-request va…
andrei-tyk 6b4ef4a
TT-13184, fixed variable naming
andrei-tyk e6c0eb5
TT-13184, fixed comment of variable that was failing CI
andrei-tyk 40a85a0
TT-13184, godocs fixes
andrei-tyk 04b2cb9
TT-13184, CR feedback implementation
andrei-tyk c020678
TT-13184, fixed typo
andrei-tyk a359234
TT-13184, more godocs
andrei-tyk 6f26abc
TT-13184, make lint
andrei-tyk d4bcd96
TT-13184, removed extra fields
andrei-tyk 57c4ad7
Merge branch 'master' into TT-13184-upstream-oauth2
andrei-tyk 3178207
TT-13184, ensured cached token expires correctly in redis; added some…
andrei-tyk 6114705
Merge branch 'TT-13184-upstream-oauth2' of github.com:TykTechnologies…
andrei-tyk 3ad4f07
Merge branch 'master' into TT-13184-upstream-oauth2
andrei-tyk 26e19ce
TT-13184, CR feedback implementation
andrei-tyk 1e93a4d
TT-13184, fixed golang-ci lint actionable inputs
andrei-tyk 5143c21
Merge branch 'master' into TT-13184-upstream-oauth2
andrei-tyk 909ef6b
TT-13185, initial implementation of password oauth2 flow
andrei-tyk 9309853
TT-13184, linting issues
andrei-tyk ad6ff30
TT-13185, working variant
andrei-tyk fe46a0b
TT-13184, fixed schema issues
andrei-tyk 1f74f51
TT-13184, fixed schema issues part 2
andrei-tyk 90599fd
Merge branch 'TT-13184-upstream-oauth2' into TT-13185-implement-oauth…
andrei-tyk 95d8107
TT-13185, linting issues
andrei-tyk ef32f7e
TT-13185, fixed schema issues
andrei-tyk e49443e
TT-13184, fixed linter issues
andrei-tyk bb1b3e2
Merge branch 'TT-13184-upstream-oauth2' into TT-13185-implement-oauth…
andrei-tyk db442a6
TT-13185, linter fixes
andrei-tyk 4f9b416
TT-13185, implemented CR feedback and added schema changes
andrei-tyk cad722f
Merge branch 'master' into TT-13185-implement-oauth-password-flow
andrei-tyk bed31cb
TT-13185, implemented CR feedback 2
andrei-tyk 0870da6
TT-13185, implemented CR feedback 3
andrei-tyk a171643
TT-13185, moved headerName to clientCredentials and passwordAuthentic…
andrei-tyk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,14 +34,63 @@ type ClientCredentialsOAuthProvider struct{} | |
|
||
type PerAPIClientCredentialsOAuthProvider struct{} | ||
|
||
func newUpstreamOAuthClientCredentialsCache(connectionHandler *storage.ConnectionHandler) *upstreamOAuthClientCredentialsCache { | ||
type PasswordOAuthProvider struct{} | ||
|
||
func newUpstreamOAuthClientCredentialsCache(connectionHandler *storage.ConnectionHandler) UpstreamOAuthCache { | ||
return &upstreamOAuthClientCredentialsCache{RedisCluster: storage.RedisCluster{KeyPrefix: "upstreamOAuthCC-", ConnectionHandler: connectionHandler}} | ||
} | ||
|
||
func newUpstreamOAuthPasswordCache(connectionHandler *storage.ConnectionHandler) UpstreamOAuthCache { | ||
return &upstreamOAuthPasswordCache{RedisCluster: storage.RedisCluster{KeyPrefix: "upstreamOAuthPW-", ConnectionHandler: connectionHandler}} | ||
} | ||
|
||
type upstreamOAuthClientCredentialsCache struct { | ||
storage.RedisCluster | ||
} | ||
|
||
type upstreamOAuthPasswordCache struct { | ||
storage.RedisCluster | ||
} | ||
|
||
func (cache *upstreamOAuthPasswordCache) getToken(r *http.Request, OAuthSpec *UpstreamOAuth) (string, error) { | ||
cacheKey := generatePasswordOAuthCacheKey(OAuthSpec.Spec.UpstreamAuth.OAuth, OAuthSpec.Spec.APIID) | ||
|
||
tokenString, err := retryGetKeyAndLock(cacheKey, &cache.RedisCluster) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if tokenString != "" { | ||
decryptedToken := decrypt(getPaddedSecret(OAuthSpec.Gw.GetConfig().Secret), tokenString) | ||
return decryptedToken, nil | ||
} | ||
|
||
token, err := cache.obtainToken(r.Context(), OAuthSpec) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
encryptedToken := encrypt(getPaddedSecret(OAuthSpec.Gw.GetConfig().Secret), token.AccessToken) | ||
|
||
ttl := time.Until(token.Expiry) | ||
if err := setTokenInCache(cacheKey, encryptedToken, ttl, &cache.RedisCluster); err != nil { | ||
return "", err | ||
} | ||
|
||
return token.AccessToken, nil | ||
} | ||
|
||
func (cache *upstreamOAuthPasswordCache) obtainToken(ctx context.Context, OAuthSpec *UpstreamOAuth) (*oauth2.Token, error) { | ||
cfg := newOAuth2PasswordConfig(OAuthSpec) | ||
|
||
token, err := cfg.PasswordCredentialsToken(ctx, OAuthSpec.Spec.UpstreamAuth.OAuth.PasswordAuthentication.Username, OAuthSpec.Spec.UpstreamAuth.OAuth.PasswordAuthentication.Password) | ||
if err != nil { | ||
return &oauth2.Token{}, err | ||
} | ||
|
||
return token, nil | ||
} | ||
|
||
type UpstreamOAuthCache interface { | ||
// getToken returns the token from cache or issues a request to obtain it from the OAuth provider. | ||
getToken(r *http.Request, OAuthSpec *UpstreamOAuth) (string, error) | ||
|
@@ -102,8 +151,20 @@ func (OAuthSpec *UpstreamOAuth) ProcessRequest(_ http.ResponseWriter, r *http.Re | |
} | ||
|
||
func getOAuthHeaderProvider(oauthConfig apidef.UpstreamOAuth) (OAuthHeaderProvider, error) { | ||
// to be extended when PasswordAuth is implemented | ||
return &ClientCredentialsOAuthProvider{}, nil | ||
if !oauthConfig.IsEnabled() { | ||
return nil, fmt.Errorf("upstream OAuth is not enabled") | ||
} | ||
|
||
switch { | ||
case oauthConfig.ClientCredentials.ClientID != "" && oauthConfig.PasswordAuthentication.ClientID != "": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return nil, fmt.Errorf("both client credentials and password authentication are provided") | ||
case oauthConfig.ClientCredentials.ClientID != "": | ||
return &ClientCredentialsOAuthProvider{}, nil | ||
case oauthConfig.PasswordAuthentication.ClientID != "": | ||
return &PasswordOAuthProvider{}, nil | ||
default: | ||
return nil, fmt.Errorf("no valid OAuth configuration provided") | ||
} | ||
} | ||
|
||
func (p *PerAPIClientCredentialsOAuthProvider) getOAuthHeaderValue(r *http.Request, OAuthSpec *UpstreamOAuth) (string, error) { | ||
|
@@ -143,7 +204,34 @@ func (p *ClientCredentialsOAuthProvider) getOAuthToken(r *http.Request, OAuthSpe | |
return fmt.Sprintf("Bearer %s", token), nil | ||
} | ||
|
||
func generateCacheKey(config apidef.UpstreamOAuth, apiId string) string { | ||
func (p *PasswordOAuthProvider) getOAuthToken(r *http.Request, OAuthSpec *UpstreamOAuth) (string, error) { | ||
if OAuthSpec.Gw.UpstreamOAuthCache == nil { | ||
OAuthSpec.Gw.UpstreamOAuthCache = newUpstreamOAuthPasswordCache(OAuthSpec.Gw.StorageConnectionHandler) | ||
} | ||
|
||
token, err := OAuthSpec.Gw.UpstreamOAuthCache.getToken(r, OAuthSpec) | ||
if err != nil { | ||
return handleOAuthError(r, OAuthSpec, err) | ||
} | ||
|
||
return fmt.Sprintf("Bearer %s", token), nil | ||
|
||
} | ||
|
||
func generatePasswordOAuthCacheKey(config apidef.UpstreamOAuth, apiId string) string { | ||
key := fmt.Sprintf( | ||
"%s|%s|%s|%s", | ||
apiId, | ||
config.PasswordAuthentication.ClientID, | ||
config.PasswordAuthentication.ClientSecret, | ||
strings.Join(config.PasswordAuthentication.Scopes, ",")) | ||
|
||
hash := sha256.New() | ||
hash.Write([]byte(key)) | ||
return hex.EncodeToString(hash.Sum(nil)) | ||
} | ||
|
||
func generateClientCredentialsCacheKey(config apidef.UpstreamOAuth, apiId string) string { | ||
key := fmt.Sprintf( | ||
"%s|%s|%s|%s", | ||
apiId, | ||
|
@@ -157,15 +245,15 @@ func generateCacheKey(config apidef.UpstreamOAuth, apiId string) string { | |
} | ||
|
||
func (cache *upstreamOAuthClientCredentialsCache) getToken(r *http.Request, OAuthSpec *UpstreamOAuth) (string, error) { | ||
cacheKey := generateCacheKey(OAuthSpec.Spec.UpstreamAuth.OAuth, OAuthSpec.Spec.APIID) | ||
cacheKey := generateClientCredentialsCacheKey(OAuthSpec.Spec.UpstreamAuth.OAuth, OAuthSpec.Spec.APIID) | ||
|
||
tokenString, err := retryGetKeyAndLock(cacheKey, &cache.RedisCluster) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if tokenString != "" { | ||
decryptedToken := decrypt(getPaddedSecret(OAuthSpec.Gw), tokenString) | ||
decryptedToken := decrypt(getPaddedSecret(OAuthSpec.Gw.GetConfig().Secret), tokenString) | ||
return decryptedToken, nil | ||
} | ||
|
||
|
@@ -174,7 +262,7 @@ func (cache *upstreamOAuthClientCredentialsCache) getToken(r *http.Request, OAut | |
return "", err | ||
} | ||
|
||
encryptedToken := encrypt(getPaddedSecret(OAuthSpec.Gw), token.AccessToken) | ||
encryptedToken := encrypt(getPaddedSecret(OAuthSpec.Gw.GetConfig().Secret), token.AccessToken) | ||
|
||
ttl := time.Until(token.Expiry) | ||
if err := setTokenInCache(cacheKey, encryptedToken, ttl, &cache.RedisCluster); err != nil { | ||
|
@@ -218,6 +306,17 @@ func newOAuth2ClientCredentialsConfig(OAuthSpec *UpstreamOAuth) oauth2clientcred | |
} | ||
} | ||
|
||
func newOAuth2PasswordConfig(OAuthSpec *UpstreamOAuth) oauth2.Config { | ||
return oauth2.Config{ | ||
ClientID: OAuthSpec.Spec.UpstreamAuth.OAuth.PasswordAuthentication.ClientID, | ||
ClientSecret: OAuthSpec.Spec.UpstreamAuth.OAuth.PasswordAuthentication.ClientSecret, | ||
Endpoint: oauth2.Endpoint{ | ||
TokenURL: OAuthSpec.Spec.UpstreamAuth.OAuth.PasswordAuthentication.TokenURL, | ||
}, | ||
Scopes: OAuthSpec.Spec.UpstreamAuth.OAuth.PasswordAuthentication.Scopes, | ||
} | ||
} | ||
|
||
func (cache *upstreamOAuthClientCredentialsCache) obtainToken(ctx context.Context, OAuthSpec *UpstreamOAuth) (*oauth2.Token, error) { | ||
cfg := newOAuth2ClientCredentialsConfig(OAuthSpec) | ||
|
||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that was some unintended typo , wil revert it to the one agreed in the schema