From a93ef49d24aaac0b331035af0b15c76e16b4f4f4 Mon Sep 17 00:00:00 2001 From: pmahindrakar-oss Date: Fri, 2 Sep 2022 11:28:12 +0530 Subject: [PATCH] Adding device authorization oauth2 flow (#313) * Added config skip opening browser for pkce auth Signed-off-by: Prafulla Mahindrakar * added docs Signed-off-by: Prafulla Mahindrakar * increased the default browser session timeout to 2min Signed-off-by: Prafulla Mahindrakar * Adding device flow idl changes Signed-off-by: Prafulla Mahindrakar * Adding device flow orchestration Signed-off-by: Prafulla Mahindrakar * lint fixes Signed-off-by: Prafulla Mahindrakar * nit Signed-off-by: Prafulla Mahindrakar * fixes Signed-off-by: Prafulla Mahindrakar * refactor and feedback Signed-off-by: Prafulla Mahindrakar * nit Signed-off-by: Prafulla Mahindrakar * test fixes Signed-off-by: Prafulla Mahindrakar * more test fixes Signed-off-by: Prafulla Mahindrakar * feedback Signed-off-by: Prafulla Mahindrakar Signed-off-by: Prafulla Mahindrakar --- clients/go/admin/authtype_enumer.go | 7 +- .../{pkce => cache}/mocks/token_cache.go | 0 .../go/admin/{pkce => cache}/token_cache.go | 2 +- .../{pkce => cache}/token_cache_inmemory.go | 2 +- clients/go/admin/client.go | 14 +- clients/go/admin/client_builder.go | 8 +- clients/go/admin/client_builder_test.go | 7 +- clients/go/admin/client_test.go | 38 ++- clients/go/admin/config.go | 19 +- clients/go/admin/config_flags.go | 7 +- clients/go/admin/config_flags_test.go | 42 ++++ clients/go/admin/deviceflow/config.go | 10 + clients/go/admin/deviceflow/payload.go | 42 ++++ .../go/admin/deviceflow/token_orchestrator.go | 174 ++++++++++++++ .../deviceflow/token_orchestrator_test.go | 103 +++++++++ clients/go/admin/oauth/config.go | 43 ++++ .../config_test.go} | 12 +- .../go/admin/pkce/auth_flow_orchestrator.go | 111 +++------ .../admin/pkce/auth_flow_orchestrator_test.go | 76 ++---- clients/go/admin/pkce/client_config.go | 34 --- clients/go/admin/pkce/config.go | 4 +- clients/go/admin/pkce/handle_app_call_back.go | 4 +- .../admin/pkce/handle_app_call_back_test.go | 6 +- clients/go/admin/token_source_provider.go | 78 ++++++- .../base_token_orchestrator.go | 94 ++++++++ .../base_token_orchestrator_test.go | 137 +++++++++++ .../testdata/token.json | 0 gen/pb-cpp/flyteidl/service/auth.pb.cc | 123 ++++++++-- gen/pb-cpp/flyteidl/service/auth.pb.h | 68 ++++++ gen/pb-go/flyteidl/service/auth.pb.go | 108 +++++---- gen/pb-go/flyteidl/service/auth.swagger.json | 4 + gen/pb-java/flyteidl/service/Auth.java | 218 ++++++++++++++++-- gen/pb-js/flyteidl.d.ts | 6 + gen/pb-js/flyteidl.js | 17 ++ gen/pb_python/flyteidl/service/auth_pb2.py | 23 +- go.mod | 2 +- go.sum | 9 - protos/docs/service/service.rst | 1 + protos/flyteidl/service/auth.proto | 3 + 39 files changed, 1303 insertions(+), 353 deletions(-) rename clients/go/admin/{pkce => cache}/mocks/token_cache.go (100%) rename clients/go/admin/{pkce => cache}/token_cache.go (96%) rename clients/go/admin/{pkce => cache}/token_cache_inmemory.go (96%) create mode 100644 clients/go/admin/deviceflow/config.go create mode 100644 clients/go/admin/deviceflow/payload.go create mode 100644 clients/go/admin/deviceflow/token_orchestrator.go create mode 100644 clients/go/admin/deviceflow/token_orchestrator_test.go create mode 100644 clients/go/admin/oauth/config.go rename clients/go/admin/{pkce/client_config_test.go => oauth/config_test.go} (86%) delete mode 100644 clients/go/admin/pkce/client_config.go create mode 100644 clients/go/admin/tokenorchestrator/base_token_orchestrator.go create mode 100644 clients/go/admin/tokenorchestrator/base_token_orchestrator_test.go rename clients/go/admin/{pkce => tokenorchestrator}/testdata/token.json (100%) diff --git a/clients/go/admin/authtype_enumer.go b/clients/go/admin/authtype_enumer.go index 41422dee51..d09a85ac6f 100644 --- a/clients/go/admin/authtype_enumer.go +++ b/clients/go/admin/authtype_enumer.go @@ -8,9 +8,9 @@ import ( "fmt" ) -const _AuthTypeName = "ClientSecretPkceExternalCommand" +const _AuthTypeName = "ClientSecretPkceExternalCommandDeviceFlow" -var _AuthTypeIndex = [...]uint8{0, 12, 16, 31} +var _AuthTypeIndex = [...]uint8{0, 12, 16, 31, 41} func (i AuthType) String() string { if i >= AuthType(len(_AuthTypeIndex)-1) { @@ -19,12 +19,13 @@ func (i AuthType) String() string { return _AuthTypeName[_AuthTypeIndex[i]:_AuthTypeIndex[i+1]] } -var _AuthTypeValues = []AuthType{0, 1, 2} +var _AuthTypeValues = []AuthType{0, 1, 2, 3} var _AuthTypeNameToValueMap = map[string]AuthType{ _AuthTypeName[0:12]: 0, _AuthTypeName[12:16]: 1, _AuthTypeName[16:31]: 2, + _AuthTypeName[31:41]: 3, } // AuthTypeString retrieves an enum value from the enum constants string name. diff --git a/clients/go/admin/pkce/mocks/token_cache.go b/clients/go/admin/cache/mocks/token_cache.go similarity index 100% rename from clients/go/admin/pkce/mocks/token_cache.go rename to clients/go/admin/cache/mocks/token_cache.go diff --git a/clients/go/admin/pkce/token_cache.go b/clients/go/admin/cache/token_cache.go similarity index 96% rename from clients/go/admin/pkce/token_cache.go rename to clients/go/admin/cache/token_cache.go index 22b1397f41..e4e2b7e17f 100644 --- a/clients/go/admin/pkce/token_cache.go +++ b/clients/go/admin/cache/token_cache.go @@ -1,4 +1,4 @@ -package pkce +package cache import "golang.org/x/oauth2" diff --git a/clients/go/admin/pkce/token_cache_inmemory.go b/clients/go/admin/cache/token_cache_inmemory.go similarity index 96% rename from clients/go/admin/pkce/token_cache_inmemory.go rename to clients/go/admin/cache/token_cache_inmemory.go index cde76cd423..9c6223fc06 100644 --- a/clients/go/admin/pkce/token_cache_inmemory.go +++ b/clients/go/admin/cache/token_cache_inmemory.go @@ -1,4 +1,4 @@ -package pkce +package cache import ( "fmt" diff --git a/clients/go/admin/client.go b/clients/go/admin/client.go index 95bbfdaf92..383c4c0699 100644 --- a/clients/go/admin/client.go +++ b/clients/go/admin/client.go @@ -7,17 +7,17 @@ import ( "errors" "fmt" - "github.com/flyteorg/flyteidl/clients/go/admin/mocks" - "github.com/flyteorg/flyteidl/clients/go/admin/pkce" - "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" - "github.com/flyteorg/flytestdlib/logger" - grpcMiddleware "github.com/grpc-ecosystem/go-grpc-middleware" grpcRetry "github.com/grpc-ecosystem/go-grpc-middleware/retry" grpcPrometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/health/grpc_health_v1" + + "github.com/flyteorg/flyteidl/clients/go/admin/cache" + "github.com/flyteorg/flyteidl/clients/go/admin/mocks" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" + "github.com/flyteorg/flytestdlib/logger" ) // IDE "Go Generate File". This will create a mocks/AdminServiceClient.go file @@ -172,7 +172,7 @@ func InitializeAdminClient(ctx context.Context, cfg *Config, opts ...grpc.DialOp // initializeClients creates an AdminClient, AuthServiceClient and IdentityServiceClient with a shared Admin connection // for the process. Note that if called with different cfg/dialoptions, it will not refresh the connection. -func initializeClients(ctx context.Context, cfg *Config, tokenCache pkce.TokenCache, opts ...grpc.DialOption) (*Clientset, error) { +func initializeClients(ctx context.Context, cfg *Config, tokenCache cache.TokenCache, opts ...grpc.DialOption) (*Clientset, error) { authMetadataClient, err := InitializeAuthMetadataClient(ctx, cfg) if err != nil { logger.Panicf(ctx, "failed to initialize Auth Metadata Client. Error: %v", err) @@ -215,7 +215,7 @@ func initializeClients(ctx context.Context, cfg *Config, tokenCache pkce.TokenCa } // Deprecated: Please use NewClientsetBuilder() instead. -func InitializeAdminClientFromConfig(ctx context.Context, tokenCache pkce.TokenCache, opts ...grpc.DialOption) (service.AdminServiceClient, error) { +func InitializeAdminClientFromConfig(ctx context.Context, tokenCache cache.TokenCache, opts ...grpc.DialOption) (service.AdminServiceClient, error) { clientSet, err := initializeClients(ctx, GetConfig(ctx), tokenCache, opts...) if err != nil { return nil, err diff --git a/clients/go/admin/client_builder.go b/clients/go/admin/client_builder.go index 11a5098c7f..a164807399 100644 --- a/clients/go/admin/client_builder.go +++ b/clients/go/admin/client_builder.go @@ -5,13 +5,13 @@ import ( "google.golang.org/grpc" - "github.com/flyteorg/flyteidl/clients/go/admin/pkce" + "github.com/flyteorg/flyteidl/clients/go/admin/cache" ) // ClientsetBuilder is used to build the clientset. This allows custom token cache implementations to be plugged in. type ClientsetBuilder struct { config *Config - tokenCache pkce.TokenCache + tokenCache cache.TokenCache opts []grpc.DialOption } @@ -27,7 +27,7 @@ func (cb *ClientsetBuilder) WithConfig(config *Config) *ClientsetBuilder { } // WithTokenCache allows pluggable token cache implemetations. eg; flytectl uses keyring as tokenCache -func (cb *ClientsetBuilder) WithTokenCache(tokenCache pkce.TokenCache) *ClientsetBuilder { +func (cb *ClientsetBuilder) WithTokenCache(tokenCache cache.TokenCache) *ClientsetBuilder { cb.tokenCache = tokenCache return cb } @@ -40,7 +40,7 @@ func (cb *ClientsetBuilder) WithDialOptions(opts ...grpc.DialOption) *ClientsetB // Build the clientset using the current state of the ClientsetBuilder func (cb *ClientsetBuilder) Build(ctx context.Context) (*Clientset, error) { if cb.tokenCache == nil { - cb.tokenCache = &pkce.TokenCacheInMemoryProvider{} + cb.tokenCache = &cache.TokenCacheInMemoryProvider{} } if cb.config == nil { diff --git a/clients/go/admin/client_builder_test.go b/clients/go/admin/client_builder_test.go index 8cca17639b..1067579b45 100644 --- a/clients/go/admin/client_builder_test.go +++ b/clients/go/admin/client_builder_test.go @@ -5,15 +5,16 @@ import ( "reflect" "testing" - "github.com/flyteorg/flyteidl/clients/go/admin/pkce" "github.com/stretchr/testify/assert" + + "github.com/flyteorg/flyteidl/clients/go/admin/cache" ) func TestClientsetBuilder_Build(t *testing.T) { cb := NewClientsetBuilder().WithConfig(&Config{ UseInsecureConnection: true, - }).WithTokenCache(&pkce.TokenCacheInMemoryProvider{}) + }).WithTokenCache(&cache.TokenCacheInMemoryProvider{}) _, err := cb.Build(context.Background()) assert.NoError(t, err) - assert.True(t, reflect.TypeOf(cb.tokenCache) == reflect.TypeOf(&pkce.TokenCacheInMemoryProvider{})) + assert.True(t, reflect.TypeOf(cb.tokenCache) == reflect.TypeOf(&cache.TokenCacheInMemoryProvider{})) } diff --git a/clients/go/admin/client_test.go b/clients/go/admin/client_test.go index 7e66079acd..8f41fc248f 100644 --- a/clients/go/admin/client_test.go +++ b/clients/go/admin/client_test.go @@ -7,20 +7,24 @@ import ( "io/ioutil" "net/http" "net/url" + "os" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "golang.org/x/oauth2" + _ "google.golang.org/grpc/balancer/roundrobin" //nolint + + "github.com/flyteorg/flyteidl/clients/go/admin/cache" + cachemocks "github.com/flyteorg/flyteidl/clients/go/admin/cache/mocks" "github.com/flyteorg/flyteidl/clients/go/admin/mocks" + "github.com/flyteorg/flyteidl/clients/go/admin/oauth" "github.com/flyteorg/flyteidl/clients/go/admin/pkce" - pkcemocks "github.com/flyteorg/flyteidl/clients/go/admin/pkce/mocks" + "github.com/flyteorg/flyteidl/clients/go/admin/tokenorchestrator" "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" "github.com/flyteorg/flytestdlib/config" "github.com/flyteorg/flytestdlib/logger" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "golang.org/x/oauth2" - _ "google.golang.org/grpc/balancer/roundrobin" //nolint ) func TestInitializeAndGetAdminClient(t *testing.T) { @@ -193,13 +197,13 @@ func TestGetAuthenticationDialOptionPkce(t *testing.T) { RedirectUri: "http://localhost:54545/callback", } http.DefaultServeMux = http.NewServeMux() - plan, _ := ioutil.ReadFile("pkce/testdata/token.json") + plan, _ := os.ReadFile("tokenorchestrator/testdata/token.json") var tokenData oauth2.Token err := json.Unmarshal(plan, &tokenData) assert.NoError(t, err) tokenData.Expiry = time.Now().Add(time.Minute) t.Run("cache hit", func(t *testing.T) { - mockTokenCache := new(pkcemocks.TokenCache) + mockTokenCache := new(cachemocks.TokenCache) mockAuthClient := new(mocks.AuthMetadataServiceClient) mockTokenCache.OnGetTokenMatch().Return(&tokenData, nil) mockTokenCache.OnSaveTokenMatch(mock.Anything).Return(nil) @@ -213,7 +217,7 @@ func TestGetAuthenticationDialOptionPkce(t *testing.T) { }) tokenData.Expiry = time.Now().Add(-time.Minute) t.Run("cache miss auth failure", func(t *testing.T) { - mockTokenCache := new(pkcemocks.TokenCache) + mockTokenCache := new(cachemocks.TokenCache) mockAuthClient := new(mocks.AuthMetadataServiceClient) mockTokenCache.OnGetTokenMatch().Return(&tokenData, nil) mockTokenCache.OnSaveTokenMatch(mock.Anything).Return(nil) @@ -244,16 +248,26 @@ func Test_getPkceAuthTokenSource(t *testing.T) { mockAuthClient.OnGetPublicClientConfigMatch(mock.Anything, mock.Anything).Return(clientMetatadata, nil) t.Run("cached token expired", func(t *testing.T) { - plan, _ := ioutil.ReadFile("pkce/testdata/token.json") + plan, _ := ioutil.ReadFile("tokenorchestrator/testdata/token.json") var tokenData oauth2.Token err := json.Unmarshal(plan, &tokenData) assert.NoError(t, err) // populate the cache - tokenCache := &pkce.TokenCacheInMemoryProvider{} + tokenCache := &cache.TokenCacheInMemoryProvider{} assert.NoError(t, tokenCache.SaveToken(&tokenData)) - orchestrator, err := pkce.NewTokenOrchestrator(ctx, pkce.Config{}, tokenCache, mockAuthClient) + baseOrchestrator := tokenorchestrator.BaseTokenOrchestrator{ + ClientConfig: &oauth.Config{ + Config: &oauth2.Config{ + RedirectURL: "http://localhost:8089/redirect", + Scopes: []string{"code", "all"}, + }, + }, + TokenCache: tokenCache, + } + + orchestrator, err := pkce.NewTokenOrchestrator(baseOrchestrator, pkce.Config{}) assert.NoError(t, err) http.DefaultServeMux = http.NewServeMux() diff --git a/clients/go/admin/config.go b/clients/go/admin/config.go index 7da739a870..d7d0abcf71 100644 --- a/clients/go/admin/config.go +++ b/clients/go/admin/config.go @@ -8,8 +8,8 @@ import ( "path/filepath" "time" + "github.com/flyteorg/flyteidl/clients/go/admin/deviceflow" "github.com/flyteorg/flyteidl/clients/go/admin/pkce" - "github.com/flyteorg/flytestdlib/config" "github.com/flyteorg/flytestdlib/logger" ) @@ -27,12 +27,14 @@ var DefaultClientSecretLocation = filepath.Join(string(filepath.Separator), "etc type AuthType uint8 const ( - // Chooses Client Secret OAuth2 protocol (ref: https://tools.ietf.org/html/rfc6749#section-4.4) + // AuthTypeClientSecret Chooses Client Secret OAuth2 protocol (ref: https://tools.ietf.org/html/rfc6749#section-4.4) AuthTypeClientSecret AuthType = iota - // Chooses Proof Key Code Exchange OAuth2 extension protocol (ref: https://tools.ietf.org/html/rfc7636) + // AuthTypePkce Chooses Proof Key Code Exchange OAuth2 extension protocol (ref: https://tools.ietf.org/html/rfc7636) AuthTypePkce - // Chooses an external authentication process + // AuthTypeExternalCommand Chooses an external authentication process AuthTypeExternalCommand + // AuthTypeDeviceFlow Uses device flow to authenticate in a constrained environment with no access to browser + AuthTypeDeviceFlow ) type Config struct { @@ -67,6 +69,8 @@ type Config struct { PkceConfig pkce.Config `json:"pkceConfig" pflag:",Config for Pkce authentication flow."` + DeviceFlowConfig deviceflow.Config `json:"deviceFlowConfig" pflag:",Config for Device authentication flow."` + Command []string `json:"command" pflag:",Command for external authentication token generation"` // Set the gRPC service config formatted as a json string https://github.com/grpc/grpc/blob/master/doc/service_config.md @@ -86,7 +90,12 @@ var ( ClientSecretLocation: DefaultClientSecretLocation, PkceConfig: pkce.Config{ TokenRefreshGracePeriod: config.Duration{Duration: 5 * time.Minute}, - BrowserSessionTimeout: config.Duration{Duration: 15 * time.Second}, + BrowserSessionTimeout: config.Duration{Duration: 2 * time.Minute}, + }, + DeviceFlowConfig: deviceflow.Config{ + TokenRefreshGracePeriod: config.Duration{Duration: 5 * time.Minute}, + Timeout: config.Duration{Duration: 10 * time.Minute}, + PollInterval: config.Duration{Duration: 5 * time.Second}, }, TokenRefreshWindow: config.Duration{Duration: 0}, } diff --git a/clients/go/admin/config_flags.go b/clients/go/admin/config_flags.go index 173105e99e..6d4ffd8091 100755 --- a/clients/go/admin/config_flags.go +++ b/clients/go/admin/config_flags.go @@ -67,8 +67,11 @@ func (cfg Config) GetPFlagSet(prefix string) *pflag.FlagSet { cmdFlags.String(fmt.Sprintf("%v%v", prefix, "authorizationServerUrl"), defaultConfig.DeprecatedAuthorizationServerURL, "This is the URL to your IdP's authorization server. It'll default to Endpoint") cmdFlags.String(fmt.Sprintf("%v%v", prefix, "tokenUrl"), defaultConfig.TokenURL, "OPTIONAL: Your IdP's token endpoint. It'll be discovered from flyte admin's OAuth Metadata endpoint if not provided.") cmdFlags.String(fmt.Sprintf("%v%v", prefix, "authorizationHeader"), defaultConfig.DeprecatedAuthorizationHeader, "Custom metadata header to pass JWT") - cmdFlags.String(fmt.Sprintf("%v%v", prefix, "pkceConfig.timeout"), defaultConfig.PkceConfig.BrowserSessionTimeout.String(), "") - cmdFlags.String(fmt.Sprintf("%v%v", prefix, "pkceConfig.refreshTime"), defaultConfig.PkceConfig.TokenRefreshGracePeriod.String(), "") + cmdFlags.String(fmt.Sprintf("%v%v", prefix, "pkceConfig.timeout"), defaultConfig.PkceConfig.BrowserSessionTimeout.String(), "Amount of time the browser session would be active for authentication from client app.") + cmdFlags.String(fmt.Sprintf("%v%v", prefix, "pkceConfig.refreshTime"), defaultConfig.PkceConfig.TokenRefreshGracePeriod.String(), "grace period from the token expiry after which it would refresh the token.") + cmdFlags.String(fmt.Sprintf("%v%v", prefix, "deviceFlowConfig.refreshTime"), defaultConfig.DeviceFlowConfig.TokenRefreshGracePeriod.String(), "grace period from the token expiry after which it would refresh the token.") + cmdFlags.String(fmt.Sprintf("%v%v", prefix, "deviceFlowConfig.timeout"), defaultConfig.DeviceFlowConfig.Timeout.String(), "amount of time the device flow should complete or else it will be cancelled.") + cmdFlags.String(fmt.Sprintf("%v%v", prefix, "deviceFlowConfig.pollInterval"), defaultConfig.DeviceFlowConfig.PollInterval.String(), "amount of time the device flow would poll the token endpoint if auth server doesn't return a polling interval. Okta and google IDP do return an interval'") cmdFlags.StringSlice(fmt.Sprintf("%v%v", prefix, "command"), defaultConfig.Command, "Command for external authentication token generation") cmdFlags.String(fmt.Sprintf("%v%v", prefix, "defaultServiceConfig"), defaultConfig.DefaultServiceConfig, "") return cmdFlags diff --git a/clients/go/admin/config_flags_test.go b/clients/go/admin/config_flags_test.go index 682886986a..cf97cdd92c 100755 --- a/clients/go/admin/config_flags_test.go +++ b/clients/go/admin/config_flags_test.go @@ -365,6 +365,48 @@ func TestConfig_SetFlags(t *testing.T) { } }) }) + t.Run("Test_deviceFlowConfig.refreshTime", func(t *testing.T) { + + t.Run("Override", func(t *testing.T) { + testValue := defaultConfig.DeviceFlowConfig.TokenRefreshGracePeriod.String() + + cmdFlags.Set("deviceFlowConfig.refreshTime", testValue) + if vString, err := cmdFlags.GetString("deviceFlowConfig.refreshTime"); err == nil { + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.DeviceFlowConfig.TokenRefreshGracePeriod) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) + t.Run("Test_deviceFlowConfig.timeout", func(t *testing.T) { + + t.Run("Override", func(t *testing.T) { + testValue := defaultConfig.DeviceFlowConfig.Timeout.String() + + cmdFlags.Set("deviceFlowConfig.timeout", testValue) + if vString, err := cmdFlags.GetString("deviceFlowConfig.timeout"); err == nil { + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.DeviceFlowConfig.Timeout) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) + t.Run("Test_deviceFlowConfig.pollInterval", func(t *testing.T) { + + t.Run("Override", func(t *testing.T) { + testValue := defaultConfig.DeviceFlowConfig.PollInterval.String() + + cmdFlags.Set("deviceFlowConfig.pollInterval", testValue) + if vString, err := cmdFlags.GetString("deviceFlowConfig.pollInterval"); err == nil { + testDecodeJson_Config(t, fmt.Sprintf("%v", vString), &actual.DeviceFlowConfig.PollInterval) + + } else { + assert.FailNow(t, err.Error()) + } + }) + }) t.Run("Test_command", func(t *testing.T) { t.Run("Override", func(t *testing.T) { diff --git a/clients/go/admin/deviceflow/config.go b/clients/go/admin/deviceflow/config.go new file mode 100644 index 0000000000..4699cd6996 --- /dev/null +++ b/clients/go/admin/deviceflow/config.go @@ -0,0 +1,10 @@ +package deviceflow + +import "github.com/flyteorg/flytestdlib/config" + +// Config defines settings used for Device orchestration flow. +type Config struct { + TokenRefreshGracePeriod config.Duration `json:"refreshTime" pflag:",grace period from the token expiry after which it would refresh the token."` + Timeout config.Duration `json:"timeout" pflag:",amount of time the device flow should complete or else it will be cancelled."` + PollInterval config.Duration `json:"pollInterval" pflag:",amount of time the device flow would poll the token endpoint if auth server doesn't return a polling interval. Okta and google IDP do return an interval'"` +} diff --git a/clients/go/admin/deviceflow/payload.go b/clients/go/admin/deviceflow/payload.go new file mode 100644 index 0000000000..fc8c58ab49 --- /dev/null +++ b/clients/go/admin/deviceflow/payload.go @@ -0,0 +1,42 @@ +package deviceflow + +import "golang.org/x/oauth2" + +// DeviceAuthorizationRequest sent to authorization server directly from the client app +type DeviceAuthorizationRequest struct { + // ClientID is the client identifier issued to the client during the registration process of OAuth app with the authorization server + ClientID string `json:"client_id"` + // Scope is the scope parameter of the access request + Scope string `json:"scope"` +} + +// DeviceAuthorizationResponse contains the information that the end user would use to authorize the app requesting the +// resource access. +type DeviceAuthorizationResponse struct { + // DeviceCode unique device code generated by the authorization server. + DeviceCode string `json:"device_code"` + // UserCode unique code generated for the user to enter on another device + UserCode string `json:"user_code"` + // VerificationURI url endpoint of the authorization server which host the device and app verification + VerificationURI string `json:"verification_uri"` + // VerificationURIComplete url endpoint of the authorization server which host the device and app verification along with user code + VerificationURIComplete string `json:"verification_uri_complete"` + // ExpiresIn lifetime in seconds of the "device_code" and "user_code" + ExpiresIn int64 `json:"expires_in"` + // Interval minimum amount of time in secs the client app should wait between polling requests to the token endpoint. + Interval int64 `json:"interval"` +} + +type DeviceAccessTokenRequest struct { + // ClientID is the client identifier issued to the client during the registration process of OAuth app with the authorization server + ClientID string `json:"client_id"` + // DeviceCode unique device code generated by the authorization server. + DeviceCode string `json:"device_code"` + // Value MUST be set to "urn:ietf:params:oauth:grant-type:device_code" + GrantType string `json:"grant_type"` +} + +type DeviceAccessTokenResponse struct { + oauth2.Token + Error string `json:"error"` +} diff --git a/clients/go/admin/deviceflow/token_orchestrator.go b/clients/go/admin/deviceflow/token_orchestrator.go new file mode 100644 index 0000000000..02f836ed86 --- /dev/null +++ b/clients/go/admin/deviceflow/token_orchestrator.go @@ -0,0 +1,174 @@ +package deviceflow + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "golang.org/x/net/context/ctxhttp" + "golang.org/x/oauth2" + + "github.com/flyteorg/flyteidl/clients/go/admin/tokenorchestrator" + "github.com/flyteorg/flytestdlib/logger" +) + +const ( + cliendID = "client_id" + deviceCode = "device_code" + grantType = "grant_type" + scope = "scope" + grantTypeValue = "urn:ietf:params:oauth:grant-type:device_code" +) + +const ( + errSlowDown = "slow_down" + errAuthPending = "authorization_pending" +) + +// OAuthTokenOrError containing the token +type OAuthTokenOrError struct { + *oauth2.Token + Error string `json:"error,omitempty"` +} + +// TokenOrchestrator implements the main logic to initiate device authorization flow +type TokenOrchestrator struct { + Config Config + tokenorchestrator.BaseTokenOrchestrator +} + +// StartDeviceAuthorization will initiate the OAuth2 device authorization flow. +func (t TokenOrchestrator) StartDeviceAuthorization(ctx context.Context, dareq DeviceAuthorizationRequest) (*DeviceAuthorizationResponse, error) { + v := url.Values{cliendID: {dareq.ClientID}, scope: {dareq.Scope}} + httpReq, err := http.NewRequest("POST", t.ClientConfig.DeviceEndpoint, strings.NewReader(v.Encode())) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + httpResp, err := ctxhttp.Do(ctx, nil, httpReq) + if err != nil { + return nil, err + } + + body, err := io.ReadAll(io.LimitReader(httpResp.Body, 1<<20)) + httpResp.Body.Close() + if err != nil { + return nil, fmt.Errorf("device authorization request failed due to %v", err) + } + + if httpResp.StatusCode != http.StatusOK { + return nil, &oauth2.RetrieveError{ + Response: httpResp, + Body: body, + } + } + + daresp := &DeviceAuthorizationResponse{} + err = json.Unmarshal(body, &daresp) + if err != nil { + return nil, err + } + + if len(daresp.VerificationURIComplete) > 0 { + fmt.Printf("Please open the browser at the url %v containing verification code\n", daresp.VerificationURIComplete) + } else { + fmt.Printf("Please open the browser at the url %v and enter following verification code %v\n", daresp.VerificationURI, daresp.UserCode) + } + return daresp, nil +} + +// PollTokenEndpoint polls the token endpoint until the user authorizes/ denies the app or an error occurs other than slow_down or authorization_pending +func (t TokenOrchestrator) PollTokenEndpoint(ctx context.Context, tokReq DeviceAccessTokenRequest, pollInterval time.Duration) (*oauth2.Token, error) { + v := url.Values{ + cliendID: {tokReq.ClientID}, + grantType: {grantTypeValue}, + deviceCode: {tokReq.DeviceCode}, + } + + for { + + httpReq, err := http.NewRequest("POST", t.ClientConfig.Endpoint.TokenURL, strings.NewReader(v.Encode())) + if err != nil { + return nil, err + } + httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + httpResp, err := ctxhttp.Do(ctx, nil, httpReq) + if err != nil { + return nil, err + } + + body, err := io.ReadAll(io.LimitReader(httpResp.Body, 1<<20)) + httpResp.Body.Close() + if err != nil { + return nil, err + } + + // We cannot check the status code since 400 is returned in case of errAuthPending and errSlowDown in which + // case the polling has to still continue + var tokResp DeviceAccessTokenResponse + err = json.Unmarshal(body, &tokResp) + if err != nil { + return nil, err + } + + // Unmarshalled response if it contains an error then check if we need to increase the polling interval + if len(tokResp.Error) > 0 { + if tokResp.Error == errSlowDown || tokResp.Error == errAuthPending { + pollInterval = pollInterval * 2 + + } else { + return nil, fmt.Errorf("oauth error : %v", tokResp.Error) + } + } else { + // Got the auth token in the response and save it in the cache + err = t.TokenCache.SaveToken(&tokResp.Token) + // Saving into the cache is only considered to be a warning in this case. + if err != nil { + logger.Warnf(ctx, "failed to save token in the token cache. Error: %w", err) + } + return &tokResp.Token, nil + } + fmt.Printf("Waiting for %v secs\n", pollInterval.Seconds()) + time.Sleep(pollInterval) + } +} + +// FetchTokenFromAuthFlow starts a webserver to listen to redirect callback from the authorization server at the end +// of the flow. It then launches the browser to authenticate the user. +func (t TokenOrchestrator) FetchTokenFromAuthFlow(ctx context.Context) (*oauth2.Token, error) { + ctx, cancelNow := context.WithTimeout(ctx, t.Config.Timeout.Duration) + defer cancelNow() + + var scopes string + if len(t.ClientConfig.Scopes) > 0 { + scopes = strings.Join(t.ClientConfig.Scopes, " ") + } + daReq := DeviceAuthorizationRequest{ClientID: t.ClientConfig.ClientID, Scope: scopes} + daResp, err := t.StartDeviceAuthorization(ctx, daReq) + if err != nil { + return nil, err + } + + pollInterval := t.Config.PollInterval.Duration // default value of 5 sec poll interval if the authorization response doesn't have interval set + if daResp.Interval > 0 { + pollInterval = time.Duration(daResp.Interval) * time.Second + } + + tokReq := DeviceAccessTokenRequest{ClientID: t.ClientConfig.ClientID, DeviceCode: daResp.DeviceCode, GrantType: grantType} + return t.PollTokenEndpoint(ctx, tokReq, pollInterval) +} + +// NewDeviceFlowTokenOrchestrator creates a new TokenOrchestrator that implements the main logic to start device authorization flow and fetch device code and then poll on the token endpoint until the device authorization is approved/denied by the user +func NewDeviceFlowTokenOrchestrator(baseOrchestrator tokenorchestrator.BaseTokenOrchestrator, cfg Config) (TokenOrchestrator, error) { + return TokenOrchestrator{ + BaseTokenOrchestrator: baseOrchestrator, + Config: cfg, + }, nil +} diff --git a/clients/go/admin/deviceflow/token_orchestrator_test.go b/clients/go/admin/deviceflow/token_orchestrator_test.go new file mode 100644 index 0000000000..a4be35d1da --- /dev/null +++ b/clients/go/admin/deviceflow/token_orchestrator_test.go @@ -0,0 +1,103 @@ +package deviceflow + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "k8s.io/apimachinery/pkg/util/json" + + "github.com/flyteorg/flytestdlib/config" + + "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" + + "github.com/flyteorg/flyteidl/clients/go/admin/cache" + "github.com/flyteorg/flyteidl/clients/go/admin/oauth" + "github.com/flyteorg/flyteidl/clients/go/admin/tokenorchestrator" +) + +func TestFetchFromAuthFlow(t *testing.T) { + ctx := context.Background() + t.Run("fetch from auth flow", func(t *testing.T) { + tokenCache := &cache.TokenCacheInMemoryProvider{} + orchestrator, err := NewDeviceFlowTokenOrchestrator(tokenorchestrator.BaseTokenOrchestrator{ + ClientConfig: &oauth.Config{ + Config: &oauth2.Config{ + RedirectURL: "http://localhost:8089/redirect", + Scopes: []string{"code", "all"}, + }, + DeviceEndpoint: "http://dummyDeviceEndpoint", + }, + TokenCache: tokenCache, + }, Config{}) + assert.NoError(t, err) + refreshedToken, err := orchestrator.FetchTokenFromAuthFlow(ctx) + assert.Nil(t, refreshedToken) + assert.NotNil(t, err) + }) + + t.Run("fetch from auth flow", func(t *testing.T) { + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + assert.Nil(t, err) + isDeviceReq := strings.Contains(string(body), scope) + isTokReq := strings.Contains(string(body), deviceCode) && strings.Contains(string(body), grantType) && strings.Contains(string(body), cliendID) + + if isDeviceReq { + dar := DeviceAuthorizationResponse{ + DeviceCode: "e1db31fe-3b23-4fce-b759-82bf8ea323d6", + UserCode: "RPBQZNRX", + VerificationURI: "https://oauth-server/activate", + VerificationURIComplete: "https://oauth-server/activate?user_code=RPBQZNRX", + Interval: 5, + } + darBytes, err := json.Marshal(dar) + assert.Nil(t, err) + _, err = w.Write(darBytes) + assert.Nil(t, err) + return + } else if isTokReq { + dar := DeviceAccessTokenResponse{ + Token: oauth2.Token{ + AccessToken: "access_token", + }, + } + darBytes, err := json.Marshal(dar) + assert.Nil(t, err) + _, err = w.Write(darBytes) + assert.Nil(t, err) + return + } + t.Fatal("unknown request") + })) + defer fakeServer.Close() + + tokenCache := &cache.TokenCacheInMemoryProvider{} + orchestrator, err := NewDeviceFlowTokenOrchestrator(tokenorchestrator.BaseTokenOrchestrator{ + ClientConfig: &oauth.Config{ + Config: &oauth2.Config{ + ClientID: cliendID, + RedirectURL: "http://localhost:8089/redirect", + Scopes: []string{"code", "all"}, + Endpoint: oauth2.Endpoint{ + TokenURL: fakeServer.URL, + }, + }, + DeviceEndpoint: fakeServer.URL, + }, + TokenCache: tokenCache, + }, Config{ + Timeout: config.Duration{Duration: 1 * time.Minute}, + }) + assert.NoError(t, err) + authToken, err := orchestrator.FetchTokenFromAuthFlow(ctx) + assert.Nil(t, err) + assert.NotNil(t, authToken) + assert.Equal(t, "access_token", authToken.AccessToken) + }) +} diff --git a/clients/go/admin/oauth/config.go b/clients/go/admin/oauth/config.go new file mode 100644 index 0000000000..1e836d61be --- /dev/null +++ b/clients/go/admin/oauth/config.go @@ -0,0 +1,43 @@ +package oauth + +import ( + "context" + + "golang.org/x/oauth2" + + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" +) + +// Config oauth2.Config overridden with device endpoint for supporting Device Authorization Grant flow [RFC8268] +type Config struct { + *oauth2.Config + DeviceEndpoint string +} + +// BuildConfigFromMetadataService builds OAuth2 config from information retrieved through the anonymous auth metadata service. +func BuildConfigFromMetadataService(ctx context.Context, authMetadataClient service.AuthMetadataServiceClient) (clientConf *Config, err error) { + var clientResp *service.PublicClientAuthConfigResponse + if clientResp, err = authMetadataClient.GetPublicClientConfig(ctx, &service.PublicClientAuthConfigRequest{}); err != nil { + return nil, err + } + + var oauthMetaResp *service.OAuth2MetadataResponse + if oauthMetaResp, err = authMetadataClient.GetOAuth2Metadata(ctx, &service.OAuth2MetadataRequest{}); err != nil { + return nil, err + } + + clientConf = &Config{ + Config: &oauth2.Config{ + ClientID: clientResp.ClientId, + RedirectURL: clientResp.RedirectUri, + Scopes: clientResp.Scopes, + Endpoint: oauth2.Endpoint{ + TokenURL: oauthMetaResp.TokenEndpoint, + AuthURL: oauthMetaResp.AuthorizationEndpoint, + }, + }, + DeviceEndpoint: oauthMetaResp.DeviceAuthorizationEndpoint, + } + + return clientConf, nil +} diff --git a/clients/go/admin/pkce/client_config_test.go b/clients/go/admin/oauth/config_test.go similarity index 86% rename from clients/go/admin/pkce/client_config_test.go rename to clients/go/admin/oauth/config_test.go index 500233b2bc..67e4c46bfe 100644 --- a/clients/go/admin/pkce/client_config_test.go +++ b/clients/go/admin/oauth/config_test.go @@ -1,14 +1,14 @@ -package pkce +package oauth import ( "context" "testing" - "github.com/flyteorg/flyteidl/clients/go/admin/mocks" - "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + + "github.com/flyteorg/flyteidl/clients/go/admin/mocks" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" ) func TestGenerateClientConfig(t *testing.T) { @@ -24,14 +24,16 @@ func TestGenerateClientConfig(t *testing.T) { AuthorizationEndpoint: "dummyAuthEndPoint", TokenEndpoint: "dummyTokenEndpoint", CodeChallengeMethodsSupported: []string{"dummyCodeChallenege"}, + DeviceAuthorizationEndpoint: "dummyDeviceEndpoint", } mockAuthClient.OnGetPublicClientConfigMatch(ctx, mock.Anything).Return(flyteClientResp, nil) mockAuthClient.OnGetOAuth2MetadataMatch(ctx, mock.Anything).Return(oauthMetaDataResp, nil) - oauthConfig, err := BuildClientConfig(ctx, mockAuthClient) + oauthConfig, err := BuildConfigFromMetadataService(ctx, mockAuthClient) assert.Nil(t, err) assert.NotNil(t, oauthConfig) assert.Equal(t, "dummyClient", oauthConfig.ClientID) assert.Equal(t, "dummyRedirectUri", oauthConfig.RedirectURL) assert.Equal(t, "dummyTokenEndpoint", oauthConfig.Endpoint.TokenURL) assert.Equal(t, "dummyAuthEndPoint", oauthConfig.Endpoint.AuthURL) + assert.Equal(t, "dummyDeviceEndpoint", oauthConfig.DeviceEndpoint) } diff --git a/clients/go/admin/pkce/auth_flow_orchestrator.go b/clients/go/admin/pkce/auth_flow_orchestrator.go index a915cdc709..e2eee411da 100644 --- a/clients/go/admin/pkce/auth_flow_orchestrator.go +++ b/clients/go/admin/pkce/auth_flow_orchestrator.go @@ -5,80 +5,27 @@ import ( "fmt" "net/http" "net/url" - "time" - - "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" - "github.com/flyteorg/flytestdlib/logger" "github.com/pkg/browser" "golang.org/x/oauth2" + + "github.com/flyteorg/flyteidl/clients/go/admin/tokenorchestrator" + "github.com/flyteorg/flytestdlib/logger" +) + +const ( + stateKey = "state" + nonceKey = "nonce" + codeChallengeKey = "code_challenge" + codeChallengeMethodKey = "code_challenge_method" + codeChallengeMethodVal = "S256" ) // TokenOrchestrator implements the main logic to initiate Pkce flow to issue access token and refresh token as well as // refreshing the access token if a refresh token is present. type TokenOrchestrator struct { - cfg Config - clientConfig *oauth2.Config - tokenCache TokenCache -} - -// RefreshToken attempts to refresh the access token if a refresh token is provided. -func (f TokenOrchestrator) RefreshToken(ctx context.Context, token *oauth2.Token) (*oauth2.Token, error) { - ts := f.clientConfig.TokenSource(ctx, token) - var refreshedToken *oauth2.Token - var err error - refreshedToken, err = ts.Token() - if err != nil { - logger.Warnf(ctx, "failed to refresh the token due to %v and will be doing re-auth", err) - return nil, err - } - - if refreshedToken != nil { - logger.Debugf(ctx, "got a response from the refresh grant for old expiry %v with new expiry %v", - token.Expiry, refreshedToken.Expiry) - if refreshedToken.AccessToken != token.AccessToken { - if err = f.tokenCache.SaveToken(refreshedToken); err != nil { - logger.Errorf(ctx, "unable to save the new token due to %v", err) - return nil, err - } - } - } - - return refreshedToken, nil -} - -// FetchTokenFromCacheOrRefreshIt fetches the token from cache and refreshes it if it'll expire within the -// Config.TokenRefreshGracePeriod period. -func (f TokenOrchestrator) FetchTokenFromCacheOrRefreshIt(ctx context.Context) (token *oauth2.Token, err error) { - token, err = f.tokenCache.GetToken() - if err != nil { - return nil, err - } - - if !token.Valid() { - return nil, fmt.Errorf("token from cache is invalid") - } - - // If token doesn't need to be refreshed, return it. - if token.Expiry.Add(f.cfg.TokenRefreshGracePeriod.Duration).Before(time.Now()) { - return token, nil - } - - token, err = f.RefreshToken(ctx, token) - if err != nil { - return nil, fmt.Errorf("failed to refresh token using cached token. Error: %w", err) - } - - if !token.Valid() { - return nil, fmt.Errorf("refreshed token is invalid") - } - - err = f.tokenCache.SaveToken(token) - if err != nil { - return nil, fmt.Errorf("failed to save token in the token cache. Error: %w", err) - } - - return token, nil + tokenorchestrator.BaseTokenOrchestrator + Config Config } // FetchTokenFromAuthFlow starts a webserver to listen to redirect callback from the authorization server at the end @@ -86,7 +33,7 @@ func (f TokenOrchestrator) FetchTokenFromCacheOrRefreshIt(ctx context.Context) ( func (f TokenOrchestrator) FetchTokenFromAuthFlow(ctx context.Context) (*oauth2.Token, error) { var err error var redirectURL *url.URL - if redirectURL, err = url.Parse(f.clientConfig.RedirectURL); err != nil { + if redirectURL, err = url.Parse(f.ClientConfig.RedirectURL); err != nil { return nil, err } @@ -106,15 +53,17 @@ func (f TokenOrchestrator) FetchTokenFromAuthFlow(ctx context.Context) (*oauth2. tokenChannel := make(chan *oauth2.Token, 1) errorChannel := make(chan error, 1) - // Replace S256 with one from cient config and provide a support to generate code challenge using the passed - // in method. - urlToOpen := f.clientConfig.AuthCodeURL(stateString) + "&nonce=" + nonces + "&code_challenge=" + - pkceCodeChallenge + "&code_challenge_method=S256" + values := url.Values{} + values.Add(stateKey, stateString) + values.Add(nonceKey, nonces) + values.Add(codeChallengeKey, pkceCodeChallenge) + values.Add(codeChallengeMethodKey, codeChallengeMethodVal) + urlToOpen := fmt.Sprintf("%s&%s", f.ClientConfig.AuthCodeURL(""), values.Encode()) serveMux := http.NewServeMux() server := &http.Server{Addr: redirectURL.Host, Handler: serveMux, ReadHeaderTimeout: 0} // Register the call back handler - serveMux.HandleFunc(redirectURL.Path, getAuthServerCallbackHandler(f.clientConfig, pkceCodeVerifier, + serveMux.HandleFunc(redirectURL.Path, getAuthServerCallbackHandler(f.ClientConfig, pkceCodeVerifier, tokenChannel, errorChannel, stateString)) // the oauth2 callback endpoint defer server.Close() @@ -125,12 +74,12 @@ func (f TokenOrchestrator) FetchTokenFromAuthFlow(ctx context.Context) (*oauth2. } }() - logger.Infof(ctx, "Opening the browser at "+urlToOpen) + logger.Infof(ctx, "Opening the browser at %s", urlToOpen) if err = browser.OpenURL(urlToOpen); err != nil { return nil, err } - ctx, cancelNow := context.WithTimeout(ctx, f.cfg.BrowserSessionTimeout.Duration) + ctx, cancelNow := context.WithTimeout(ctx, f.Config.BrowserSessionTimeout.Duration) defer cancelNow() var token *oauth2.Token @@ -140,7 +89,7 @@ func (f TokenOrchestrator) FetchTokenFromAuthFlow(ctx context.Context) (*oauth2. case <-ctx.Done(): return nil, fmt.Errorf("context was canceled during auth flow") case token = <-tokenChannel: - if err = f.tokenCache.SaveToken(token); err != nil { + if err = f.TokenCache.SaveToken(token); err != nil { logger.Errorf(ctx, "unable to save the new token due to. Will ignore the error and use the issued token. Error: %v", err) } @@ -150,15 +99,9 @@ func (f TokenOrchestrator) FetchTokenFromAuthFlow(ctx context.Context) (*oauth2. // NewTokenOrchestrator creates a new TokenOrchestrator that implements the main logic to initiate Pkce flow to issue // access token and refresh token as well as refreshing the access token if a refresh token is present. -func NewTokenOrchestrator(ctx context.Context, cfg Config, tokenCache TokenCache, authMetadataClient service.AuthMetadataServiceClient) (TokenOrchestrator, error) { - clientConf, err := BuildClientConfig(ctx, authMetadataClient) - if err != nil { - return TokenOrchestrator{}, err - } - +func NewTokenOrchestrator(baseOrchestrator tokenorchestrator.BaseTokenOrchestrator, cfg Config) (TokenOrchestrator, error) { return TokenOrchestrator{ - cfg: cfg, - clientConfig: clientConf, - tokenCache: tokenCache, + BaseTokenOrchestrator: baseOrchestrator, + Config: cfg, }, nil } diff --git a/clients/go/admin/pkce/auth_flow_orchestrator_test.go b/clients/go/admin/pkce/auth_flow_orchestrator_test.go index 879c68de0f..76225de4ad 100644 --- a/clients/go/admin/pkce/auth_flow_orchestrator_test.go +++ b/clients/go/admin/pkce/auth_flow_orchestrator_test.go @@ -2,77 +2,29 @@ package pkce import ( "context" - "encoding/json" - "io/ioutil" "testing" - "github.com/flyteorg/flyteidl/clients/go/admin/mocks" - "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "golang.org/x/oauth2" -) - -func TestRefreshTheToken(t *testing.T) { - ctx := context.Background() - clientConf := &oauth2.Config{ - ClientID: "dummyClient", - } - orchestrator := TokenOrchestrator{ - clientConfig: clientConf, - } - plan, _ := ioutil.ReadFile("testdata/token.json") - var tokenData oauth2.Token - err := json.Unmarshal(plan, &tokenData) - assert.Nil(t, err) - t.Run("bad url in config", func(t *testing.T) { - refreshedToken, err := orchestrator.RefreshToken(ctx, &tokenData) - assert.Nil(t, refreshedToken) - assert.NotNil(t, err) - }) -} -func TestFetchFromCache(t *testing.T) { - ctx := context.Background() - metatdata := &service.OAuth2MetadataResponse{ - TokenEndpoint: "/token", - ScopesSupported: []string{"code", "all"}, - } - clientMetatadata := &service.PublicClientAuthConfigResponse{ - AuthorizationMetadataKey: "flyte_authorization", - RedirectUri: "http://localhost:8089/redirect", - } - mockAuthClient := new(mocks.AuthMetadataServiceClient) - mockAuthClient.OnGetOAuth2MetadataMatch(mock.Anything, mock.Anything).Return(metatdata, nil) - mockAuthClient.OnGetPublicClientConfigMatch(mock.Anything, mock.Anything).Return(clientMetatadata, nil) - orchestrator, err := NewTokenOrchestrator(ctx, Config{}, &TokenCacheInMemoryProvider{}, mockAuthClient) - assert.NoError(t, err) - - t.Run("no token in cache", func(t *testing.T) { - refreshedToken, err := orchestrator.FetchTokenFromCacheOrRefreshIt(ctx) - assert.Nil(t, refreshedToken) - assert.NotNil(t, err) - }) -} + "github.com/flyteorg/flyteidl/clients/go/admin/cache" + "github.com/flyteorg/flyteidl/clients/go/admin/oauth" + "github.com/flyteorg/flyteidl/clients/go/admin/tokenorchestrator" +) func TestFetchFromAuthFlow(t *testing.T) { ctx := context.Background() t.Run("fetch from auth flow", func(t *testing.T) { - metatdata := &service.OAuth2MetadataResponse{ - TokenEndpoint: "/token", - ScopesSupported: []string{"code", "all"}, - } - clientMetatadata := &service.PublicClientAuthConfigResponse{ - AuthorizationMetadataKey: "flyte_authorization", - RedirectUri: "http://localhost:8089/redirect", - } - - mockAuthClient := new(mocks.AuthMetadataServiceClient) - mockAuthClient.OnGetOAuth2MetadataMatch(mock.Anything, mock.Anything).Return(metatdata, nil) - mockAuthClient.OnGetPublicClientConfigMatch(mock.Anything, mock.Anything).Return(clientMetatadata, nil) - tokenCache := &TokenCacheInMemoryProvider{} - orchestrator, err := NewTokenOrchestrator(ctx, Config{}, tokenCache, mockAuthClient) + tokenCache := &cache.TokenCacheInMemoryProvider{} + orchestrator, err := NewTokenOrchestrator(tokenorchestrator.BaseTokenOrchestrator{ + ClientConfig: &oauth.Config{ + Config: &oauth2.Config{ + RedirectURL: "http://localhost:8089/redirect", + Scopes: []string{"code", "all"}, + }, + }, + TokenCache: tokenCache, + }, Config{}) assert.NoError(t, err) refreshedToken, err := orchestrator.FetchTokenFromAuthFlow(ctx) assert.Nil(t, refreshedToken) diff --git a/clients/go/admin/pkce/client_config.go b/clients/go/admin/pkce/client_config.go deleted file mode 100644 index 93c76397e1..0000000000 --- a/clients/go/admin/pkce/client_config.go +++ /dev/null @@ -1,34 +0,0 @@ -package pkce - -import ( - "context" - - "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" - - "golang.org/x/oauth2" -) - -// BuildClientConfig builds OAuth2 config from information retrieved through the anonymous auth metadata service. -func BuildClientConfig(ctx context.Context, authMetadataClient service.AuthMetadataServiceClient) (clientConf *oauth2.Config, err error) { - var clientResp *service.PublicClientAuthConfigResponse - if clientResp, err = authMetadataClient.GetPublicClientConfig(ctx, &service.PublicClientAuthConfigRequest{}); err != nil { - return nil, err - } - - var oauthMetaResp *service.OAuth2MetadataResponse - if oauthMetaResp, err = authMetadataClient.GetOAuth2Metadata(ctx, &service.OAuth2MetadataRequest{}); err != nil { - return nil, err - } - - clientConf = &oauth2.Config{ - ClientID: clientResp.ClientId, - RedirectURL: clientResp.RedirectUri, - Scopes: clientResp.Scopes, - Endpoint: oauth2.Endpoint{ - TokenURL: oauthMetaResp.TokenEndpoint, - AuthURL: oauthMetaResp.AuthorizationEndpoint, - }, - } - - return clientConf, nil -} diff --git a/clients/go/admin/pkce/config.go b/clients/go/admin/pkce/config.go index ff56dcf62c..320afa0a44 100644 --- a/clients/go/admin/pkce/config.go +++ b/clients/go/admin/pkce/config.go @@ -4,6 +4,6 @@ import "github.com/flyteorg/flytestdlib/config" // Config defines settings used for PKCE flow. type Config struct { - BrowserSessionTimeout config.Duration `json:"timeout"` - TokenRefreshGracePeriod config.Duration `json:"refreshTime"` + BrowserSessionTimeout config.Duration `json:"timeout" pflag:",Amount of time the browser session would be active for authentication from client app."` + TokenRefreshGracePeriod config.Duration `json:"refreshTime" pflag:",grace period from the token expiry after which it would refresh the token."` } diff --git a/clients/go/admin/pkce/handle_app_call_back.go b/clients/go/admin/pkce/handle_app_call_back.go index 022cce6d1b..0874a5a4c4 100644 --- a/clients/go/admin/pkce/handle_app_call_back.go +++ b/clients/go/admin/pkce/handle_app_call_back.go @@ -6,9 +6,11 @@ import ( "net/http" "golang.org/x/oauth2" + + "github.com/flyteorg/flyteidl/clients/go/admin/oauth" ) -func getAuthServerCallbackHandler(c *oauth2.Config, codeVerifier string, tokenChannel chan *oauth2.Token, +func getAuthServerCallbackHandler(c *oauth.Config, codeVerifier string, tokenChannel chan *oauth2.Token, errorChannel chan error, stateString string) func(rw http.ResponseWriter, req *http.Request) { return func(rw http.ResponseWriter, req *http.Request) { diff --git a/clients/go/admin/pkce/handle_app_call_back_test.go b/clients/go/admin/pkce/handle_app_call_back_test.go index fe86fb112f..91eed672c3 100644 --- a/clients/go/admin/pkce/handle_app_call_back_test.go +++ b/clients/go/admin/pkce/handle_app_call_back_test.go @@ -7,6 +7,8 @@ import ( "strings" "testing" + "github.com/flyteorg/flyteidl/clients/go/admin/oauth" + "github.com/stretchr/testify/assert" testhttp "github.com/stretchr/testify/http" "golang.org/x/oauth2" @@ -19,10 +21,10 @@ var ( ) func HandleAppCallBackSetup(t *testing.T, state string) (tokenChannel chan *oauth2.Token, errorChannel chan error) { - var testAuthConfig *oauth2.Config + var testAuthConfig *oauth.Config errorChannel = make(chan error, 1) tokenChannel = make(chan *oauth2.Token) - testAuthConfig = &oauth2.Config{} + testAuthConfig = &oauth.Config{Config: &oauth2.Config{}, DeviceEndpoint: "dummyDeviceEndpoint"} callBackFn = getAuthServerCallbackHandler(testAuthConfig, "", tokenChannel, errorChannel, state) assert.NotNil(t, callBackFn) req = &http.Request{ diff --git a/clients/go/admin/token_source_provider.go b/clients/go/admin/token_source_provider.go index 0a426bbfcb..9890c75daa 100644 --- a/clients/go/admin/token_source_provider.go +++ b/clients/go/admin/token_source_provider.go @@ -9,15 +9,17 @@ import ( "sync" "time" + "golang.org/x/oauth2" + "golang.org/x/oauth2/clientcredentials" "k8s.io/apimachinery/pkg/util/wait" + "github.com/flyteorg/flyteidl/clients/go/admin/cache" + "github.com/flyteorg/flyteidl/clients/go/admin/deviceflow" "github.com/flyteorg/flyteidl/clients/go/admin/externalprocess" - "github.com/flyteorg/flyteidl/clients/go/admin/pkce" + "github.com/flyteorg/flyteidl/clients/go/admin/tokenorchestrator" "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" "github.com/flyteorg/flytestdlib/logger" - "golang.org/x/oauth2" - "golang.org/x/oauth2/clientcredentials" ) // TokenSourceProvider defines the interface needed to provide a TokenSource that is used to @@ -26,7 +28,7 @@ type TokenSourceProvider interface { GetTokenSource(ctx context.Context) (oauth2.TokenSource, error) } -func NewTokenSourceProvider(ctx context.Context, cfg *Config, tokenCache pkce.TokenCache, +func NewTokenSourceProvider(ctx context.Context, cfg *Config, tokenCache cache.TokenCache, authClient service.AuthMetadataServiceClient) (TokenSourceProvider, error) { var tokenProvider TokenSourceProvider @@ -53,7 +55,12 @@ func NewTokenSourceProvider(ctx context.Context, cfg *Config, tokenCache pkce.To return nil, err } case AuthTypePkce: - tokenProvider, err = NewPKCETokenSourceProvider(ctx, cfg.PkceConfig, tokenCache, authClient) + baseTokenOrchestrator, err := tokenorchestrator.NewBaseTokenOrchestrator(ctx, tokenCache, authClient) + if err != nil { + return nil, err + } + + tokenProvider, err = NewPKCETokenSourceProvider(baseTokenOrchestrator, cfg.PkceConfig) if err != nil { return nil, err } @@ -62,6 +69,16 @@ func NewTokenSourceProvider(ctx context.Context, cfg *Config, tokenCache pkce.To if err != nil { return nil, err } + case AuthTypeDeviceFlow: + baseTokenOrchestrator, err := tokenorchestrator.NewBaseTokenOrchestrator(ctx, tokenCache, authClient) + if err != nil { + return nil, err + } + + tokenProvider, err = NewDeviceFlowTokenSourceProvider(baseTokenOrchestrator, cfg.DeviceFlowConfig) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unsupported type %v", cfg.AuthType) } @@ -93,13 +110,11 @@ type PKCETokenSourceProvider struct { tokenOrchestrator pkce.TokenOrchestrator } -func NewPKCETokenSourceProvider(ctx context.Context, pkceCfg pkce.Config, tokenCache pkce.TokenCache, authClient service.AuthMetadataServiceClient) (TokenSourceProvider, error) { - - tokenOrchestrator, err := pkce.NewTokenOrchestrator(ctx, pkceCfg, tokenCache, authClient) +func NewPKCETokenSourceProvider(baseTokenOrchestrator tokenorchestrator.BaseTokenOrchestrator, pkceCfg pkce.Config) (TokenSourceProvider, error) { + tokenOrchestrator, err := pkce.NewTokenOrchestrator(baseTokenOrchestrator, pkceCfg) if err != nil { return nil, err } - return PKCETokenSourceProvider{tokenOrchestrator: tokenOrchestrator}, nil } @@ -108,16 +123,16 @@ func (p PKCETokenSourceProvider) GetTokenSource(ctx context.Context) (oauth2.Tok } // Returns the token source which would be used for three legged oauth. eg : for admin to authorize access to flytectl -func GetPKCEAuthTokenSource(ctx context.Context, tokenOrchestrator pkce.TokenOrchestrator) (oauth2.TokenSource, error) { +func GetPKCEAuthTokenSource(ctx context.Context, pkceTokenOrchestrator pkce.TokenOrchestrator) (oauth2.TokenSource, error) { // explicitly ignore error while fetching token from cache. - authToken, err := tokenOrchestrator.FetchTokenFromCacheOrRefreshIt(ctx) + authToken, err := pkceTokenOrchestrator.FetchTokenFromCacheOrRefreshIt(ctx, pkceTokenOrchestrator.Config.BrowserSessionTimeout) if err != nil { logger.Warnf(ctx, "Failed fetching from cache. Will restart the flow. Error: %v", err) } if authToken == nil { // Fetch using auth flow - if authToken, err = tokenOrchestrator.FetchTokenFromAuthFlow(ctx); err != nil { + if authToken, err = pkceTokenOrchestrator.FetchTokenFromAuthFlow(ctx); err != nil { logger.Errorf(ctx, "Error fetching token using auth flow due to %v", err) return nil, err } @@ -216,3 +231,42 @@ func getRandomDuration(maxDuration time.Duration) time.Duration { d := wait.Jitter(maxDuration, 1) return d - maxDuration } + +type DeviceFlowTokenSourceProvider struct { + tokenOrchestrator deviceflow.TokenOrchestrator +} + +func NewDeviceFlowTokenSourceProvider(baseTokenOrchestrator tokenorchestrator.BaseTokenOrchestrator, deviceFlowConfig deviceflow.Config) (TokenSourceProvider, error) { + + tokenOrchestrator, err := deviceflow.NewDeviceFlowTokenOrchestrator(baseTokenOrchestrator, deviceFlowConfig) + if err != nil { + return nil, err + } + + return DeviceFlowTokenSourceProvider{tokenOrchestrator: tokenOrchestrator}, nil +} + +func (p DeviceFlowTokenSourceProvider) GetTokenSource(ctx context.Context) (oauth2.TokenSource, error) { + return GetDeviceFlowAuthTokenSource(ctx, p.tokenOrchestrator) +} + +// GetDeviceFlowAuthTokenSource Returns the token source which would be used for device auth flow +func GetDeviceFlowAuthTokenSource(ctx context.Context, deviceFlowOrchestrator deviceflow.TokenOrchestrator) (oauth2.TokenSource, error) { + // explicitly ignore error while fetching token from cache. + authToken, err := deviceFlowOrchestrator.FetchTokenFromCacheOrRefreshIt(ctx, deviceFlowOrchestrator.Config.TokenRefreshGracePeriod) + if err != nil { + logger.Warnf(ctx, "Failed fetching from cache. Will restart the flow. Error: %v", err) + } + + if authToken == nil { + // Fetch using auth flow + if authToken, err = deviceFlowOrchestrator.FetchTokenFromAuthFlow(ctx); err != nil { + logger.Errorf(ctx, "Error fetching token using auth flow due to %v", err) + return nil, err + } + } + + return &pkce.SimpleTokenSource{ + CachedToken: authToken, + }, nil +} diff --git a/clients/go/admin/tokenorchestrator/base_token_orchestrator.go b/clients/go/admin/tokenorchestrator/base_token_orchestrator.go new file mode 100644 index 0000000000..1136435da5 --- /dev/null +++ b/clients/go/admin/tokenorchestrator/base_token_orchestrator.go @@ -0,0 +1,94 @@ +package tokenorchestrator + +import ( + "context" + "fmt" + "time" + + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" + + "golang.org/x/oauth2" + + "github.com/flyteorg/flyteidl/clients/go/admin/cache" + "github.com/flyteorg/flyteidl/clients/go/admin/oauth" + "github.com/flyteorg/flytestdlib/config" + "github.com/flyteorg/flytestdlib/logger" +) + +// BaseTokenOrchestrator implements the main logic to initiate device authorization flow +type BaseTokenOrchestrator struct { + ClientConfig *oauth.Config + TokenCache cache.TokenCache +} + +// RefreshToken attempts to refresh the access token if a refresh token is provided. +func (t BaseTokenOrchestrator) RefreshToken(ctx context.Context, token *oauth2.Token) (*oauth2.Token, error) { + ts := t.ClientConfig.TokenSource(ctx, token) + var refreshedToken *oauth2.Token + var err error + refreshedToken, err = ts.Token() + if err != nil { + logger.Warnf(ctx, "failed to refresh the token due to %v and will be doing re-auth", err) + return nil, err + } + + if refreshedToken != nil { + logger.Debugf(ctx, "got a response from the refresh grant for old expiry %v with new expiry %v", + token.Expiry, refreshedToken.Expiry) + if refreshedToken.AccessToken != token.AccessToken { + if err = t.TokenCache.SaveToken(refreshedToken); err != nil { + logger.Errorf(ctx, "unable to save the new token due to %v", err) + return nil, err + } + } + } + + return refreshedToken, nil +} + +// FetchTokenFromCacheOrRefreshIt fetches the token from cache and refreshes it if it'll expire within the +// Config.TokenRefreshGracePeriod period. +func (t BaseTokenOrchestrator) FetchTokenFromCacheOrRefreshIt(ctx context.Context, tokenRefreshGracePeriod config.Duration) (token *oauth2.Token, err error) { + token, err = t.TokenCache.GetToken() + if err != nil { + return nil, err + } + + if !token.Valid() { + return nil, fmt.Errorf("token from cache is invalid") + } + + // If token doesn't need to be refreshed, return it. + if time.Now().Before(token.Expiry.Add(-tokenRefreshGracePeriod.Duration)) { + logger.Infof(ctx, "found the token in the cache") + return token, nil + } + token.Expiry = token.Expiry.Add(-tokenRefreshGracePeriod.Duration) + + token, err = t.RefreshToken(ctx, token) + if err != nil { + return nil, fmt.Errorf("failed to refresh token using cached token. Error: %w", err) + } + + if !token.Valid() { + return nil, fmt.Errorf("refreshed token is invalid") + } + + err = t.TokenCache.SaveToken(token) + if err != nil { + return nil, fmt.Errorf("failed to save token in the token cache. Error: %w", err) + } + + return token, nil +} + +func NewBaseTokenOrchestrator(ctx context.Context, tokenCache cache.TokenCache, authClient service.AuthMetadataServiceClient) (BaseTokenOrchestrator, error) { + clientConfig, err := oauth.BuildConfigFromMetadataService(ctx, authClient) + if err != nil { + return BaseTokenOrchestrator{}, err + } + return BaseTokenOrchestrator{ + ClientConfig: clientConfig, + TokenCache: tokenCache, + }, nil +} diff --git a/clients/go/admin/tokenorchestrator/base_token_orchestrator_test.go b/clients/go/admin/tokenorchestrator/base_token_orchestrator_test.go new file mode 100644 index 0000000000..136719a933 --- /dev/null +++ b/clients/go/admin/tokenorchestrator/base_token_orchestrator_test.go @@ -0,0 +1,137 @@ +package tokenorchestrator + +import ( + "context" + "encoding/json" + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "golang.org/x/oauth2" + + "github.com/flyteorg/flyteidl/clients/go/admin/cache" + cacheMocks "github.com/flyteorg/flyteidl/clients/go/admin/cache/mocks" + "github.com/flyteorg/flyteidl/clients/go/admin/mocks" + "github.com/flyteorg/flyteidl/clients/go/admin/oauth" + "github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service" + "github.com/flyteorg/flytestdlib/config" +) + +func TestRefreshTheToken(t *testing.T) { + ctx := context.Background() + clientConf := &oauth.Config{ + Config: &oauth2.Config{ + ClientID: "dummyClient", + }, + } + tokenCacheProvider := &cache.TokenCacheInMemoryProvider{} + orchestrator := BaseTokenOrchestrator{ + ClientConfig: clientConf, + TokenCache: tokenCacheProvider, + } + + plan, _ := os.ReadFile("testdata/token.json") + var tokenData oauth2.Token + err := json.Unmarshal(plan, &tokenData) + assert.Nil(t, err) + t.Run("bad url in Config", func(t *testing.T) { + refreshedToken, err := orchestrator.RefreshToken(ctx, &tokenData) + assert.Nil(t, refreshedToken) + assert.NotNil(t, err) + }) +} + +func TestFetchFromCache(t *testing.T) { + ctx := context.Background() + metatdata := &service.OAuth2MetadataResponse{ + TokenEndpoint: "/token", + ScopesSupported: []string{"code", "all"}, + } + clientMetatadata := &service.PublicClientAuthConfigResponse{ + AuthorizationMetadataKey: "flyte_authorization", + RedirectUri: "http://localhost:8089/redirect", + } + mockAuthClient := new(mocks.AuthMetadataServiceClient) + mockAuthClient.OnGetOAuth2MetadataMatch(mock.Anything, mock.Anything).Return(metatdata, nil) + mockAuthClient.OnGetPublicClientConfigMatch(mock.Anything, mock.Anything).Return(clientMetatadata, nil) + + t.Run("no token in cache", func(t *testing.T) { + tokenCacheProvider := &cache.TokenCacheInMemoryProvider{} + + orchestrator, err := NewBaseTokenOrchestrator(ctx, tokenCacheProvider, mockAuthClient) + + assert.NoError(t, err) + refreshedToken, err := orchestrator.FetchTokenFromCacheOrRefreshIt(ctx, config.Duration{Duration: 5 * time.Minute}) + assert.Nil(t, refreshedToken) + assert.NotNil(t, err) + }) + + t.Run("token in cache", func(t *testing.T) { + tokenCacheProvider := &cache.TokenCacheInMemoryProvider{} + orchestrator, err := NewBaseTokenOrchestrator(ctx, tokenCacheProvider, mockAuthClient) + assert.NoError(t, err) + fileData, _ := os.ReadFile("testdata/token.json") + var tokenData oauth2.Token + err = json.Unmarshal(fileData, &tokenData) + assert.Nil(t, err) + tokenData.Expiry = time.Now().Add(20 * time.Minute) + err = tokenCacheProvider.SaveToken(&tokenData) + assert.Nil(t, err) + cachedToken, err := orchestrator.FetchTokenFromCacheOrRefreshIt(ctx, config.Duration{Duration: 5 * time.Minute}) + assert.Nil(t, err) + assert.NotNil(t, cachedToken) + assert.Equal(t, tokenData.AccessToken, cachedToken.AccessToken) + }) + + t.Run("expired token in cache", func(t *testing.T) { + tokenCacheProvider := &cache.TokenCacheInMemoryProvider{} + orchestrator, err := NewBaseTokenOrchestrator(ctx, tokenCacheProvider, mockAuthClient) + assert.NoError(t, err) + fileData, _ := os.ReadFile("testdata/token.json") + var tokenData oauth2.Token + err = json.Unmarshal(fileData, &tokenData) + assert.Nil(t, err) + tokenData.Expiry = time.Now().Add(-20 * time.Minute) + err = tokenCacheProvider.SaveToken(&tokenData) + assert.Nil(t, err) + _, err = orchestrator.FetchTokenFromCacheOrRefreshIt(ctx, config.Duration{Duration: 5 * time.Minute}) + assert.NotNil(t, err) + }) + + t.Run("token fetch before grace period", func(t *testing.T) { + mockTokenCacheProvider := new(cacheMocks.TokenCache) + orchestrator, err := NewBaseTokenOrchestrator(ctx, mockTokenCacheProvider, mockAuthClient) + assert.NoError(t, err) + fileData, _ := os.ReadFile("testdata/token.json") + var tokenData oauth2.Token + err = json.Unmarshal(fileData, &tokenData) + assert.Nil(t, err) + tokenData.Expiry = time.Now().Add(20 * time.Minute) + mockTokenCacheProvider.OnGetTokenMatch(mock.Anything).Return(&tokenData, nil) + mockTokenCacheProvider.OnSaveTokenMatch(mock.Anything).Return(nil) + assert.Nil(t, err) + refreshedToken, err := orchestrator.FetchTokenFromCacheOrRefreshIt(ctx, config.Duration{Duration: 5 * time.Minute}) + assert.Nil(t, err) + assert.NotNil(t, refreshedToken) + mockTokenCacheProvider.AssertNotCalled(t, "SaveToken") + }) + + t.Run("token fetch after grace period with refresh", func(t *testing.T) { + mockTokenCacheProvider := new(cacheMocks.TokenCache) + orchestrator, err := NewBaseTokenOrchestrator(ctx, mockTokenCacheProvider, mockAuthClient) + assert.NoError(t, err) + fileData, _ := os.ReadFile("testdata/token.json") + var tokenData oauth2.Token + err = json.Unmarshal(fileData, &tokenData) + assert.Nil(t, err) + tokenData.Expiry = time.Now().Add(20 * time.Minute) + mockTokenCacheProvider.OnGetTokenMatch(mock.Anything).Return(&tokenData, nil) + assert.Nil(t, err) + refreshedToken, err := orchestrator.FetchTokenFromCacheOrRefreshIt(ctx, config.Duration{Duration: 5 * time.Minute}) + assert.Nil(t, err) + assert.NotNil(t, refreshedToken) + mockTokenCacheProvider.AssertNotCalled(t, "SaveToken") + }) +} diff --git a/clients/go/admin/pkce/testdata/token.json b/clients/go/admin/tokenorchestrator/testdata/token.json similarity index 100% rename from clients/go/admin/pkce/testdata/token.json rename to clients/go/admin/tokenorchestrator/testdata/token.json diff --git a/gen/pb-cpp/flyteidl/service/auth.pb.cc b/gen/pb-cpp/flyteidl/service/auth.pb.cc index 51a120d669..b24cb227e6 100644 --- a/gen/pb-cpp/flyteidl/service/auth.pb.cc +++ b/gen/pb-cpp/flyteidl/service/auth.pb.cc @@ -123,6 +123,7 @@ const ::google::protobuf::uint32 TableStruct_flyteidl_2fservice_2fauth_2eproto:: PROTOBUF_FIELD_OFFSET(::flyteidl::service::OAuth2MetadataResponse, jwks_uri_), PROTOBUF_FIELD_OFFSET(::flyteidl::service::OAuth2MetadataResponse, code_challenge_methods_supported_), PROTOBUF_FIELD_OFFSET(::flyteidl::service::OAuth2MetadataResponse, grant_types_supported_), + PROTOBUF_FIELD_OFFSET(::flyteidl::service::OAuth2MetadataResponse, device_authorization_endpoint_), ~0u, // no _has_bits_ PROTOBUF_FIELD_OFFSET(::flyteidl::service::PublicClientAuthConfigRequest, _internal_metadata_), ~0u, // no _extensions_ @@ -142,8 +143,8 @@ const ::google::protobuf::uint32 TableStruct_flyteidl_2fservice_2fauth_2eproto:: static const ::google::protobuf::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { { 0, -1, sizeof(::flyteidl::service::OAuth2MetadataRequest)}, { 5, -1, sizeof(::flyteidl::service::OAuth2MetadataResponse)}, - { 19, -1, sizeof(::flyteidl::service::PublicClientAuthConfigRequest)}, - { 24, -1, sizeof(::flyteidl::service::PublicClientAuthConfigResponse)}, + { 20, -1, sizeof(::flyteidl::service::PublicClientAuthConfigRequest)}, + { 25, -1, sizeof(::flyteidl::service::PublicClientAuthConfigResponse)}, }; static ::google::protobuf::Message const * const file_default_instances[] = { @@ -163,7 +164,7 @@ const char descriptor_table_protodef_flyteidl_2fservice_2fauth_2eproto[] = "\n\033flyteidl/service/auth.proto\022\020flyteidl." "service\032\034google/api/annotations.proto\032,p" "rotoc-gen-swagger/options/annotations.pr" - "oto\"\027\n\025OAuth2MetadataRequest\"\246\002\n\026OAuth2M" + "oto\"\027\n\025OAuth2MetadataRequest\"\315\002\n\026OAuth2M" "etadataResponse\022\016\n\006issuer\030\001 \001(\t\022\036\n\026autho" "rization_endpoint\030\002 \001(\t\022\026\n\016token_endpoin" "t\030\003 \001(\t\022 \n\030response_types_supported\030\004 \003(" @@ -171,31 +172,32 @@ const char descriptor_table_protodef_flyteidl_2fservice_2fauth_2eproto[] = "point_auth_methods_supported\030\006 \003(\t\022\020\n\010jw" "ks_uri\030\007 \001(\t\022(\n code_challenge_methods_s" "upported\030\010 \003(\t\022\035\n\025grant_types_supported\030" - "\t \003(\t\"\037\n\035PublicClientAuthConfigRequest\"\234" - "\001\n\036PublicClientAuthConfigResponse\022\021\n\tcli" - "ent_id\030\001 \001(\t\022\024\n\014redirect_uri\030\002 \001(\t\022\016\n\006sc" - "opes\030\003 \003(\t\022\"\n\032authorization_metadata_key" - "\030\004 \001(\t\022\035\n\025service_http_endpoint\030\005 \001(\t2\374\003" - "\n\023AuthMetadataService\022\365\001\n\021GetOAuth2Metad" - "ata\022\'.flyteidl.service.OAuth2MetadataReq" - "uest\032(.flyteidl.service.OAuth2MetadataRe" - "sponse\"\214\001\202\323\344\223\002)\022\'/.well-known/oauth-auth" - "orization-server\222AZ\032XRetrieves OAuth2 au" - "thorization server metadata. This endpoi" - "nt is anonymously accessible.\022\354\001\n\025GetPub" - "licClientConfig\022/.flyteidl.service.Publi" - "cClientAuthConfigRequest\0320.flyteidl.serv" - "ice.PublicClientAuthConfigResponse\"p\202\323\344\223" - "\002\031\022\027/config/v1/flyte_client\222AN\032LRetrieve" - "s public flyte client info. This endpoin" - "t is anonymously accessible.B9Z7github.c" - "om/flyteorg/flyteidl/gen/pb-go/flyteidl/" - "serviceb\006proto3" + "\t \003(\t\022%\n\035device_authorization_endpoint\030\n" + " \001(\t\"\037\n\035PublicClientAuthConfigRequest\"\234\001" + "\n\036PublicClientAuthConfigResponse\022\021\n\tclie" + "nt_id\030\001 \001(\t\022\024\n\014redirect_uri\030\002 \001(\t\022\016\n\006sco" + "pes\030\003 \003(\t\022\"\n\032authorization_metadata_key\030" + "\004 \001(\t\022\035\n\025service_http_endpoint\030\005 \001(\t2\374\003\n" + "\023AuthMetadataService\022\365\001\n\021GetOAuth2Metada" + "ta\022\'.flyteidl.service.OAuth2MetadataRequ" + "est\032(.flyteidl.service.OAuth2MetadataRes" + "ponse\"\214\001\202\323\344\223\002)\022\'/.well-known/oauth-autho" + "rization-server\222AZ\032XRetrieves OAuth2 aut" + "horization server metadata. This endpoin" + "t is anonymously accessible.\022\354\001\n\025GetPubl" + "icClientConfig\022/.flyteidl.service.Public" + "ClientAuthConfigRequest\0320.flyteidl.servi" + "ce.PublicClientAuthConfigResponse\"p\202\323\344\223\002" + "\031\022\027/config/v1/flyte_client\222AN\032LRetrieves" + " public flyte client info. This endpoint" + " is anonymously accessible.B9Z7github.co" + "m/flyteorg/flyteidl/gen/pb-go/flyteidl/s" + "erviceb\006proto3" ; ::google::protobuf::internal::DescriptorTable descriptor_table_flyteidl_2fservice_2fauth_2eproto = { false, InitDefaults_flyteidl_2fservice_2fauth_2eproto, descriptor_table_protodef_flyteidl_2fservice_2fauth_2eproto, - "flyteidl/service/auth.proto", &assign_descriptors_table_flyteidl_2fservice_2fauth_2eproto, 1215, + "flyteidl/service/auth.proto", &assign_descriptors_table_flyteidl_2fservice_2fauth_2eproto, 1254, }; void AddDescriptors_flyteidl_2fservice_2fauth_2eproto() { @@ -439,6 +441,7 @@ const int OAuth2MetadataResponse::kTokenEndpointAuthMethodsSupportedFieldNumber; const int OAuth2MetadataResponse::kJwksUriFieldNumber; const int OAuth2MetadataResponse::kCodeChallengeMethodsSupportedFieldNumber; const int OAuth2MetadataResponse::kGrantTypesSupportedFieldNumber; +const int OAuth2MetadataResponse::kDeviceAuthorizationEndpointFieldNumber; #endif // !defined(_MSC_VER) || _MSC_VER >= 1900 OAuth2MetadataResponse::OAuth2MetadataResponse() @@ -471,6 +474,10 @@ OAuth2MetadataResponse::OAuth2MetadataResponse(const OAuth2MetadataResponse& fro if (from.jwks_uri().size() > 0) { jwks_uri_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.jwks_uri_); } + device_authorization_endpoint_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + if (from.device_authorization_endpoint().size() > 0) { + device_authorization_endpoint_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.device_authorization_endpoint_); + } // @@protoc_insertion_point(copy_constructor:flyteidl.service.OAuth2MetadataResponse) } @@ -481,6 +488,7 @@ void OAuth2MetadataResponse::SharedCtor() { authorization_endpoint_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); token_endpoint_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); jwks_uri_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + device_authorization_endpoint_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } OAuth2MetadataResponse::~OAuth2MetadataResponse() { @@ -493,6 +501,7 @@ void OAuth2MetadataResponse::SharedDtor() { authorization_endpoint_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); token_endpoint_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); jwks_uri_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + device_authorization_endpoint_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); } void OAuth2MetadataResponse::SetCachedSize(int size) const { @@ -519,6 +528,7 @@ void OAuth2MetadataResponse::Clear() { authorization_endpoint_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); token_endpoint_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); jwks_uri_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + device_authorization_endpoint_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); _internal_metadata_.Clear(); } @@ -694,6 +704,22 @@ const char* OAuth2MetadataResponse::_InternalParse(const char* begin, const char } while ((::google::protobuf::io::UnalignedLoad<::google::protobuf::uint64>(ptr) & 255) == 74 && (ptr += 1)); break; } + // string device_authorization_endpoint = 10; + case 10: { + if (static_cast<::google::protobuf::uint8>(tag) != 82) goto handle_unusual; + ptr = ::google::protobuf::io::ReadSize(ptr, &size); + GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); + ctx->extra_parse_data().SetFieldName("flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint"); + object = msg->mutable_device_authorization_endpoint(); + if (size > end - ptr + ::google::protobuf::internal::ParseContext::kSlopBytes) { + parser_till_end = ::google::protobuf::internal::GreedyStringParserUTF8; + goto string_till_end; + } + GOOGLE_PROTOBUF_PARSER_ASSERT(::google::protobuf::internal::StringCheckUTF8(ptr, size, ctx)); + ::google::protobuf::internal::InlineGreedyStringParser(object, ptr, size, ctx); + ptr += size; + break; + } default: { handle_unusual: if ((tag & 7) == 4 || tag == 0) { @@ -868,6 +894,21 @@ bool OAuth2MetadataResponse::MergePartialFromCodedStream( break; } + // string device_authorization_endpoint = 10; + case 10: { + if (static_cast< ::google::protobuf::uint8>(tag) == (82 & 0xFF)) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_device_authorization_endpoint())); + DO_(::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->device_authorization_endpoint().data(), static_cast(this->device_authorization_endpoint().length()), + ::google::protobuf::internal::WireFormatLite::PARSE, + "flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint")); + } else { + goto handle_unusual; + } + break; + } + default: { handle_unusual: if (tag == 0) { @@ -985,6 +1026,16 @@ void OAuth2MetadataResponse::SerializeWithCachedSizes( 9, this->grant_types_supported(i), output); } + // string device_authorization_endpoint = 10; + if (this->device_authorization_endpoint().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->device_authorization_endpoint().data(), static_cast(this->device_authorization_endpoint().length()), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 10, this->device_authorization_endpoint(), output); + } + if (_internal_metadata_.have_unknown_fields()) { ::google::protobuf::internal::WireFormat::SerializeUnknownFields( _internal_metadata_.unknown_fields(), output); @@ -1092,6 +1143,17 @@ ::google::protobuf::uint8* OAuth2MetadataResponse::InternalSerializeWithCachedSi WriteStringToArray(9, this->grant_types_supported(i), target); } + // string device_authorization_endpoint = 10; + if (this->device_authorization_endpoint().size() > 0) { + ::google::protobuf::internal::WireFormatLite::VerifyUtf8String( + this->device_authorization_endpoint().data(), static_cast(this->device_authorization_endpoint().length()), + ::google::protobuf::internal::WireFormatLite::SERIALIZE, + "flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 10, this->device_authorization_endpoint(), target); + } + if (_internal_metadata_.have_unknown_fields()) { target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( _internal_metadata_.unknown_fields(), target); @@ -1181,6 +1243,13 @@ size_t OAuth2MetadataResponse::ByteSizeLong() const { this->jwks_uri()); } + // string device_authorization_endpoint = 10; + if (this->device_authorization_endpoint().size() > 0) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->device_authorization_endpoint()); + } + int cached_size = ::google::protobuf::internal::ToCachedSize(total_size); SetCachedSize(cached_size); return total_size; @@ -1229,6 +1298,10 @@ void OAuth2MetadataResponse::MergeFrom(const OAuth2MetadataResponse& from) { jwks_uri_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.jwks_uri_); } + if (from.device_authorization_endpoint().size() > 0) { + + device_authorization_endpoint_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.device_authorization_endpoint_); + } } void OAuth2MetadataResponse::CopyFrom(const ::google::protobuf::Message& from) { @@ -1269,6 +1342,8 @@ void OAuth2MetadataResponse::InternalSwap(OAuth2MetadataResponse* other) { GetArenaNoVirtual()); jwks_uri_.Swap(&other->jwks_uri_, &::google::protobuf::internal::GetEmptyStringAlreadyInited(), GetArenaNoVirtual()); + device_authorization_endpoint_.Swap(&other->device_authorization_endpoint_, &::google::protobuf::internal::GetEmptyStringAlreadyInited(), + GetArenaNoVirtual()); } ::google::protobuf::Metadata OAuth2MetadataResponse::GetMetadata() const { diff --git a/gen/pb-cpp/flyteidl/service/auth.pb.h b/gen/pb-cpp/flyteidl/service/auth.pb.h index 72e16b3e9b..609bd43b44 100644 --- a/gen/pb-cpp/flyteidl/service/auth.pb.h +++ b/gen/pb-cpp/flyteidl/service/auth.pb.h @@ -445,6 +445,20 @@ class OAuth2MetadataResponse final : ::std::string* release_jwks_uri(); void set_allocated_jwks_uri(::std::string* jwks_uri); + // string device_authorization_endpoint = 10; + void clear_device_authorization_endpoint(); + static const int kDeviceAuthorizationEndpointFieldNumber = 10; + const ::std::string& device_authorization_endpoint() const; + void set_device_authorization_endpoint(const ::std::string& value); + #if LANG_CXX11 + void set_device_authorization_endpoint(::std::string&& value); + #endif + void set_device_authorization_endpoint(const char* value); + void set_device_authorization_endpoint(const char* value, size_t size); + ::std::string* mutable_device_authorization_endpoint(); + ::std::string* release_device_authorization_endpoint(); + void set_allocated_device_authorization_endpoint(::std::string* device_authorization_endpoint); + // @@protoc_insertion_point(class_scope:flyteidl.service.OAuth2MetadataResponse) private: class HasBitSetters; @@ -459,6 +473,7 @@ class OAuth2MetadataResponse final : ::google::protobuf::internal::ArenaStringPtr authorization_endpoint_; ::google::protobuf::internal::ArenaStringPtr token_endpoint_; ::google::protobuf::internal::ArenaStringPtr jwks_uri_; + ::google::protobuf::internal::ArenaStringPtr device_authorization_endpoint_; mutable ::google::protobuf::internal::CachedSize _cached_size_; friend struct ::TableStruct_flyteidl_2fservice_2fauth_2eproto; }; @@ -1327,6 +1342,59 @@ OAuth2MetadataResponse::mutable_grant_types_supported() { return &grant_types_supported_; } +// string device_authorization_endpoint = 10; +inline void OAuth2MetadataResponse::clear_device_authorization_endpoint() { + device_authorization_endpoint_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline const ::std::string& OAuth2MetadataResponse::device_authorization_endpoint() const { + // @@protoc_insertion_point(field_get:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) + return device_authorization_endpoint_.GetNoArena(); +} +inline void OAuth2MetadataResponse::set_device_authorization_endpoint(const ::std::string& value) { + + device_authorization_endpoint_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) +} +#if LANG_CXX11 +inline void OAuth2MetadataResponse::set_device_authorization_endpoint(::std::string&& value) { + + device_authorization_endpoint_.SetNoArena( + &::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); + // @@protoc_insertion_point(field_set_rvalue:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) +} +#endif +inline void OAuth2MetadataResponse::set_device_authorization_endpoint(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + device_authorization_endpoint_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) +} +inline void OAuth2MetadataResponse::set_device_authorization_endpoint(const char* value, size_t size) { + + device_authorization_endpoint_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) +} +inline ::std::string* OAuth2MetadataResponse::mutable_device_authorization_endpoint() { + + // @@protoc_insertion_point(field_mutable:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) + return device_authorization_endpoint_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* OAuth2MetadataResponse::release_device_authorization_endpoint() { + // @@protoc_insertion_point(field_release:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) + + return device_authorization_endpoint_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void OAuth2MetadataResponse::set_allocated_device_authorization_endpoint(::std::string* device_authorization_endpoint) { + if (device_authorization_endpoint != nullptr) { + + } else { + + } + device_authorization_endpoint_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), device_authorization_endpoint); + // @@protoc_insertion_point(field_set_allocated:flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint) +} + // ------------------------------------------------------------------- // PublicClientAuthConfigRequest diff --git a/gen/pb-go/flyteidl/service/auth.pb.go b/gen/pb-go/flyteidl/service/auth.pb.go index 5c44181574..bbec92c4fc 100644 --- a/gen/pb-go/flyteidl/service/auth.pb.go +++ b/gen/pb-go/flyteidl/service/auth.pb.go @@ -81,10 +81,12 @@ type OAuth2MetadataResponse struct { // this authorization server. CodeChallengeMethodsSupported []string `protobuf:"bytes,8,rep,name=code_challenge_methods_supported,json=codeChallengeMethodsSupported,proto3" json:"code_challenge_methods_supported,omitempty"` // JSON array containing a list of the OAuth 2.0 grant type values that this authorization server supports. - GrantTypesSupported []string `protobuf:"bytes,9,rep,name=grant_types_supported,json=grantTypesSupported,proto3" json:"grant_types_supported,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + GrantTypesSupported []string `protobuf:"bytes,9,rep,name=grant_types_supported,json=grantTypesSupported,proto3" json:"grant_types_supported,omitempty"` + // URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628] + DeviceAuthorizationEndpoint string `protobuf:"bytes,10,opt,name=device_authorization_endpoint,json=deviceAuthorizationEndpoint,proto3" json:"device_authorization_endpoint,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *OAuth2MetadataResponse) Reset() { *m = OAuth2MetadataResponse{} } @@ -175,6 +177,13 @@ func (m *OAuth2MetadataResponse) GetGrantTypesSupported() []string { return nil } +func (m *OAuth2MetadataResponse) GetDeviceAuthorizationEndpoint() string { + if m != nil { + return m.DeviceAuthorizationEndpoint + } + return "" +} + type PublicClientAuthConfigRequest struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -296,51 +305,52 @@ func init() { func init() { proto.RegisterFile("flyteidl/service/auth.proto", fileDescriptor_6eee4a0c193ab842) } var fileDescriptor_6eee4a0c193ab842 = []byte{ - // 690 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x4f, 0xd4, 0x4e, - 0x18, 0xce, 0xb2, 0xb0, 0xb0, 0xf3, 0xfb, 0xe2, 0x57, 0xb2, 0x4b, 0x59, 0x44, 0x87, 0x4d, 0x08, - 0x90, 0xb8, 0x1d, 0xc5, 0x18, 0x35, 0xf1, 0x82, 0xc4, 0xa0, 0x51, 0x94, 0x2c, 0x98, 0x18, 0x2e, - 0x4d, 0xb7, 0x7d, 0x69, 0xc7, 0xed, 0xce, 0xd4, 0x99, 0x29, 0x9b, 0xf5, 0xe8, 0xd9, 0x13, 0xfe, - 0x69, 0x9e, 0xbd, 0x79, 0x32, 0x5e, 0xbd, 0x79, 0x30, 0x9d, 0x69, 0x59, 0xca, 0xe2, 0xd7, 0xa9, - 0xe9, 0x3c, 0xcf, 0xfb, 0xf5, 0xbc, 0xcf, 0x0c, 0x5a, 0x3e, 0x8e, 0x47, 0x0a, 0x68, 0x10, 0x13, - 0x09, 0xe2, 0x84, 0xfa, 0x40, 0xbc, 0x54, 0x45, 0x4e, 0x22, 0xb8, 0xe2, 0xd6, 0x7c, 0x01, 0x3a, - 0x39, 0xd8, 0xba, 0x12, 0x72, 0x1e, 0xc6, 0x40, 0xbc, 0x84, 0x12, 0x8f, 0x31, 0xae, 0x3c, 0x45, - 0x39, 0x93, 0x86, 0xdf, 0xba, 0xae, 0x3f, 0x7e, 0x27, 0x04, 0xd6, 0x91, 0x43, 0x2f, 0x0c, 0x41, - 0x10, 0x9e, 0x68, 0xc6, 0x24, 0xbb, 0xbd, 0x88, 0x1a, 0xcf, 0xb7, 0x53, 0x15, 0x6d, 0xed, 0x81, - 0xf2, 0x02, 0x4f, 0x79, 0x5d, 0x78, 0x9d, 0x82, 0x54, 0xed, 0x8f, 0x55, 0xd4, 0xbc, 0x88, 0xc8, - 0x84, 0x33, 0x09, 0x56, 0x13, 0xd5, 0xa8, 0x94, 0x29, 0x08, 0xbb, 0x82, 0x2b, 0x1b, 0xf5, 0x6e, - 0xfe, 0x67, 0xdd, 0x46, 0xcd, 0xac, 0x6f, 0x2e, 0xe8, 0x1b, 0x5d, 0xc3, 0x05, 0x16, 0x24, 0x9c, - 0x32, 0x65, 0x4f, 0x69, 0x5e, 0xa3, 0x84, 0x3e, 0xcc, 0x41, 0x6b, 0x0d, 0xfd, 0xab, 0x78, 0x1f, - 0xce, 0xd1, 0xab, 0x9a, 0xfe, 0x8f, 0x3e, 0x3d, 0xa3, 0xdd, 0x45, 0xb6, 0xc8, 0x3b, 0x70, 0xd5, - 0x28, 0x01, 0xe9, 0xca, 0x34, 0x49, 0xb8, 0x50, 0x10, 0xd8, 0xd3, 0xb8, 0xba, 0x51, 0xef, 0x36, - 0x0b, 0xfc, 0x30, 0x83, 0x0f, 0x0a, 0xd4, 0xda, 0x44, 0xf3, 0xd2, 0xe7, 0xe5, 0x88, 0x19, 0x1d, - 0xf1, 0x9f, 0x39, 0x1f, 0x53, 0xf7, 0xd1, 0x5a, 0xb9, 0x17, 0x37, 0xeb, 0xd9, 0x1d, 0x80, 0x8a, - 0x78, 0x70, 0x3e, 0xbe, 0xa6, 0xe3, 0x57, 0x4b, 0x2d, 0x66, 0x6a, 0xed, 0x19, 0xe6, 0x38, 0xe3, - 0x12, 0x9a, 0x7b, 0x35, 0xec, 0x4b, 0x37, 0x15, 0xd4, 0x9e, 0xd5, 0x73, 0xcd, 0x66, 0xff, 0x2f, - 0x04, 0xb5, 0x76, 0x11, 0xf6, 0x79, 0x00, 0xae, 0x1f, 0x79, 0x71, 0x0c, 0x2c, 0x84, 0x4b, 0xea, - 0xcc, 0xe9, 0x3a, 0x2b, 0x19, 0x6f, 0xa7, 0xa0, 0x4d, 0xd4, 0xd8, 0x42, 0x8d, 0x50, 0x78, 0x4c, - 0x4d, 0xe8, 0x52, 0xd7, 0xd1, 0x0b, 0x1a, 0x2c, 0x8b, 0xd2, 0xbe, 0x86, 0x56, 0xf6, 0xd3, 0x5e, - 0x4c, 0xfd, 0x9d, 0x98, 0x82, 0xe9, 0x7d, 0x87, 0xb3, 0x63, 0x1a, 0x16, 0x06, 0xf8, 0x5c, 0x41, - 0x57, 0x7f, 0xc4, 0xc8, 0x8d, 0xb0, 0x8c, 0xea, 0xbe, 0xc6, 0x5c, 0x1a, 0xe4, 0x5e, 0x98, 0x33, - 0x07, 0x8f, 0x03, 0x6b, 0x15, 0xfd, 0x2d, 0x20, 0xa0, 0x02, 0x7c, 0xa5, 0x87, 0x37, 0x1e, 0xf8, - 0xab, 0x38, 0xcb, 0x04, 0x68, 0xa2, 0x9a, 0x59, 0x80, 0x5d, 0xd5, 0x8d, 0xe6, 0x7f, 0xd6, 0x7d, - 0xd4, 0x2a, 0x1b, 0x69, 0x90, 0x5b, 0xd0, 0xed, 0xc3, 0xc8, 0x9e, 0xd6, 0x89, 0xec, 0x12, 0xa3, - 0xf0, 0xe8, 0x13, 0x18, 0x65, 0x6a, 0xe4, 0x37, 0xc5, 0x8d, 0x94, 0x4a, 0xc6, 0xb6, 0x9a, 0xd1, - 0x81, 0x0b, 0x39, 0xf8, 0x48, 0xa9, 0xa4, 0xd8, 0xdc, 0xd6, 0xb7, 0x2a, 0x5a, 0xc8, 0xd7, 0xa7, - 0xf3, 0x1c, 0x18, 0x8e, 0xf5, 0xb5, 0x82, 0xfe, 0xdf, 0x05, 0x55, 0xbe, 0x08, 0xd6, 0xba, 0x73, - 0xf1, 0x4e, 0x3a, 0x97, 0x5e, 0xa2, 0xd6, 0xc6, 0xaf, 0x89, 0x46, 0xca, 0xf6, 0xbb, 0xca, 0xe9, - 0xf6, 0x51, 0xeb, 0x65, 0x17, 0x94, 0xa0, 0x70, 0x02, 0x12, 0x1b, 0x1e, 0x2e, 0x4d, 0x89, 0xb3, - 0x24, 0x20, 0x70, 0x21, 0x87, 0x83, 0x0f, 0x23, 0x2a, 0x71, 0x31, 0x20, 0xa6, 0x12, 0x7b, 0x8c, - 0xb3, 0xd1, 0x80, 0xa7, 0x32, 0x1e, 0x61, 0xcf, 0xf7, 0x41, 0x4a, 0xda, 0x8b, 0xc1, 0x79, 0xfb, - 0xe1, 0xd3, 0xfb, 0xa9, 0x4d, 0x6b, 0x9d, 0x38, 0x43, 0x88, 0xe3, 0x4e, 0x9f, 0xf1, 0x21, 0x23, - 0x3c, 0x4b, 0xde, 0x29, 0x55, 0xe8, 0x98, 0x0a, 0xd6, 0x97, 0x0a, 0x6a, 0xec, 0x82, 0x3a, 0xbf, - 0x7f, 0xb3, 0x7b, 0x8b, 0x4c, 0x8e, 0xf4, 0x53, 0x1f, 0xb5, 0x6e, 0xfc, 0x7e, 0x40, 0xae, 0x45, - 0x72, 0xba, 0xfd, 0xac, 0xf5, 0x74, 0x2c, 0x45, 0xa2, 0xe9, 0x58, 0xa7, 0xc1, 0xc6, 0x5f, 0x98, - 0xb2, 0x63, 0xfe, 0x87, 0xe3, 0x2f, 0x59, 0x8b, 0xc4, 0xd7, 0x95, 0xc8, 0xc9, 0x4d, 0xa2, 0xb3, - 0xb9, 0x26, 0xdb, 0x83, 0x7b, 0x47, 0x77, 0x42, 0xaa, 0xa2, 0xb4, 0xe7, 0xf8, 0x7c, 0x60, 0x20, - 0x2e, 0x42, 0x72, 0xf6, 0x2c, 0x87, 0xc0, 0x48, 0xd2, 0xeb, 0x84, 0x9c, 0x5c, 0x7c, 0xa9, 0x7b, - 0x35, 0xfd, 0x8e, 0xde, 0xfa, 0x1e, 0x00, 0x00, 0xff, 0xff, 0x44, 0x71, 0x10, 0xa8, 0xc4, 0x05, - 0x00, 0x00, + // 710 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0x55, 0x9a, 0x36, 0x6d, 0xe6, 0xfb, 0x80, 0xe2, 0x2a, 0xa9, 0x9b, 0x52, 0x98, 0x46, 0xaa, + 0xda, 0x4a, 0xc4, 0x86, 0x22, 0x04, 0x48, 0x6c, 0xd2, 0x0a, 0x15, 0x04, 0x85, 0x2a, 0x2d, 0x12, + 0xea, 0xc6, 0x72, 0xec, 0x5b, 0x7b, 0x88, 0x33, 0x63, 0x66, 0xc6, 0x8d, 0xc2, 0x92, 0x35, 0xab, + 0xf2, 0x04, 0x3c, 0x13, 0xaf, 0xc0, 0x0a, 0xb1, 0x65, 0xc7, 0x02, 0x79, 0xc6, 0x6e, 0xea, 0xa4, + 0xe5, 0x67, 0x65, 0x79, 0xce, 0xb9, 0x7f, 0xe7, 0x9e, 0x19, 0xb4, 0x7c, 0x1c, 0x0d, 0x25, 0x10, + 0x3f, 0xb2, 0x05, 0xf0, 0x13, 0xe2, 0x81, 0xed, 0x26, 0x32, 0xb4, 0x62, 0xce, 0x24, 0x33, 0xe6, + 0x73, 0xd0, 0xca, 0xc0, 0xc6, 0x8d, 0x80, 0xb1, 0x20, 0x02, 0xdb, 0x8d, 0x89, 0xed, 0x52, 0xca, + 0xa4, 0x2b, 0x09, 0xa3, 0x42, 0xf3, 0x1b, 0xb7, 0xd5, 0xc7, 0x6b, 0x05, 0x40, 0x5b, 0x62, 0xe0, + 0x06, 0x01, 0x70, 0x9b, 0xc5, 0x8a, 0x31, 0xc9, 0x6e, 0x2e, 0xa2, 0xda, 0xab, 0x76, 0x22, 0xc3, + 0xad, 0x3d, 0x90, 0xae, 0xef, 0x4a, 0xb7, 0x03, 0xef, 0x12, 0x10, 0xb2, 0xf9, 0x79, 0x1a, 0xd5, + 0xc7, 0x11, 0x11, 0x33, 0x2a, 0xc0, 0xa8, 0xa3, 0x0a, 0x11, 0x22, 0x01, 0x6e, 0x96, 0x70, 0x69, + 0xa3, 0xda, 0xc9, 0xfe, 0x8c, 0xfb, 0xa8, 0x9e, 0xf6, 0xcd, 0x38, 0x79, 0xaf, 0x6a, 0x38, 0x40, + 0xfd, 0x98, 0x11, 0x2a, 0xcd, 0x29, 0xc5, 0xab, 0x15, 0xd0, 0x27, 0x19, 0x68, 0xac, 0xa1, 0xab, + 0x92, 0xf5, 0xe0, 0x1c, 0xbd, 0xac, 0xe8, 0x57, 0xd4, 0xe9, 0x19, 0xed, 0x21, 0x32, 0x79, 0xd6, + 0x81, 0x23, 0x87, 0x31, 0x08, 0x47, 0x24, 0x71, 0xcc, 0xb8, 0x04, 0xdf, 0x9c, 0xc6, 0xe5, 0x8d, + 0x6a, 0xa7, 0x9e, 0xe3, 0x87, 0x29, 0x7c, 0x90, 0xa3, 0xc6, 0x26, 0x9a, 0x17, 0x1e, 0x2b, 0x46, + 0xcc, 0xa8, 0x88, 0x6b, 0xfa, 0x7c, 0x44, 0xdd, 0x47, 0x6b, 0xc5, 0x5e, 0x9c, 0xb4, 0x67, 0xa7, + 0x0f, 0x32, 0x64, 0xfe, 0xf9, 0xf8, 0x8a, 0x8a, 0x5f, 0x2d, 0xb4, 0x98, 0xaa, 0xb5, 0xa7, 0x99, + 0xa3, 0x8c, 0x4b, 0x68, 0xee, 0xed, 0xa0, 0x27, 0x9c, 0x84, 0x13, 0x73, 0x56, 0xcd, 0x35, 0x9b, + 0xfe, 0xbf, 0xe6, 0xc4, 0xd8, 0x45, 0xd8, 0x63, 0x3e, 0x38, 0x5e, 0xe8, 0x46, 0x11, 0xd0, 0x00, + 0x2e, 0xa8, 0x33, 0xa7, 0xea, 0xac, 0xa4, 0xbc, 0x9d, 0x9c, 0x36, 0x51, 0x63, 0x0b, 0xd5, 0x02, + 0xee, 0x52, 0x39, 0xa1, 0x4b, 0x55, 0x45, 0x2f, 0x28, 0x70, 0x4c, 0x94, 0x6d, 0xb4, 0xe2, 0x43, + 0x6a, 0x27, 0xe7, 0x92, 0x9d, 0x21, 0xd5, 0xec, 0xb2, 0x26, 0xb5, 0x2f, 0xda, 0x5c, 0xf3, 0x16, + 0x5a, 0xd9, 0x4f, 0xba, 0x11, 0xf1, 0x76, 0x22, 0x02, 0x7a, 0xfe, 0x1d, 0x46, 0x8f, 0x49, 0x90, + 0x9b, 0xe8, 0x5b, 0x09, 0xdd, 0xbc, 0x8c, 0x91, 0x99, 0x69, 0x19, 0x55, 0x3d, 0x85, 0x39, 0xc4, + 0xcf, 0xfc, 0x34, 0xa7, 0x0f, 0x9e, 0xf9, 0xc6, 0x2a, 0xfa, 0x9f, 0x83, 0x4f, 0x38, 0x78, 0x52, + 0x09, 0xa8, 0x7d, 0xf4, 0x5f, 0x7e, 0x96, 0x8a, 0x58, 0x47, 0x15, 0xbd, 0x44, 0xb3, 0xac, 0x86, + 0xcd, 0xfe, 0x8c, 0xc7, 0xa8, 0x51, 0x1c, 0xac, 0x9f, 0xd9, 0xd8, 0xe9, 0xc1, 0xd0, 0x9c, 0x56, + 0x89, 0xcc, 0x02, 0x23, 0xf7, 0xf9, 0x73, 0x18, 0xa6, 0x8a, 0x66, 0xb7, 0xcd, 0x09, 0xa5, 0x8c, + 0x47, 0xaa, 0xcc, 0xa8, 0xc0, 0x85, 0x0c, 0x7c, 0x2a, 0x65, 0x9c, 0xab, 0xb1, 0xf5, 0xb3, 0x8c, + 0x16, 0x32, 0x0b, 0xa8, 0x3c, 0x07, 0x9a, 0x63, 0xfc, 0x28, 0xa1, 0xeb, 0xbb, 0x20, 0x8b, 0x97, + 0xc9, 0x58, 0xb7, 0xc6, 0xef, 0xb5, 0x75, 0xe1, 0x45, 0x6c, 0x6c, 0xfc, 0x99, 0xa8, 0xa5, 0x6c, + 0x7e, 0x2c, 0x9d, 0xb6, 0x8f, 0x1a, 0x6f, 0x3a, 0x20, 0x39, 0x81, 0x13, 0x10, 0x58, 0xf3, 0x70, + 0x61, 0x4a, 0x9c, 0x26, 0x01, 0x8e, 0x73, 0x39, 0x2c, 0x7c, 0x18, 0x12, 0x81, 0xf3, 0x01, 0x31, + 0x11, 0xd8, 0xa5, 0x8c, 0x0e, 0xfb, 0x2c, 0x11, 0xd1, 0x10, 0xbb, 0x9e, 0x07, 0x42, 0x90, 0x6e, + 0x04, 0xd6, 0x87, 0x2f, 0x5f, 0x3f, 0x4d, 0x6d, 0x1a, 0xeb, 0xb6, 0x35, 0x80, 0x28, 0x6a, 0xf5, + 0x28, 0x1b, 0x50, 0x9b, 0xa5, 0xc9, 0x5b, 0x85, 0x0a, 0x2d, 0x5d, 0xc1, 0xf8, 0x5e, 0x42, 0xb5, + 0x5d, 0x90, 0xe7, 0xf7, 0xaf, 0x77, 0x6f, 0xd8, 0x93, 0x23, 0xfd, 0xd6, 0x47, 0x8d, 0x3b, 0x7f, + 0x1f, 0x90, 0x69, 0x11, 0x9f, 0xb6, 0x5f, 0x36, 0x5e, 0x8c, 0xa4, 0x88, 0x15, 0x1d, 0xab, 0x34, + 0x58, 0xfb, 0x0b, 0x13, 0x7a, 0xcc, 0xfe, 0x71, 0xfc, 0x25, 0x63, 0xd1, 0xf6, 0x54, 0x25, 0xfb, + 0xe4, 0xae, 0xad, 0xb2, 0x39, 0x3a, 0xdb, 0xf6, 0xa3, 0xa3, 0x07, 0x01, 0x91, 0x61, 0xd2, 0xb5, + 0x3c, 0xd6, 0xd7, 0x10, 0xe3, 0x81, 0x7d, 0xf6, 0xb4, 0x07, 0x40, 0xed, 0xb8, 0xdb, 0x0a, 0x98, + 0x3d, 0xfe, 0xda, 0x77, 0x2b, 0xea, 0x2d, 0xbe, 0xf7, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x97, 0x52, + 0x2f, 0x35, 0x08, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/gen/pb-go/flyteidl/service/auth.swagger.json b/gen/pb-go/flyteidl/service/auth.swagger.json index 544bbfde3b..acdc0fd87b 100644 --- a/gen/pb-go/flyteidl/service/auth.swagger.json +++ b/gen/pb-go/flyteidl/service/auth.swagger.json @@ -106,6 +106,10 @@ "type": "string" }, "description": "JSON array containing a list of the OAuth 2.0 grant type values that this authorization server supports." + }, + "device_authorization_endpoint": { + "type": "string", + "title": "URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]" } }, "title": "OAuth2MetadataResponse defines an RFC-Compliant response for /.well-known/oauth-authorization-server metadata\nas defined in https://tools.ietf.org/html/rfc8414" diff --git a/gen/pb-java/flyteidl/service/Auth.java b/gen/pb-java/flyteidl/service/Auth.java index f15d98d211..dd19ee9294 100644 --- a/gen/pb-java/flyteidl/service/Auth.java +++ b/gen/pb-java/flyteidl/service/Auth.java @@ -685,6 +685,24 @@ public interface OAuth2MetadataResponseOrBuilder extends */ com.google.protobuf.ByteString getGrantTypesSupportedBytes(int index); + + /** + *
+     * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+     * 
+ * + * string device_authorization_endpoint = 10; + */ + java.lang.String getDeviceAuthorizationEndpoint(); + /** + *
+     * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+     * 
+ * + * string device_authorization_endpoint = 10; + */ + com.google.protobuf.ByteString + getDeviceAuthorizationEndpointBytes(); } /** *
@@ -713,6 +731,7 @@ private OAuth2MetadataResponse() {
       jwksUri_ = "";
       codeChallengeMethodsSupported_ = com.google.protobuf.LazyStringArrayList.EMPTY;
       grantTypesSupported_ = com.google.protobuf.LazyStringArrayList.EMPTY;
+      deviceAuthorizationEndpoint_ = "";
     }
 
     @java.lang.Override
@@ -808,6 +827,12 @@ private OAuth2MetadataResponse(
               grantTypesSupported_.add(s);
               break;
             }
+            case 82: {
+              java.lang.String s = input.readStringRequireUtf8();
+
+              deviceAuthorizationEndpoint_ = s;
+              break;
+            }
             default: {
               if (!parseUnknownField(
                   input, unknownFields, extensionRegistry, tag)) {
@@ -1259,6 +1284,48 @@ public java.lang.String getGrantTypesSupported(int index) {
       return grantTypesSupported_.getByteString(index);
     }
 
+    public static final int DEVICE_AUTHORIZATION_ENDPOINT_FIELD_NUMBER = 10;
+    private volatile java.lang.Object deviceAuthorizationEndpoint_;
+    /**
+     * 
+     * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+     * 
+ * + * string device_authorization_endpoint = 10; + */ + public java.lang.String getDeviceAuthorizationEndpoint() { + java.lang.Object ref = deviceAuthorizationEndpoint_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + deviceAuthorizationEndpoint_ = s; + return s; + } + } + /** + *
+     * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+     * 
+ * + * string device_authorization_endpoint = 10; + */ + public com.google.protobuf.ByteString + getDeviceAuthorizationEndpointBytes() { + java.lang.Object ref = deviceAuthorizationEndpoint_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + deviceAuthorizationEndpoint_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -1300,6 +1367,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) for (int i = 0; i < grantTypesSupported_.size(); i++) { com.google.protobuf.GeneratedMessageV3.writeString(output, 9, grantTypesSupported_.getRaw(i)); } + if (!getDeviceAuthorizationEndpointBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 10, deviceAuthorizationEndpoint_); + } unknownFields.writeTo(output); } @@ -1361,6 +1431,9 @@ public int getSerializedSize() { size += dataSize; size += 1 * getGrantTypesSupportedList().size(); } + if (!getDeviceAuthorizationEndpointBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(10, deviceAuthorizationEndpoint_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -1394,6 +1467,8 @@ public boolean equals(final java.lang.Object obj) { .equals(other.getCodeChallengeMethodsSupportedList())) return false; if (!getGrantTypesSupportedList() .equals(other.getGrantTypesSupportedList())) return false; + if (!getDeviceAuthorizationEndpoint() + .equals(other.getDeviceAuthorizationEndpoint())) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -1433,6 +1508,8 @@ public int hashCode() { hash = (37 * hash) + GRANT_TYPES_SUPPORTED_FIELD_NUMBER; hash = (53 * hash) + getGrantTypesSupportedList().hashCode(); } + hash = (37 * hash) + DEVICE_AUTHORIZATION_ENDPOINT_FIELD_NUMBER; + hash = (53 * hash) + getDeviceAuthorizationEndpoint().hashCode(); hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -1589,6 +1666,8 @@ public Builder clear() { bitField0_ = (bitField0_ & ~0x00000080); grantTypesSupported_ = com.google.protobuf.LazyStringArrayList.EMPTY; bitField0_ = (bitField0_ & ~0x00000100); + deviceAuthorizationEndpoint_ = ""; + return this; } @@ -1646,6 +1725,7 @@ public flyteidl.service.Auth.OAuth2MetadataResponse buildPartial() { bitField0_ = (bitField0_ & ~0x00000100); } result.grantTypesSupported_ = grantTypesSupported_; + result.deviceAuthorizationEndpoint_ = deviceAuthorizationEndpoint_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -1761,6 +1841,10 @@ public Builder mergeFrom(flyteidl.service.Auth.OAuth2MetadataResponse other) { } onChanged(); } + if (!other.getDeviceAuthorizationEndpoint().isEmpty()) { + deviceAuthorizationEndpoint_ = other.deviceAuthorizationEndpoint_; + onChanged(); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -2820,6 +2904,95 @@ public Builder addGrantTypesSupportedBytes( onChanged(); return this; } + + private java.lang.Object deviceAuthorizationEndpoint_ = ""; + /** + *
+       * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+       * 
+ * + * string device_authorization_endpoint = 10; + */ + public java.lang.String getDeviceAuthorizationEndpoint() { + java.lang.Object ref = deviceAuthorizationEndpoint_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + deviceAuthorizationEndpoint_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + *
+       * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+       * 
+ * + * string device_authorization_endpoint = 10; + */ + public com.google.protobuf.ByteString + getDeviceAuthorizationEndpointBytes() { + java.lang.Object ref = deviceAuthorizationEndpoint_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + deviceAuthorizationEndpoint_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + *
+       * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+       * 
+ * + * string device_authorization_endpoint = 10; + */ + public Builder setDeviceAuthorizationEndpoint( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + deviceAuthorizationEndpoint_ = value; + onChanged(); + return this; + } + /** + *
+       * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+       * 
+ * + * string device_authorization_endpoint = 10; + */ + public Builder clearDeviceAuthorizationEndpoint() { + + deviceAuthorizationEndpoint_ = getDefaultInstance().getDeviceAuthorizationEndpoint(); + onChanged(); + return this; + } + /** + *
+       * URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]
+       * 
+ * + * string device_authorization_endpoint = 10; + */ + public Builder setDeviceAuthorizationEndpointBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + deviceAuthorizationEndpoint_ = value; + onChanged(); + return this; + } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { @@ -4717,7 +4890,7 @@ public flyteidl.service.Auth.PublicClientAuthConfigResponse getDefaultInstanceFo "\n\033flyteidl/service/auth.proto\022\020flyteidl." + "service\032\034google/api/annotations.proto\032,p" + "rotoc-gen-swagger/options/annotations.pr" + - "oto\"\027\n\025OAuth2MetadataRequest\"\246\002\n\026OAuth2M" + + "oto\"\027\n\025OAuth2MetadataRequest\"\315\002\n\026OAuth2M" + "etadataResponse\022\016\n\006issuer\030\001 \001(\t\022\036\n\026autho" + "rization_endpoint\030\002 \001(\t\022\026\n\016token_endpoin" + "t\030\003 \001(\t\022 \n\030response_types_supported\030\004 \003(" + @@ -4725,26 +4898,27 @@ public flyteidl.service.Auth.PublicClientAuthConfigResponse getDefaultInstanceFo "point_auth_methods_supported\030\006 \003(\t\022\020\n\010jw" + "ks_uri\030\007 \001(\t\022(\n code_challenge_methods_s" + "upported\030\010 \003(\t\022\035\n\025grant_types_supported\030" + - "\t \003(\t\"\037\n\035PublicClientAuthConfigRequest\"\234" + - "\001\n\036PublicClientAuthConfigResponse\022\021\n\tcli" + - "ent_id\030\001 \001(\t\022\024\n\014redirect_uri\030\002 \001(\t\022\016\n\006sc" + - "opes\030\003 \003(\t\022\"\n\032authorization_metadata_key" + - "\030\004 \001(\t\022\035\n\025service_http_endpoint\030\005 \001(\t2\374\003" + - "\n\023AuthMetadataService\022\365\001\n\021GetOAuth2Metad" + - "ata\022\'.flyteidl.service.OAuth2MetadataReq" + - "uest\032(.flyteidl.service.OAuth2MetadataRe" + - "sponse\"\214\001\202\323\344\223\002)\022\'/.well-known/oauth-auth" + - "orization-server\222AZ\032XRetrieves OAuth2 au" + - "thorization server metadata. This endpoi" + - "nt is anonymously accessible.\022\354\001\n\025GetPub" + - "licClientConfig\022/.flyteidl.service.Publi" + - "cClientAuthConfigRequest\0320.flyteidl.serv" + - "ice.PublicClientAuthConfigResponse\"p\202\323\344\223" + - "\002\031\022\027/config/v1/flyte_client\222AN\032LRetrieve" + - "s public flyte client info. This endpoin" + - "t is anonymously accessible.B9Z7github.c" + - "om/flyteorg/flyteidl/gen/pb-go/flyteidl/" + - "serviceb\006proto3" + "\t \003(\t\022%\n\035device_authorization_endpoint\030\n" + + " \001(\t\"\037\n\035PublicClientAuthConfigRequest\"\234\001" + + "\n\036PublicClientAuthConfigResponse\022\021\n\tclie" + + "nt_id\030\001 \001(\t\022\024\n\014redirect_uri\030\002 \001(\t\022\016\n\006sco" + + "pes\030\003 \003(\t\022\"\n\032authorization_metadata_key\030" + + "\004 \001(\t\022\035\n\025service_http_endpoint\030\005 \001(\t2\374\003\n" + + "\023AuthMetadataService\022\365\001\n\021GetOAuth2Metada" + + "ta\022\'.flyteidl.service.OAuth2MetadataRequ" + + "est\032(.flyteidl.service.OAuth2MetadataRes" + + "ponse\"\214\001\202\323\344\223\002)\022\'/.well-known/oauth-autho" + + "rization-server\222AZ\032XRetrieves OAuth2 aut" + + "horization server metadata. This endpoin" + + "t is anonymously accessible.\022\354\001\n\025GetPubl" + + "icClientConfig\022/.flyteidl.service.Public" + + "ClientAuthConfigRequest\0320.flyteidl.servi" + + "ce.PublicClientAuthConfigResponse\"p\202\323\344\223\002" + + "\031\022\027/config/v1/flyte_client\222AN\032LRetrieves" + + " public flyte client info. This endpoint" + + " is anonymously accessible.B9Z7github.co" + + "m/flyteorg/flyteidl/gen/pb-go/flyteidl/s" + + "erviceb\006proto3" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { @@ -4771,7 +4945,7 @@ public com.google.protobuf.ExtensionRegistry assignDescriptors( internal_static_flyteidl_service_OAuth2MetadataResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_flyteidl_service_OAuth2MetadataResponse_descriptor, - new java.lang.String[] { "Issuer", "AuthorizationEndpoint", "TokenEndpoint", "ResponseTypesSupported", "ScopesSupported", "TokenEndpointAuthMethodsSupported", "JwksUri", "CodeChallengeMethodsSupported", "GrantTypesSupported", }); + new java.lang.String[] { "Issuer", "AuthorizationEndpoint", "TokenEndpoint", "ResponseTypesSupported", "ScopesSupported", "TokenEndpointAuthMethodsSupported", "JwksUri", "CodeChallengeMethodsSupported", "GrantTypesSupported", "DeviceAuthorizationEndpoint", }); internal_static_flyteidl_service_PublicClientAuthConfigRequest_descriptor = getDescriptor().getMessageTypes().get(2); internal_static_flyteidl_service_PublicClientAuthConfigRequest_fieldAccessorTable = new diff --git a/gen/pb-js/flyteidl.d.ts b/gen/pb-js/flyteidl.d.ts index c14c5dba3f..4f41ecbc0b 100644 --- a/gen/pb-js/flyteidl.d.ts +++ b/gen/pb-js/flyteidl.d.ts @@ -16805,6 +16805,9 @@ export namespace flyteidl { /** OAuth2MetadataResponse grantTypesSupported */ grantTypesSupported?: (string[]|null); + + /** OAuth2MetadataResponse deviceAuthorizationEndpoint */ + deviceAuthorizationEndpoint?: (string|null); } /** Represents a OAuth2MetadataResponse. */ @@ -16843,6 +16846,9 @@ export namespace flyteidl { /** OAuth2MetadataResponse grantTypesSupported. */ public grantTypesSupported: string[]; + /** OAuth2MetadataResponse deviceAuthorizationEndpoint. */ + public deviceAuthorizationEndpoint: string; + /** * Creates a new OAuth2MetadataResponse instance using the specified properties. * @param [properties] Properties to set diff --git a/gen/pb-js/flyteidl.js b/gen/pb-js/flyteidl.js index 7875bf3eb3..af9ddb662b 100644 --- a/gen/pb-js/flyteidl.js +++ b/gen/pb-js/flyteidl.js @@ -39141,6 +39141,7 @@ export const flyteidl = $root.flyteidl = (() => { * @property {string|null} [jwksUri] OAuth2MetadataResponse jwksUri * @property {Array.|null} [codeChallengeMethodsSupported] OAuth2MetadataResponse codeChallengeMethodsSupported * @property {Array.|null} [grantTypesSupported] OAuth2MetadataResponse grantTypesSupported + * @property {string|null} [deviceAuthorizationEndpoint] OAuth2MetadataResponse deviceAuthorizationEndpoint */ /** @@ -39235,6 +39236,14 @@ export const flyteidl = $root.flyteidl = (() => { */ OAuth2MetadataResponse.prototype.grantTypesSupported = $util.emptyArray; + /** + * OAuth2MetadataResponse deviceAuthorizationEndpoint. + * @member {string} deviceAuthorizationEndpoint + * @memberof flyteidl.service.OAuth2MetadataResponse + * @instance + */ + OAuth2MetadataResponse.prototype.deviceAuthorizationEndpoint = ""; + /** * Creates a new OAuth2MetadataResponse instance using the specified properties. * @function create @@ -39282,6 +39291,8 @@ export const flyteidl = $root.flyteidl = (() => { if (message.grantTypesSupported != null && message.grantTypesSupported.length) for (let i = 0; i < message.grantTypesSupported.length; ++i) writer.uint32(/* id 9, wireType 2 =*/74).string(message.grantTypesSupported[i]); + if (message.deviceAuthorizationEndpoint != null && message.hasOwnProperty("deviceAuthorizationEndpoint")) + writer.uint32(/* id 10, wireType 2 =*/82).string(message.deviceAuthorizationEndpoint); return writer; }; @@ -39340,6 +39351,9 @@ export const flyteidl = $root.flyteidl = (() => { message.grantTypesSupported = []; message.grantTypesSupported.push(reader.string()); break; + case 10: + message.deviceAuthorizationEndpoint = reader.string(); + break; default: reader.skipType(tag & 7); break; @@ -39406,6 +39420,9 @@ export const flyteidl = $root.flyteidl = (() => { if (!$util.isString(message.grantTypesSupported[i])) return "grantTypesSupported: string[] expected"; } + if (message.deviceAuthorizationEndpoint != null && message.hasOwnProperty("deviceAuthorizationEndpoint")) + if (!$util.isString(message.deviceAuthorizationEndpoint)) + return "deviceAuthorizationEndpoint: string expected"; return null; }; diff --git a/gen/pb_python/flyteidl/service/auth_pb2.py b/gen/pb_python/flyteidl/service/auth_pb2.py index 6b72bcaf85..208b64abad 100644 --- a/gen/pb_python/flyteidl/service/auth_pb2.py +++ b/gen/pb_python/flyteidl/service/auth_pb2.py @@ -22,7 +22,7 @@ package='flyteidl.service', syntax='proto3', serialized_options=_b('Z7github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/service'), - serialized_pb=_b('\n\x1b\x66lyteidl/service/auth.proto\x12\x10\x66lyteidl.service\x1a\x1cgoogle/api/annotations.proto\x1a,protoc-gen-swagger/options/annotations.proto\"\x17\n\x15OAuth2MetadataRequest\"\xa6\x02\n\x16OAuth2MetadataResponse\x12\x0e\n\x06issuer\x18\x01 \x01(\t\x12\x1e\n\x16\x61uthorization_endpoint\x18\x02 \x01(\t\x12\x16\n\x0etoken_endpoint\x18\x03 \x01(\t\x12 \n\x18response_types_supported\x18\x04 \x03(\t\x12\x18\n\x10scopes_supported\x18\x05 \x03(\t\x12-\n%token_endpoint_auth_methods_supported\x18\x06 \x03(\t\x12\x10\n\x08jwks_uri\x18\x07 \x01(\t\x12(\n code_challenge_methods_supported\x18\x08 \x03(\t\x12\x1d\n\x15grant_types_supported\x18\t \x03(\t\"\x1f\n\x1dPublicClientAuthConfigRequest\"\x9c\x01\n\x1ePublicClientAuthConfigResponse\x12\x11\n\tclient_id\x18\x01 \x01(\t\x12\x14\n\x0credirect_uri\x18\x02 \x01(\t\x12\x0e\n\x06scopes\x18\x03 \x03(\t\x12\"\n\x1a\x61uthorization_metadata_key\x18\x04 \x01(\t\x12\x1d\n\x15service_http_endpoint\x18\x05 \x01(\t2\xfc\x03\n\x13\x41uthMetadataService\x12\xf5\x01\n\x11GetOAuth2Metadata\x12\'.flyteidl.service.OAuth2MetadataRequest\x1a(.flyteidl.service.OAuth2MetadataResponse\"\x8c\x01\x82\xd3\xe4\x93\x02)\x12\'/.well-known/oauth-authorization-server\x92\x41Z\x1aXRetrieves OAuth2 authorization server metadata. This endpoint is anonymously accessible.\x12\xec\x01\n\x15GetPublicClientConfig\x12/.flyteidl.service.PublicClientAuthConfigRequest\x1a\x30.flyteidl.service.PublicClientAuthConfigResponse\"p\x82\xd3\xe4\x93\x02\x19\x12\x17/config/v1/flyte_client\x92\x41N\x1aLRetrieves public flyte client info. This endpoint is anonymously accessible.B9Z7github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/serviceb\x06proto3') + serialized_pb=_b('\n\x1b\x66lyteidl/service/auth.proto\x12\x10\x66lyteidl.service\x1a\x1cgoogle/api/annotations.proto\x1a,protoc-gen-swagger/options/annotations.proto\"\x17\n\x15OAuth2MetadataRequest\"\xcd\x02\n\x16OAuth2MetadataResponse\x12\x0e\n\x06issuer\x18\x01 \x01(\t\x12\x1e\n\x16\x61uthorization_endpoint\x18\x02 \x01(\t\x12\x16\n\x0etoken_endpoint\x18\x03 \x01(\t\x12 \n\x18response_types_supported\x18\x04 \x03(\t\x12\x18\n\x10scopes_supported\x18\x05 \x03(\t\x12-\n%token_endpoint_auth_methods_supported\x18\x06 \x03(\t\x12\x10\n\x08jwks_uri\x18\x07 \x01(\t\x12(\n code_challenge_methods_supported\x18\x08 \x03(\t\x12\x1d\n\x15grant_types_supported\x18\t \x03(\t\x12%\n\x1d\x64\x65vice_authorization_endpoint\x18\n \x01(\t\"\x1f\n\x1dPublicClientAuthConfigRequest\"\x9c\x01\n\x1ePublicClientAuthConfigResponse\x12\x11\n\tclient_id\x18\x01 \x01(\t\x12\x14\n\x0credirect_uri\x18\x02 \x01(\t\x12\x0e\n\x06scopes\x18\x03 \x03(\t\x12\"\n\x1a\x61uthorization_metadata_key\x18\x04 \x01(\t\x12\x1d\n\x15service_http_endpoint\x18\x05 \x01(\t2\xfc\x03\n\x13\x41uthMetadataService\x12\xf5\x01\n\x11GetOAuth2Metadata\x12\'.flyteidl.service.OAuth2MetadataRequest\x1a(.flyteidl.service.OAuth2MetadataResponse\"\x8c\x01\x82\xd3\xe4\x93\x02)\x12\'/.well-known/oauth-authorization-server\x92\x41Z\x1aXRetrieves OAuth2 authorization server metadata. This endpoint is anonymously accessible.\x12\xec\x01\n\x15GetPublicClientConfig\x12/.flyteidl.service.PublicClientAuthConfigRequest\x1a\x30.flyteidl.service.PublicClientAuthConfigResponse\"p\x82\xd3\xe4\x93\x02\x19\x12\x17/config/v1/flyte_client\x92\x41N\x1aLRetrieves public flyte client info. This endpoint is anonymously accessible.B9Z7github.com/flyteorg/flyteidl/gen/pb-go/flyteidl/serviceb\x06proto3') , dependencies=[google_dot_api_dot_annotations__pb2.DESCRIPTOR,protoc__gen__swagger_dot_options_dot_annotations__pb2.DESCRIPTOR,]) @@ -123,6 +123,13 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='device_authorization_endpoint', full_name='flyteidl.service.OAuth2MetadataResponse.device_authorization_endpoint', index=9, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), ], extensions=[ ], @@ -136,7 +143,7 @@ oneofs=[ ], serialized_start=151, - serialized_end=445, + serialized_end=484, ) @@ -159,8 +166,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=447, - serialized_end=478, + serialized_start=486, + serialized_end=517, ) @@ -218,8 +225,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=481, - serialized_end=637, + serialized_start=520, + serialized_end=676, ) DESCRIPTOR.message_types_by_name['OAuth2MetadataRequest'] = _OAUTH2METADATAREQUEST @@ -265,8 +272,8 @@ file=DESCRIPTOR, index=0, serialized_options=None, - serialized_start=640, - serialized_end=1148, + serialized_start=679, + serialized_end=1187, methods=[ _descriptor.MethodDescriptor( name='GetOAuth2Metadata', diff --git a/go.mod b/go.mod index 8823719749..d84d7370c6 100644 --- a/go.mod +++ b/go.mod @@ -16,10 +16,10 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 + golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013 google.golang.org/api v0.38.0 google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 google.golang.org/grpc v1.35.0 - k8s.io/api v0.20.2 k8s.io/apimachinery v0.20.2 ) diff --git a/go.sum b/go.sum index 877080b807..60e1c96bb3 100644 --- a/go.sum +++ b/go.sum @@ -217,7 +217,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -264,7 +263,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -352,7 +350,6 @@ github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= @@ -406,10 +403,8 @@ github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxd github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -958,7 +953,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -989,7 +983,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.0.0-20210217171935-8e2decd92398/go.mod h1:60tmSUpHxGPFerNHbo/ayI2lKxvtrhbxFyXuEIWJd78= -k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw= k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= k8s.io/apimachinery v0.0.0-20210217011835-527a61b4dffe/go.mod h1:Z7ps/g0rjlTeMstYrMOUttJfT2Gg34DEaG/f2PYLCWY= k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg= @@ -1007,9 +1000,7 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/protos/docs/service/service.rst b/protos/docs/service/service.rst index 398a48d2b2..bd36c2f67d 100644 --- a/protos/docs/service/service.rst +++ b/protos/docs/service/service.rst @@ -138,6 +138,7 @@ as defined in https://tools.ietf.org/html/rfc8414 "jwks_uri", ":ref:`ref_string`", "", "URL of the authorization server's JWK Set [JWK] document. The referenced document contains the signing key(s) the client uses to validate signatures from the authorization server." "code_challenge_methods_supported", ":ref:`ref_string`", "repeated", "JSON array containing a list of Proof Key for Code Exchange (PKCE) [RFC7636] code challenge methods supported by this authorization server." "grant_types_supported", ":ref:`ref_string`", "repeated", "JSON array containing a list of the OAuth 2.0 grant type values that this authorization server supports." + "device_authorization_endpoint", ":ref:`ref_string`", "", "URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628]" diff --git a/protos/flyteidl/service/auth.proto b/protos/flyteidl/service/auth.proto index defed6ecf5..c1fc3e7113 100644 --- a/protos/flyteidl/service/auth.proto +++ b/protos/flyteidl/service/auth.proto @@ -41,6 +41,9 @@ message OAuth2MetadataResponse { // JSON array containing a list of the OAuth 2.0 grant type values that this authorization server supports. repeated string grant_types_supported = 9; + + // URL of the authorization server's device authorization endpoint, as defined in Section 3.1 of [RFC8628] + string device_authorization_endpoint = 10; } message PublicClientAuthConfigRequest {}