From b65e18fe7cfdeaa47bf4e24bf9ccb17af75a3497 Mon Sep 17 00:00:00 2001 From: Nikos Date: Fri, 9 Feb 2024 17:01:36 +0200 Subject: [PATCH 1/8] fix: add rfc8628 providers to registry --- driver/config/provider.go | 20 ++++ driver/config/provider_test.go | 8 ++ driver/registry_base.go | 13 ++ fositex/config.go | 13 ++ go.mod | 54 +++++---- go.sum | 177 ++++++++++------------------ internal/mock/config_cookie.go | 5 +- jwk/registry_mock_test.go | 6 +- oauth2/oauth2_provider_mock_test.go | 46 +++++++- oauth2/registry.go | 2 + spec/config.json | 42 +++++++ 11 files changed, 235 insertions(+), 151 deletions(-) diff --git a/driver/config/provider.go b/driver/config/provider.go index ba1869498fe..84280440418 100644 --- a/driver/config/provider.go +++ b/driver/config/provider.go @@ -49,6 +49,7 @@ const ( KeyOIDCDiscoverySupportedClaims = "webfinger.oidc_discovery.supported_claims" KeyOIDCDiscoverySupportedScope = "webfinger.oidc_discovery.supported_scope" KeyOIDCDiscoveryUserinfoEndpoint = "webfinger.oidc_discovery.userinfo_url" + KeyOAuth2DeviceAuthorisationURL = "webfinger.oidc_discovery.device_authorization_url" KeySubjectTypesSupported = "oidc.subject_identifiers.supported_types" KeyDefaultClientScope = "oidc.dynamic_client_registration.default_scope" KeyDSN = "dsn" @@ -72,6 +73,7 @@ const ( KeyVerifiableCredentialsNonceLifespan = "ttl.vc_nonce" // #nosec G101 KeyIDTokenLifespan = "ttl.id_token" // #nosec G101 KeyAuthCodeLifespan = "ttl.auth_code" + KeyDeviceAndUserCodeLifespan = "ttl.device_user_code" KeyScopeStrategy = "strategies.scope" KeyGetCookieSecrets = "secrets.cookie" KeyGetSystemSecret = "secrets.system" @@ -81,6 +83,7 @@ const ( KeyLogoutURL = "urls.logout" KeyConsentURL = "urls.consent" KeyErrorURL = "urls.error" + KeyDeviceVerificationURL = "urls.device_verification" KeyPublicURL = "urls.self.public" KeyAdminURL = "urls.self.admin" KeyIssuerURL = "urls.self.issuer" @@ -92,6 +95,7 @@ const ( KeyDBIgnoreUnknownTableColumns = "db.ignore_unknown_table_columns" KeySubjectIdentifierAlgorithmSalt = "oidc.subject_identifiers.pairwise.salt" KeyPublicAllowDynamicRegistration = "oidc.dynamic_client_registration.enabled" + KeyDeviceAuthTokenPollingInterval = "oauth2.device_authorization.token_polling_interval" // #nosec G101 KeyPKCEEnforced = "oauth2.pkce.enforced" KeyPKCEEnforcedForPublicClients = "oauth2.pkce.enforced_for_public_clients" KeyLogLevel = "log.level" @@ -372,6 +376,14 @@ func (p *DefaultProvider) fallbackURL(ctx context.Context, path string, host str return &u } +func (p *DefaultProvider) GetDeviceAndUserCodeLifespan(ctx context.Context) time.Duration { + return p.p.DurationF(KeyDeviceAndUserCodeLifespan, time.Minute*15) +} + +func (p *DefaultProvider) GetDeviceAuthTokenPollingInterval(ctx context.Context) time.Duration { + return p.p.DurationF(KeyDeviceAuthTokenPollingInterval, time.Second*5) +} + func (p *DefaultProvider) LoginURL(ctx context.Context) *url.URL { return urlRoot(p.getProvider(ctx).URIF(KeyLoginURL, p.publicFallbackURL(ctx, "oauth2/fallbacks/login"))) } @@ -392,6 +404,10 @@ func (p *DefaultProvider) ErrorURL(ctx context.Context) *url.URL { return urlRoot(p.getProvider(ctx).RequestURIF(KeyErrorURL, p.publicFallbackURL(ctx, "oauth2/fallbacks/error"))) } +func (p *DefaultProvider) DeviceVerificationURL(ctx context.Context) *url.URL { + return urlRoot(p.getProvider(ctx).URIF(KeyDeviceVerificationURL, p.publicFallbackURL(ctx, "oauth2/fallbacks/device"))) +} + func (p *DefaultProvider) PublicURL(ctx context.Context) *url.URL { return urlRoot(p.getProvider(ctx).RequestURIF(KeyPublicURL, p.IssuerURL(ctx))) } @@ -449,6 +465,10 @@ func (p *DefaultProvider) OAuth2AuthURL(ctx context.Context) *url.URL { return p.getProvider(ctx).RequestURIF(KeyOAuth2AuthURL, urlx.AppendPaths(p.PublicURL(ctx), "/oauth2/auth")) } +func (p *DefaultProvider) OAuth2DeviceAuthorisationURL(ctx context.Context) *url.URL { + return p.getProvider(ctx).RequestURIF(KeyOAuth2DeviceAuthorisationURL, urlx.AppendPaths(p.PublicURL(ctx), "/oauth2/device/auth")) +} + func (p *DefaultProvider) JWKSURL(ctx context.Context) *url.URL { return p.getProvider(ctx).RequestURIF(KeyJWKSURL, urlx.AppendPaths(p.IssuerURL(ctx), "/.well-known/jwks.json")) } diff --git a/driver/config/provider_test.go b/driver/config/provider_test.go index 8e5c44a9e2e..a0da7ace9bf 100644 --- a/driver/config/provider_test.go +++ b/driver/config/provider_test.go @@ -279,6 +279,7 @@ func TestViperProviderValidates(t *testing.T) { // webfinger assert.Equal(t, []string{"hydra.openid.id-token", "hydra.jwt.access-token"}, c.WellKnownKeys(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://example.com"), c.OAuth2ClientRegistrationURL(ctx)) + assert.Equal(t, urlx.ParseOrPanic("https://example.com/device_authorization"), c.OAuth2DeviceAuthorisationURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://example.com/jwks.json"), c.JWKSURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://example.com/auth"), c.OAuth2AuthURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://example.com/token"), c.OAuth2TokenURL(ctx)) @@ -297,6 +298,7 @@ func TestViperProviderValidates(t *testing.T) { assert.Equal(t, urlx.ParseOrPanic("https://admin/"), c.AdminURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://login/"), c.LoginURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://consent/"), c.ConsentURL(ctx)) + assert.Equal(t, urlx.ParseOrPanic("https://device/"), c.DeviceVerificationURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://logout/"), c.LogoutURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://error/"), c.ErrorURL(ctx)) assert.Equal(t, urlx.ParseOrPanic("https://post_logout/"), c.LogoutRedirectURL(ctx)) @@ -314,12 +316,14 @@ func TestViperProviderValidates(t *testing.T) { assert.Equal(t, 2*time.Hour, c.GetRefreshTokenLifespan(ctx)) assert.Equal(t, 2*time.Hour, c.GetIDTokenLifespan(ctx)) assert.Equal(t, 2*time.Hour, c.GetAuthorizeCodeLifespan(ctx)) + assert.Equal(t, 2*time.Hour, c.GetDeviceAndUserCodeLifespan(ctx)) // oauth2 assert.Equal(t, true, c.GetSendDebugMessagesToClients(ctx)) assert.Equal(t, 20, c.GetBCryptCost(ctx)) assert.Equal(t, true, c.GetEnforcePKCE(ctx)) assert.Equal(t, true, c.GetEnforcePKCEForPublicClients(ctx)) + assert.Equal(t, 2*time.Hour, c.GetDeviceAuthTokenPollingInterval(ctx)) // secrets secret, err := c.GetGlobalSecret(ctx) @@ -388,16 +392,20 @@ func TestLoginConsentURL(t *testing.T) { p := MustNew(context.Background(), l) p.MustSet(ctx, KeyLoginURL, "http://localhost:8080/oauth/login") p.MustSet(ctx, KeyConsentURL, "http://localhost:8080/oauth/consent") + p.MustSet(ctx, KeyDeviceVerificationURL, "http://localhost:8080/oauth/device") assert.Equal(t, "http://localhost:8080/oauth/login", p.LoginURL(ctx).String()) assert.Equal(t, "http://localhost:8080/oauth/consent", p.ConsentURL(ctx).String()) + assert.Equal(t, "http://localhost:8080/oauth/device", p.DeviceVerificationURL(ctx).String()) p2 := MustNew(context.Background(), l) p2.MustSet(ctx, KeyLoginURL, "http://localhost:3000/#/oauth/login") p2.MustSet(ctx, KeyConsentURL, "http://localhost:3000/#/oauth/consent") + p2.MustSet(ctx, KeyDeviceVerificationURL, "http://localhost:3000/#/oauth/device") assert.Equal(t, "http://localhost:3000/#/oauth/login", p2.LoginURL(ctx).String()) assert.Equal(t, "http://localhost:3000/#/oauth/consent", p2.ConsentURL(ctx).String()) + assert.Equal(t, "http://localhost:3000/#/oauth/device", p2.DeviceVerificationURL(ctx).String()) } func TestInfinitRefreshTokenTTL(t *testing.T) { diff --git a/driver/registry_base.go b/driver/registry_base.go index a541e06ce19..cf05892185b 100644 --- a/driver/registry_base.go +++ b/driver/registry_base.go @@ -21,6 +21,7 @@ import ( "github.com/ory/fosite/compose" foauth2 "github.com/ory/fosite/handler/oauth2" "github.com/ory/fosite/handler/openid" + "github.com/ory/fosite/handler/rfc8628" "github.com/ory/herodot" "github.com/ory/hydra/v2/aead" "github.com/ory/hydra/v2/client" @@ -87,6 +88,7 @@ type RegistryBase struct { oidcs jwk.JWTSigner ats jwk.JWTSigner hmacs *foauth2.HMACSHAStrategy + deviceHmac rfc8628.RFC8628CodeStrategy fc *fositex.Config publicCORS *cors.Cors kratos kratos.Client @@ -409,6 +411,15 @@ func (m *RegistryBase) OAuth2HMACStrategy() *foauth2.HMACSHAStrategy { return m.hmacs } +func (m *RegistryBase) RFC8628HMACStrategy() rfc8628.RFC8628CodeStrategy { + if m.deviceHmac != nil { + return m.deviceHmac + } + + m.deviceHmac = compose.NewDeviceStrategy(m.OAuth2Config()) + return m.deviceHmac +} + func (m *RegistryBase) OAuth2Config() *fositex.Config { if m.fc != nil { return m.fc @@ -435,6 +446,7 @@ func (m *RegistryBase) OAuth2ProviderConfig() fosite.Configurator { conf := m.OAuth2Config() hmacAtStrategy := m.OAuth2HMACStrategy() + deviceHmacAtStrategy := m.RFC8628HMACStrategy() oidcSigner := m.OpenIDJWTStrategy() atSigner := m.AccessTokenJWTStrategy() jwtAtStrategy := &foauth2.DefaultJWTStrategy{ @@ -449,6 +461,7 @@ func (m *RegistryBase) OAuth2ProviderConfig() fosite.Configurator { HMACSHAStrategy: hmacAtStrategy, Config: conf, }), + RFC8628CodeStrategy: deviceHmacAtStrategy, OpenIDConnectTokenStrategy: &openid.DefaultStrategy{ Config: conf, Signer: oidcSigner, diff --git a/fositex/config.go b/fositex/config.go index 4377efb1f6d..7c2018971f6 100644 --- a/fositex/config.go +++ b/fositex/config.go @@ -42,6 +42,7 @@ type Config struct { tokenEndpointHandlers fosite.TokenEndpointHandlers tokenIntrospectionHandlers fosite.TokenIntrospectionHandlers revocationHandlers fosite.RevocationHandlers + deviceEndpointHandlers fosite.DeviceEndpointHandlers *config.DefaultProvider } @@ -61,6 +62,7 @@ var defaultFactories = []Factory{ compose.OAuth2PKCEFactory, compose.RFC7523AssertionGrantFactory, compose.OIDCUserinfoVerifiableCredentialFactory, + compose.RFC8628DeviceFactory, } func NewConfig(deps configDependencies) *Config { @@ -87,6 +89,9 @@ func (c *Config) LoadDefaultHandlers(strategy interface{}) { if rh, ok := res.(fosite.RevocationHandler); ok { c.revocationHandlers.Append(rh) } + if dh, ok := res.(fosite.DeviceEndpointHandler); ok { + c.deviceEndpointHandlers.Append(dh) + } } } @@ -114,6 +119,10 @@ func (c *Config) GetRevocationHandlers(context.Context) fosite.RevocationHandler return c.revocationHandlers } +func (c *Config) GetDeviceEndpointHandlers(ctx context.Context) fosite.DeviceEndpointHandlers { + return c.deviceEndpointHandlers +} + func (c *Config) GetGrantTypeJWTBearerCanSkipClientAuth(context.Context) bool { return false } @@ -206,3 +215,7 @@ func (c *Config) GetTokenURLs(ctx context.Context) []string { urlx.AppendPaths(c.deps.Config().PublicURL(ctx), oauth2.TokenPath).String(), }) } + +func (c *Config) GetDeviceVerificationURL(ctx context.Context) string { + return urlx.AppendPaths(c.deps.Config().PublicURL(ctx), oauth2.DeviceAuthPath).String() +} diff --git a/go.mod b/go.mod index 947f7f0c9d7..9778bc460fa 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/ory/hydra/v2 go 1.21 -toolchain go1.21.0 +toolchain go1.21.4 replace ( github.com/jackc/pcmock => github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 @@ -25,10 +25,10 @@ require ( github.com/gofrs/uuid v4.4.0+incompatible github.com/golang-jwt/jwt/v5 v5.0.0 github.com/golang/mock v1.6.0 - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.6.0 github.com/gorilla/securecookie v1.1.2 github.com/gorilla/sessions v1.2.2 - github.com/hashicorp/go-retryablehttp v0.7.4 + github.com/hashicorp/go-retryablehttp v0.7.5 github.com/jackc/pgx/v4 v4.18.1 github.com/julienschmidt/httprouter v1.3.0 github.com/luna-duclos/instrumentedsql v1.1.3 @@ -44,14 +44,14 @@ require ( github.com/ory/hydra-client-go/v2 v2.1.1 github.com/ory/jsonschema/v3 v3.0.8 github.com/ory/kratos-client-go v0.13.1 - github.com/ory/x v0.0.607 + github.com/ory/x v0.0.613 github.com/pborman/uuid v1.2.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 github.com/rs/cors v1.9.0 github.com/sawadashota/encrypta v0.0.3 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 github.com/tidwall/gjson v1.17.0 @@ -66,9 +66,9 @@ require ( go.opentelemetry.io/otel/sdk v1.21.0 go.opentelemetry.io/otel/trace v1.21.0 go.uber.org/automaxprocs v1.5.3 - golang.org/x/crypto v0.17.0 + golang.org/x/crypto v0.18.0 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa - golang.org/x/oauth2 v0.14.0 + golang.org/x/oauth2 v0.15.0 golang.org/x/sync v0.5.0 golang.org/x/tools v0.15.0 ) @@ -96,7 +96,7 @@ require ( github.com/creasty/defaults v1.7.0 // indirect github.com/cristalhq/jwt/v4 v4.0.2 // indirect github.com/dave/jennifer v1.7.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v20.10.21+incompatible // indirect @@ -112,7 +112,7 @@ require ( github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.21.4 // indirect @@ -140,13 +140,13 @@ require ( github.com/goccy/go-yaml v1.11.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/glog v1.1.2 // indirect + github.com/golang/glog v1.2.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/pprof v0.0.0-20230808223545-4887780b67fb // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect @@ -197,25 +197,28 @@ require ( github.com/openzipkin/zipkin-go v0.4.2 // indirect github.com/ory/dockertest/v3 v3.10.0 // indirect github.com/ory/go-convenience v0.1.0 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.9 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pkg/profile v1.7.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect github.com/segmentio/backo-go v1.0.1 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect + github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect - github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/viper v1.16.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -225,23 +228,26 @@ require ( github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect go.mongodb.org/mongo-driver v1.12.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.20.0 // indirect - go.opentelemetry.io/contrib/propagators/jaeger v1.20.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.21.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 // indirect go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect go.opentelemetry.io/otel/exporters/zipkin v1.21.0 // indirect go.opentelemetry.io/otel/metric v1.21.0 // indirect go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.9.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -250,4 +256,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/ory/fosite => github.com/canonical/fosite v0.0.0-20231227112919-2c69fd81013a +replace github.com/ory/fosite => github.com/canonical/fosite v0.0.0-20240131124711-821471ea939e diff --git a/go.sum b/go.sum index 5ca296ca63c..0a0dc233ca8 100644 --- a/go.sum +++ b/go.sum @@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -14,9 +13,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -34,7 +30,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= code.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI= code.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -79,8 +74,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= -github.com/canonical/fosite v0.0.0-20231227112919-2c69fd81013a h1:GKYPzq+XqqJF3MME+sr8DExhaKyXUvPmTu8VFz6KPm8= -github.com/canonical/fosite v0.0.0-20231227112919-2c69fd81013a/go.mod h1:fkMPsnm/UjiefE9dE9CdZQGOH48TWJLIzUcdGIXg8Kk= +github.com/canonical/fosite v0.0.0-20240131124711-821471ea939e h1:tiksz1KigpLeZzucUtymyhq1XtVy8PLdi50oA8yxx1c= +github.com/canonical/fosite v0.0.0-20240131124711-821471ea939e/go.mod h1:kmWnOXEOHkGcerE/vDP5lEkKI7yJ2E8a6EFaos23xzw= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -94,8 +89,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go/v2 v2.3.5 h1:Khtm8K6fTTz/ZCWPzU9Ne3aOW9VyAnj4qIPCJgKtwK0= @@ -104,6 +97,7 @@ github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5 github.com/containerd/continuity v0.4.1/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -114,8 +108,9 @@ github.com/cristalhq/jwt/v4 v4.0.2/go.mod h1:HnYraSNKDRag1DZP92rYHyrjyQHnVEHPNqe github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= @@ -142,8 +137,6 @@ github.com/elliotchance/orderedmap v1.5.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0z github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= @@ -159,10 +152,10 @@ github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNu github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-faker/faker/v4 v4.1.1 h1:zkxj/JH/aezB4R6cTEMKU7qcVScGhlB3qRtF3D7K+rI= @@ -310,8 +303,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -337,7 +330,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= @@ -353,7 +345,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -361,7 +352,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -369,9 +359,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20230808223545-4887780b67fb h1:oqpb3Cwpc7EOml5PVGMYbSGmwNui2R7i8IW83gs4W0c= github.com/google/pprof v0.0.0-20230808223545-4887780b67fb/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= @@ -380,12 +367,10 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= @@ -394,8 +379,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U= @@ -403,10 +388,10 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3Kp github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= +github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -417,7 +402,6 @@ github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -497,7 +481,6 @@ github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g= github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -601,15 +584,17 @@ github.com/ory/jsonschema/v3 v3.0.8 h1:Ssdb3eJ4lDZ/+XnGkvQS/te0p+EkolqwTsDOCxr/F github.com/ory/jsonschema/v3 v3.0.8/go.mod h1:ZPzqjDkwd3QTnb2Z6PAS+OTvBE2x5i6m25wCGx54W/0= github.com/ory/kratos-client-go v0.13.1 h1:o+pFV9ZRMFSBa4QeNJYbJeLz036UWU4p+7yfKghK+0E= github.com/ory/kratos-client-go v0.13.1/go.mod h1:hkrFJuHSBQw+qN6Ks0faOAYhAKwtpjvhCZzsQ7g/Ufc= -github.com/ory/x v0.0.607 h1:qNP1gU6RWVtsEB04rPht+1rV2DqQhvOAN2sF+4eqVWo= -github.com/ory/x v0.0.607/go.mod h1:fCYvVVHo8wYrCwLyU8+9hFY3IRo4EZM3KI30ysDsDYY= +github.com/ory/x v0.0.613 h1:MHT0scH7hcrOkc3aH7qqYLzXVJkjhB0szWTwpD2lh8Q= +github.com/ory/x v0.0.613/go.mod h1:uH065puz8neija0neqwIN3PmXXfDsB9VbZTZ20Znoos= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= -github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= @@ -620,9 +605,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= @@ -646,6 +631,10 @@ github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sawadashota/encrypta v0.0.3 h1:NWo2S6oBzZmD/tlm6iH1eYLZA99NsFPvc33MhklME6o= github.com/sawadashota/encrypta v0.0.3/go.mod h1:W3Nja83iH22fOS8sGgKCf4rCehZqLrca1+oQbtFUFck= github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 h1:0b8DF5kR0PhRoRXDiEEdzrgBc8UqVY4JWLkQJCRsLME= @@ -671,24 +660,24 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -705,8 +694,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -762,15 +751,14 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 h1:gbhw/u49SS3gkPWiYweQNJGm/uJN5GkI/FrosxSHT7A= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1/go.mod h1:GnOaBaFQ2we3b9AGWJpsBa7v1S5RlQzlC3O7dRMxZhM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0 h1:Yty9Vs4F3D6/liF1o6FNt0PvN85h/BJJ6DQKJ3nrcM0= -go.opentelemetry.io/contrib/propagators/b3 v1.20.0/go.mod h1:On4VgbkqYL18kbJlWsa18+cMNe6rYpBnPi1ARI/BrsU= -go.opentelemetry.io/contrib/propagators/jaeger v1.20.0 h1:iVhNKkMIpzyZqxk8jkDU2n4DFTD+FbpGacvooxEvyyc= -go.opentelemetry.io/contrib/propagators/jaeger v1.20.0/go.mod h1:cpSABr0cm/AH/HhbJjn+AudBVUMgZWdfN3Gb+ZqxSZc= +go.opentelemetry.io/contrib/propagators/b3 v1.21.0 h1:uGdgDPNzwQWRwCXJgw/7h29JaRqcq9B87Iv4hJDKAZw= +go.opentelemetry.io/contrib/propagators/b3 v1.21.0/go.mod h1:D9GQXvVGT2pzyTfp1QBOnD1rzKEWzKjjwu5q2mslCUI= +go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 h1:f4beMGDKiVzg9IcX7/VuWVy+oGdjx3dNJ72YehmtY5k= +go.opentelemetry.io/contrib/propagators/jaeger v1.21.1/go.mod h1:U9jhkEl8d1LL+QXY7q3kneJWJugiN3kZJV2OWz3hkBY= go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1 h1:Qb+5A+JbIjXwO7l4HkRUhgIn4Bzz0GNS2q+qdmSx+0c= go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1/go.mod h1:G4vNCm7fRk0kjZ6pGNLo5SpLxAUvOfSrcaegnT8TPck= go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= @@ -793,12 +781,16 @@ go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lI go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -810,15 +802,13 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -843,7 +833,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -852,8 +841,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -887,9 +874,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= @@ -901,20 +885,16 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -925,7 +905,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -964,18 +943,12 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -986,14 +959,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1005,7 +977,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -1018,8 +989,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= -golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1069,14 +1040,7 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= @@ -1105,16 +1069,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -1146,19 +1106,12 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1171,10 +1124,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc/examples v0.0.0-20210304020650-930c79186c99 h1:qA8rMbz1wQ4DOFfM2ouD29DG9aHWBm6ZOy9BGxiUMmY= diff --git a/internal/mock/config_cookie.go b/internal/mock/config_cookie.go index 5fab6d1d7dc..d6898a7b8d8 100644 --- a/internal/mock/config_cookie.go +++ b/internal/mock/config_cookie.go @@ -1,8 +1,5 @@ -// Copyright © 2022 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/ory/hydra/x (interfaces: CookieConfigProvider) +// Source: github.com/ory/hydra/v2/x (interfaces: CookieConfigProvider) // Package mock is a generated GoMock package. package mock diff --git a/jwk/registry_mock_test.go b/jwk/registry_mock_test.go index c305fd18167..68de41ca30b 100644 --- a/jwk/registry_mock_test.go +++ b/jwk/registry_mock_test.go @@ -1,6 +1,3 @@ -// Copyright © 2022 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - // Code generated by MockGen. DO NOT EDIT. // Source: jwk/registry.go @@ -11,9 +8,8 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - herodot "github.com/ory/herodot" - "github.com/ory/hydra/v2/aead" + aead "github.com/ory/hydra/v2/aead" config "github.com/ory/hydra/v2/driver/config" jwk "github.com/ory/hydra/v2/jwk" logrusx "github.com/ory/x/logrusx" diff --git a/oauth2/oauth2_provider_mock_test.go b/oauth2/oauth2_provider_mock_test.go index 83d584eb12f..e99c959fc4f 100644 --- a/oauth2/oauth2_provider_mock_test.go +++ b/oauth2/oauth2_provider_mock_test.go @@ -1,6 +1,3 @@ -// Copyright © 2022 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - // Code generated by MockGen. DO NOT EDIT. // Source: github.com/ory/fosite (interfaces: OAuth2Provider) @@ -13,7 +10,6 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - fosite "github.com/ory/fosite" ) @@ -121,6 +117,36 @@ func (mr *MockOAuth2ProviderMockRecorder) NewAuthorizeResponse(arg0, arg1, arg2 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAuthorizeResponse", reflect.TypeOf((*MockOAuth2Provider)(nil).NewAuthorizeResponse), arg0, arg1, arg2) } +// NewDeviceRequest mocks base method. +func (m *MockOAuth2Provider) NewDeviceRequest(arg0 context.Context, arg1 *http.Request) (fosite.DeviceRequester, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewDeviceRequest", arg0, arg1) + ret0, _ := ret[0].(fosite.DeviceRequester) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewDeviceRequest indicates an expected call of NewDeviceRequest. +func (mr *MockOAuth2ProviderMockRecorder) NewDeviceRequest(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDeviceRequest", reflect.TypeOf((*MockOAuth2Provider)(nil).NewDeviceRequest), arg0, arg1) +} + +// NewDeviceResponse mocks base method. +func (m *MockOAuth2Provider) NewDeviceResponse(arg0 context.Context, arg1 fosite.DeviceRequester) (fosite.DeviceResponder, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewDeviceResponse", arg0, arg1) + ret0, _ := ret[0].(fosite.DeviceResponder) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// NewDeviceResponse indicates an expected call of NewDeviceResponse. +func (mr *MockOAuth2ProviderMockRecorder) NewDeviceResponse(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDeviceResponse", reflect.TypeOf((*MockOAuth2Provider)(nil).NewDeviceResponse), arg0, arg1) +} + // NewIntrospectionRequest mocks base method. func (m *MockOAuth2Provider) NewIntrospectionRequest(arg0 context.Context, arg1 *http.Request, arg2 fosite.Session) (fosite.IntrospectionResponder, error) { m.ctrl.T.Helper() @@ -228,6 +254,18 @@ func (mr *MockOAuth2ProviderMockRecorder) WriteAuthorizeResponse(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteAuthorizeResponse", reflect.TypeOf((*MockOAuth2Provider)(nil).WriteAuthorizeResponse), arg0, arg1, arg2, arg3) } +// WriteDeviceResponse mocks base method. +func (m *MockOAuth2Provider) WriteDeviceResponse(arg0 context.Context, arg1 http.ResponseWriter, arg2 fosite.DeviceRequester, arg3 fosite.DeviceResponder) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "WriteDeviceResponse", arg0, arg1, arg2, arg3) +} + +// WriteDeviceResponse indicates an expected call of WriteDeviceResponse. +func (mr *MockOAuth2ProviderMockRecorder) WriteDeviceResponse(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteDeviceResponse", reflect.TypeOf((*MockOAuth2Provider)(nil).WriteDeviceResponse), arg0, arg1, arg2, arg3) +} + // WriteIntrospectionError mocks base method. func (m *MockOAuth2Provider) WriteIntrospectionError(arg0 context.Context, arg1 http.ResponseWriter, arg2 error) { m.ctrl.T.Helper() diff --git a/oauth2/registry.go b/oauth2/registry.go index 52f9f7bb9bf..4b7a19c402a 100644 --- a/oauth2/registry.go +++ b/oauth2/registry.go @@ -6,6 +6,7 @@ package oauth2 import ( "github.com/ory/fosite" "github.com/ory/fosite/handler/openid" + "github.com/ory/fosite/handler/rfc8628" "github.com/ory/hydra/v2/aead" "github.com/ory/hydra/v2/client" "github.com/ory/hydra/v2/consent" @@ -33,4 +34,5 @@ type Registry interface { OpenIDConnectRequestValidator() *openid.OpenIDConnectRequestValidator AccessRequestHooks() []AccessRequestHook OAuth2ProviderConfig() fosite.Configurator + RFC8628HMACStrategy() rfc8628.RFC8628CodeStrategy } diff --git a/spec/config.json b/spec/config.json index 9899db71df0..fbaa33f99a7 100644 --- a/spec/config.json +++ b/spec/config.json @@ -614,6 +614,14 @@ "https://my-service.com/oauth2/auth" ] }, + "device_authorization_url": { + "type": "string", + "description": "Overwrites the OAuth2 Device Auth URL", + "format": "uri-reference", + "examples": [ + "https://my-service.com/oauth2/device/auth" + ] + }, "client_registration_url": { "description": "Sets the OpenID Connect Dynamic Client Registration Endpoint", "type": "string", @@ -803,6 +811,15 @@ "/ui/logout" ] }, + "device_verification": { + "type": "string", + "description": "Sets the device verification URL. Defaults to an internal fallback URL showing an error.", + "format": "uri-reference", + "examples": [ + "https://my-app/device", + "/ui/device" + ] + }, "error": { "type": "string", "description": "Sets the error endpoint. The error ui will be shown when an OAuth2 error occurs that which can not be sent back to the client. Defaults to an internal fallback URL showing an error.", @@ -947,6 +964,15 @@ "$ref": "#/definitions/duration" } ] + }, + "device_user_code": { + "description": "Configures how long device & user codes are valid.", + "default": "10m", + "allOf": [ + { + "$ref": "#/definitions/duration" + } + ] } } }, @@ -1109,6 +1135,22 @@ } ] }, + "device_authorization": { + "type": "object", + "additionalProperties": false, + "properties": { + "token_polling_interval": { + "allOf": [ + { + "$ref": "#/definitions/duration" + } + ], + "default": "5s", + "description": "configure how often a non-interactive device should poll the device token endpoint", + "examples": ["5s", "15s", "1m"] + } + } + }, "token_hook": { "description": "Sets the token hook endpoint for all grant types. If set it will be called while providing token to customize claims.", "examples": ["https://my-example.app/token-hook"], From a84f8ed27077545868b55c51bfbf193153cc111a Mon Sep 17 00:00:00 2001 From: Nikos Date: Fri, 9 Feb 2024 17:02:28 +0200 Subject: [PATCH 2/8] fix: update database schema --- .../20230202000001000000_device_flow.down.sql | 16 ++++ ...30202000001000000_device_flow.mysql.up.sql | 57 +++++++++++ ...000001000000_device_flow.postgres.down.sql | 6 ++ ...02000001000000_device_flow.postgres.up.sql | 55 +++++++++++ .../20230202000001000000_device_flow.up.sql | 49 ++++++++++ persistence/sql/persister_oauth2.go | 96 ++++++++++++++++++- .../20230202000001000000_device_flow.down.sql | 14 +++ ...30202000001000000_device_flow.mysql.up.sql | 55 +++++++++++ ...02000001000000_device_flow.postgres.up.sql | 53 ++++++++++ .../20230202000001000000_device_flow.up.sql | 47 +++++++++ x/clean_sql.go | 4 + 11 files changed, 447 insertions(+), 5 deletions(-) create mode 100644 persistence/sql/migrations/20230202000001000000_device_flow.down.sql create mode 100644 persistence/sql/migrations/20230202000001000000_device_flow.mysql.up.sql create mode 100644 persistence/sql/migrations/20230202000001000000_device_flow.postgres.down.sql create mode 100644 persistence/sql/migrations/20230202000001000000_device_flow.postgres.up.sql create mode 100644 persistence/sql/migrations/20230202000001000000_device_flow.up.sql create mode 100644 persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.down.sql create mode 100644 persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.mysql.up.sql create mode 100644 persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.postgres.up.sql create mode 100644 persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.up.sql diff --git a/persistence/sql/migrations/20230202000001000000_device_flow.down.sql b/persistence/sql/migrations/20230202000001000000_device_flow.down.sql new file mode 100644 index 00000000000..90811796437 --- /dev/null +++ b/persistence/sql/migrations/20230202000001000000_device_flow.down.sql @@ -0,0 +1,16 @@ +-- Migration generated by the command below; DO NOT EDIT. +-- hydra:generate hydra migrate gen +ALTER TABLE hydra_oauth2_device_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_device_code_challenge_id_fk; +ALTER TABLE hydra_oauth2_device_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_device_code_client_id_fk; +ALTER TABLE hydra_oauth2_device_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_device_code_nid_fk_idx; + +DROP TABLE IF EXISTS hydra_oauth2_device_code; + +ALTER TABLE hydra_oauth2_user_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_user_code_challenge_id_fk; +ALTER TABLE hydra_oauth2_user_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_user_code_client_id_fk; +ALTER TABLE hydra_oauth2_user_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_user_code_nid_fk_idx; + +DROP TABLE IF EXISTS hydra_oauth2_user_code; + +ALTER TABLE hydra_oauth2_flow DROP COLUMN IF EXISTS device_verifier; +ALTER TABLE hydra_oauth2_flow DROP COLUMN IF EXISTS device_challenge; diff --git a/persistence/sql/migrations/20230202000001000000_device_flow.mysql.up.sql b/persistence/sql/migrations/20230202000001000000_device_flow.mysql.up.sql new file mode 100644 index 00000000000..825122243c4 --- /dev/null +++ b/persistence/sql/migrations/20230202000001000000_device_flow.mysql.up.sql @@ -0,0 +1,57 @@ +-- Migration generated by the command below; DO NOT EDIT. +-- hydra:generate hydra migrate gen +CREATE TABLE IF NOT EXISTS hydra_oauth2_device_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(255) NOT NULL DEFAULT '', + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL DEFAULT '', + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NOT NULL, + granted_audience TEXT NOT NULL, + challenge_id VARCHAR(40) NULL, + nid CHAR(36) NOT NULL +); +CREATE INDEX hydra_oauth2_device_code_nid_fk_idx ON hydra_oauth2_device_code (nid); +CREATE INDEX hydra_oauth2_device_code_request_id_idx ON hydra_oauth2_device_code (request_id, nid); +CREATE INDEX hydra_oauth2_device_code_client_id_idx ON hydra_oauth2_device_code (client_id, nid); +CREATE INDEX hydra_oauth2_device_code_challenge_id_idx ON hydra_oauth2_device_code (challenge_id); +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +CREATE TABLE IF NOT EXISTS hydra_oauth2_user_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(40) NOT NULL DEFAULT '', + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL DEFAULT '', + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NOT NULL, + granted_audience TEXT NOT NULL, + challenge_id VARCHAR(40) NULL, + nid CHAR(36) NOT NULL +); +CREATE INDEX hydra_oauth2_user_code_nid_fk_idx ON hydra_oauth2_user_code (nid); +CREATE INDEX hydra_oauth2_user_code_request_id_idx ON hydra_oauth2_user_code (request_id, nid); +CREATE INDEX hydra_oauth2_user_code_client_id_idx ON hydra_oauth2_user_code (client_id, nid); +CREATE INDEX hydra_oauth2_user_code_challenge_id_idx ON hydra_oauth2_user_code (challenge_id); +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +ALTER TABLE hydra_oauth2_flow ADD device_verifier VARCHAR(40); +ALTER TABLE hydra_oauth2_flow ADD device_challenge VARCHAR(255); + +CREATE INDEX hydra_oauth2_flow_device_verifier_idx ON hydra_oauth2_flow (device_verifier, nid); +CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge, nid); diff --git a/persistence/sql/migrations/20230202000001000000_device_flow.postgres.down.sql b/persistence/sql/migrations/20230202000001000000_device_flow.postgres.down.sql new file mode 100644 index 00000000000..e32dda541c5 --- /dev/null +++ b/persistence/sql/migrations/20230202000001000000_device_flow.postgres.down.sql @@ -0,0 +1,6 @@ + + +-- Migration generated by the command below; DO NOT EDIT. +-- hydra:generate hydra migrate gen +DROP TABLE IF EXISTS hydra_oauth2_device_code; +DROP TABLE IF EXISTS hydra_oauth2_user_code; diff --git a/persistence/sql/migrations/20230202000001000000_device_flow.postgres.up.sql b/persistence/sql/migrations/20230202000001000000_device_flow.postgres.up.sql new file mode 100644 index 00000000000..eaf57974203 --- /dev/null +++ b/persistence/sql/migrations/20230202000001000000_device_flow.postgres.up.sql @@ -0,0 +1,55 @@ +-- Migration generated by the command below; DO NOT EDIT. +-- hydra:generate hydra migrate gen +CREATE TABLE IF NOT EXISTS hydra_oauth2_device_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(40) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_device_code_request_id_idx ON hydra_oauth2_device_code (request_id, nid); +CREATE INDEX hydra_oauth2_device_code_client_id_idx ON hydra_oauth2_device_code (client_id, nid); +CREATE INDEX hydra_oauth2_device_code_challenge_id_idx ON hydra_oauth2_device_code (challenge_id, nid); +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +CREATE TABLE IF NOT EXISTS hydra_oauth2_user_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(255) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_user_code_request_id_idx ON hydra_oauth2_user_code (request_id, nid); +CREATE INDEX hydra_oauth2_user_code_client_id_idx ON hydra_oauth2_user_code (client_id, nid); +CREATE INDEX hydra_oauth2_user_code_challenge_id_idx ON hydra_oauth2_user_code (challenge_id, nid); +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +ALTER TABLE hydra_oauth2_flow ADD device_verifier VARCHAR(40); +ALTER TABLE hydra_oauth2_flow ADD device_challenge VARCHAR(255); + +CREATE INDEX hydra_oauth2_flow_device_verifier_idx ON hydra_oauth2_flow (device_verifier, nid); +CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge, nid); diff --git a/persistence/sql/migrations/20230202000001000000_device_flow.up.sql b/persistence/sql/migrations/20230202000001000000_device_flow.up.sql new file mode 100644 index 00000000000..bb746ae4450 --- /dev/null +++ b/persistence/sql/migrations/20230202000001000000_device_flow.up.sql @@ -0,0 +1,49 @@ +-- Migration generated by the command below; DO NOT EDIT. +-- hydra:generate hydra migrate gen +CREATE TABLE IF NOT EXISTS hydra_oauth2_device_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(40) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_device_code_request_id_idx ON hydra_oauth2_device_code (request_id, nid); +CREATE INDEX hydra_oauth2_device_code_client_id_idx ON hydra_oauth2_device_code (client_id, nid); +CREATE INDEX hydra_oauth2_device_code_challenge_id_idx ON hydra_oauth2_device_code (challenge_id, nid); + +CREATE TABLE IF NOT EXISTS hydra_oauth2_user_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(255) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_user_code_request_id_idx ON hydra_oauth2_user_code (request_id, nid); +CREATE INDEX hydra_oauth2_user_code_client_id_idx ON hydra_oauth2_user_code (client_id, nid); +CREATE INDEX hydra_oauth2_user_code_challenge_id_idx ON hydra_oauth2_user_code (challenge_id, nid); + +ALTER TABLE hydra_oauth2_flow ADD device_verifier VARCHAR(40); +ALTER TABLE hydra_oauth2_flow ADD device_challenge VARCHAR(255); + +CREATE INDEX hydra_oauth2_flow_verifier_idx ON hydra_oauth2_flow (device_verifier, nid); +CREATE INDEX hydra_oauth2_flow_challenge_idx ON hydra_oauth2_flow (device_challenge, nid); diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index c49c9c7f823..251236373a7 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -56,11 +56,13 @@ type ( ) const ( - sqlTableOpenID tableName = "oidc" - sqlTableAccess tableName = "access" - sqlTableRefresh tableName = "refresh" - sqlTableCode tableName = "code" - sqlTablePKCE tableName = "pkce" + sqlTableOpenID tableName = "oidc" + sqlTableAccess tableName = "access" + sqlTableRefresh tableName = "refresh" + sqlTableCode tableName = "code" + sqlTablePKCE tableName = "pkce" + sqlTableDeviceCode tableName = "device_code" + sqlTableUserCode tableName = "user_code" ) func (r OAuth2RequestSQL) TableName() string { @@ -228,6 +230,28 @@ func (p *Persister) createSession(ctx context.Context, signature string, request return nil } +func (p *Persister) updateSessionBySignature(ctx context.Context, signature string, requester fosite.Requester, table tableName) (err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.updateSession") + defer otelx.End(span, &err) + + req, err := p.sqlSchemaFromRequest(ctx, signature, requester, table) + if err != nil { + return err + } + + if count, err := p.UpdateWithNetwork(ctx, req); count != 1 { + return errorsx.WithStack(fosite.ErrNotFound) + } else if err := sqlcon.HandleError(err); err != nil { + if errors.Is(err, sqlcon.ErrConcurrentUpdate) { + return errors.Wrap(fosite.ErrSerializationFailure, err.Error()) + } else if strings.Contains(err.Error(), "Error 1213") { // InnoDB Deadlock? + return errors.Wrap(fosite.ErrSerializationFailure, err.Error()) + } + return err + } + return nil +} + func (p *Persister) findSessionBySignature(ctx context.Context, signature string, session fosite.Session, table tableName) (fosite.Requester, error) { r := OAuth2RequestSQL{Table: table} err := p.QueryWithNetwork(ctx).Where("signature = ?", signature).First(&r) @@ -546,3 +570,65 @@ func (p *Persister) DeleteAccessTokens(ctx context.Context, clientID string) (er p.QueryWithNetwork(ctx).Where("client_id=?", clientID).Delete(&OAuth2RequestSQL{Table: sqlTableAccess}), ) } + +func (p *Persister) CreateDeviceCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.CreateDeviceCodeSession") + defer otelx.End(span, &err) + return p.createSession(ctx, signature, requester, sqlTableDeviceCode) +} + +func (p *Persister) UpdateDeviceCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UpdateDeviceCodeSession") + defer otelx.End(span, &err) + return p.updateSessionBySignature(ctx, signature, requester, sqlTableDeviceCode) +} + +func (p *Persister) GetDeviceCodeSession(ctx context.Context, signature string, session fosite.Session) (_ fosite.Requester, err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.GetDeviceCodeSession") + defer otelx.End(span, &err) + return p.findSessionBySignature(ctx, signature, session, sqlTableDeviceCode) +} + +func (p *Persister) InvalidateDeviceCodeSession(ctx context.Context, signature string) (err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.InvalidateDeviceCodeSession") + defer otelx.End(span, &err) + + /* #nosec G201 table is static */ + return sqlcon.HandleError( + p.Connection(ctx). + RawQuery( + fmt.Sprintf("UPDATE %s SET active=false WHERE signature=? AND nid = ?", OAuth2RequestSQL{Table: sqlTableDeviceCode}.TableName()), + signature, + p.NetworkID(ctx), + ). + Exec(), + ) +} + +func (p *Persister) CreateUserCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.CreateUserCodeSession") + defer otelx.End(span, &err) + return p.createSession(ctx, signature, requester, sqlTableUserCode) +} + +func (p *Persister) GetUserCodeSession(ctx context.Context, signature string, session fosite.Session) (_ fosite.Requester, err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.GetUserCodeSession") + defer otelx.End(span, &err) + return p.findSessionBySignature(ctx, signature, session, sqlTableUserCode) +} + +func (p *Persister) InvalidateUserCodeSession(ctx context.Context, signature string) (err error) { + ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.InvalidateUserCodeSession") + defer otelx.End(span, &err) + + /* #nosec G201 table is static */ + return sqlcon.HandleError( + p.Connection(ctx). + RawQuery( + fmt.Sprintf("UPDATE %s SET active=false WHERE signature=? AND nid = ?", OAuth2RequestSQL{Table: sqlTableUserCode}.TableName()), + signature, + p.NetworkID(ctx), + ). + Exec(), + ) +} diff --git a/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.down.sql b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.down.sql new file mode 100644 index 00000000000..f2f01b696a0 --- /dev/null +++ b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.down.sql @@ -0,0 +1,14 @@ +ALTER TABLE hydra_oauth2_device_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_device_code_challenge_id_fk; +ALTER TABLE hydra_oauth2_device_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_device_code_client_id_fk; +ALTER TABLE hydra_oauth2_device_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_device_code_nid_fk_idx; + +DROP TABLE IF EXISTS hydra_oauth2_device_code; + +ALTER TABLE hydra_oauth2_user_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_user_code_challenge_id_fk; +ALTER TABLE hydra_oauth2_user_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_user_code_client_id_fk; +ALTER TABLE hydra_oauth2_user_code DROP FOREIGN KEY IF EXISTS hydra_oauth2_user_code_nid_fk_idx; + +DROP TABLE IF EXISTS hydra_oauth2_user_code; + +ALTER TABLE hydra_oauth2_flow DROP COLUMN IF EXISTS device_verifier; +ALTER TABLE hydra_oauth2_flow DROP COLUMN IF EXISTS device_challenge; diff --git a/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.mysql.up.sql b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.mysql.up.sql new file mode 100644 index 00000000000..eb75a615ab8 --- /dev/null +++ b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.mysql.up.sql @@ -0,0 +1,55 @@ +CREATE TABLE IF NOT EXISTS hydra_oauth2_device_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(255) NOT NULL DEFAULT '', + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL DEFAULT '', + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NOT NULL, + granted_audience TEXT NOT NULL, + challenge_id VARCHAR(40) NULL, + nid CHAR(36) NOT NULL +); +CREATE INDEX hydra_oauth2_device_code_nid_fk_idx ON hydra_oauth2_device_code (nid); +CREATE INDEX hydra_oauth2_device_code_request_id_idx ON hydra_oauth2_device_code (request_id, nid); +CREATE INDEX hydra_oauth2_device_code_client_id_idx ON hydra_oauth2_device_code (client_id, nid); +CREATE INDEX hydra_oauth2_device_code_challenge_id_idx ON hydra_oauth2_device_code (challenge_id); +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +CREATE TABLE IF NOT EXISTS hydra_oauth2_user_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(40) NOT NULL DEFAULT '', + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL DEFAULT '', + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NOT NULL, + granted_audience TEXT NOT NULL, + challenge_id VARCHAR(40) NULL, + nid CHAR(36) NOT NULL +); +CREATE INDEX hydra_oauth2_user_code_nid_fk_idx ON hydra_oauth2_user_code (nid); +CREATE INDEX hydra_oauth2_user_code_request_id_idx ON hydra_oauth2_user_code (request_id, nid); +CREATE INDEX hydra_oauth2_user_code_client_id_idx ON hydra_oauth2_user_code (client_id, nid); +CREATE INDEX hydra_oauth2_user_code_challenge_id_idx ON hydra_oauth2_user_code (challenge_id); +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +ALTER TABLE hydra_oauth2_flow ADD device_verifier VARCHAR(40); +ALTER TABLE hydra_oauth2_flow ADD device_challenge VARCHAR(255); + +CREATE INDEX hydra_oauth2_flow_device_verifier_idx ON hydra_oauth2_flow (device_verifier, nid); +CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge, nid); diff --git a/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.postgres.up.sql b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.postgres.up.sql new file mode 100644 index 00000000000..d8d4ef50947 --- /dev/null +++ b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.postgres.up.sql @@ -0,0 +1,53 @@ +CREATE TABLE IF NOT EXISTS hydra_oauth2_device_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(40) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_device_code_request_id_idx ON hydra_oauth2_device_code (request_id, nid); +CREATE INDEX hydra_oauth2_device_code_client_id_idx ON hydra_oauth2_device_code (client_id, nid); +CREATE INDEX hydra_oauth2_device_code_challenge_id_idx ON hydra_oauth2_device_code (challenge_id, nid); +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_device_code ADD CONSTRAINT hydra_oauth2_device_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +CREATE TABLE IF NOT EXISTS hydra_oauth2_user_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(255) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT NOW(), + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_user_code_request_id_idx ON hydra_oauth2_user_code (request_id, nid); +CREATE INDEX hydra_oauth2_user_code_client_id_idx ON hydra_oauth2_user_code (client_id, nid); +CREATE INDEX hydra_oauth2_user_code_challenge_id_idx ON hydra_oauth2_user_code (challenge_id, nid); +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_challenge_id_fk FOREIGN KEY (challenge_id) REFERENCES hydra_oauth2_flow(consent_challenge_id) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_client_id_fk FOREIGN KEY (client_id, nid) REFERENCES hydra_client(id, nid) ON DELETE CASCADE; +ALTER TABLE hydra_oauth2_user_code ADD CONSTRAINT hydra_oauth2_user_code_nid_fk_idx FOREIGN KEY (nid) REFERENCES networks(id) ON UPDATE RESTRICT ON DELETE CASCADE; + +ALTER TABLE hydra_oauth2_flow ADD device_verifier VARCHAR(40); +ALTER TABLE hydra_oauth2_flow ADD device_challenge VARCHAR(255); + +CREATE INDEX hydra_oauth2_flow_device_verifier_idx ON hydra_oauth2_flow (device_verifier, nid); +CREATE INDEX hydra_oauth2_flow_device_challenge_idx ON hydra_oauth2_flow (device_challenge, nid); diff --git a/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.up.sql b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.up.sql new file mode 100644 index 00000000000..7b5a09682ae --- /dev/null +++ b/persistence/sql/src/YYYYMMDD000001_device_flow/20230202000001000000_device_flow.up.sql @@ -0,0 +1,47 @@ +CREATE TABLE IF NOT EXISTS hydra_oauth2_device_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(40) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_device_code_request_id_idx ON hydra_oauth2_device_code (request_id, nid); +CREATE INDEX hydra_oauth2_device_code_client_id_idx ON hydra_oauth2_device_code (client_id, nid); +CREATE INDEX hydra_oauth2_device_code_challenge_id_idx ON hydra_oauth2_device_code (challenge_id, nid); + +CREATE TABLE IF NOT EXISTS hydra_oauth2_user_code +( + signature VARCHAR(255) NOT NULL PRIMARY KEY, + request_id VARCHAR(255) NOT NULL, + requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + client_id VARCHAR(255) NOT NULL, + scope TEXT NOT NULL, + granted_scope TEXT NOT NULL, + form_data TEXT NOT NULL, + session_data TEXT NOT NULL, + subject VARCHAR(255) NOT NULL DEFAULT '', + active BOOL NOT NULL DEFAULT true, + requested_audience TEXT NULL DEFAULT '', + granted_audience TEXT NULL DEFAULT '', + challenge_id VARCHAR(40) NULL, + nid UUID NULL +); +CREATE INDEX hydra_oauth2_user_code_request_id_idx ON hydra_oauth2_user_code (request_id, nid); +CREATE INDEX hydra_oauth2_user_code_client_id_idx ON hydra_oauth2_user_code (client_id, nid); +CREATE INDEX hydra_oauth2_user_code_challenge_id_idx ON hydra_oauth2_user_code (challenge_id, nid); + +ALTER TABLE hydra_oauth2_flow ADD device_verifier VARCHAR(40); +ALTER TABLE hydra_oauth2_flow ADD device_challenge VARCHAR(255); + +CREATE INDEX hydra_oauth2_flow_verifier_idx ON hydra_oauth2_flow (device_verifier, nid); +CREATE INDEX hydra_oauth2_flow_challenge_idx ON hydra_oauth2_flow (device_challenge, nid); diff --git a/x/clean_sql.go b/x/clean_sql.go index a02a9a054ce..243d65033d8 100644 --- a/x/clean_sql.go +++ b/x/clean_sql.go @@ -16,6 +16,8 @@ func DeleteHydraRows(t *testing.T, c *pop.Connection) { "hydra_oauth2_code", "hydra_oauth2_oidc", "hydra_oauth2_pkce", + "hydra_oauth2_device_code", + "hydra_oauth2_user_code", "hydra_oauth2_flow", "hydra_oauth2_authentication_session", "hydra_oauth2_obfuscated_authentication_session", @@ -39,6 +41,8 @@ func CleanSQLPop(t *testing.T, c *pop.Connection) { "hydra_oauth2_code", "hydra_oauth2_oidc", "hydra_oauth2_pkce", + "hydra_oauth2_device_code", + "hydra_oauth2_user_code", "hydra_oauth2_flow", "hydra_oauth2_authentication_session", "hydra_oauth2_obfuscated_authentication_session", From 6ce8eae2b918768d5bada9ab3147ec16876788a5 Mon Sep 17 00:00:00 2001 From: Nikos Date: Fri, 9 Feb 2024 17:04:19 +0200 Subject: [PATCH 3/8] feat: add device authorization endpoint handler --- oauth2/handler.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ spec/swagger.json | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/oauth2/handler.go b/oauth2/handler.go index 5662be5cc8e..7749770fe2a 100644 --- a/oauth2/handler.go +++ b/oauth2/handler.go @@ -59,6 +59,9 @@ const ( IntrospectPath = "/oauth2/introspect" RevocationPath = "/oauth2/revoke" DeleteTokensPath = "/oauth2/tokens" // #nosec G101 + + // Device Grant Handler + DeviceAuthPath = "/oauth2/device/auth" ) type Handler struct { @@ -104,6 +107,8 @@ func (h *Handler) SetRoutes(admin *httprouterx.RouterAdmin, public *httprouterx. public.Handler("OPTIONS", VerifiableCredentialsPath, corsMiddleware(http.HandlerFunc(h.handleOptions))) public.Handler("POST", VerifiableCredentialsPath, corsMiddleware(http.HandlerFunc(h.createVerifiableCredential))) + public.Handler("POST", DeviceAuthPath, http.HandlerFunc(h.performOAuth2DeviceFlow)) + admin.POST(IntrospectPath, h.introspectOAuth2Token) admin.DELETE(DeleteTokensPath, h.deleteOAuth2Token) } @@ -687,6 +692,47 @@ func (h *Handler) getOidcUserInfo(w http.ResponseWriter, r *http.Request) { } } +// swagger:route POST /oauth2/device/auth v0alpha2 performOAuth2DeviceFlow +// +// # The OAuth 2.0 Device Authorize Endpoint +// +// This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows. +// OAuth2 is a very popular protocol and a library for your programming language will exists. +// +// To learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc8628 +// +// Consumes: +// - application/x-www-form-urlencoded +// +// Schemes: http, https +// +// Responses: +// 200: deviceAuthorization +// default: errorOAuth2 +func (h *Handler) performOAuth2DeviceFlow(w http.ResponseWriter, r *http.Request) { + var ctx = r.Context() + request, err := h.r.OAuth2Provider().NewDeviceRequest(ctx, r) + if err != nil { + h.r.OAuth2Provider().WriteAccessError(ctx, w, request, err) + return + } + + // TODO: We need to call the consent manager here to create a new loginFlow with the + // device_challenge and device_verifier + var session = &Session{ + DefaultSession: &openid.DefaultSession{ + Headers: &jwt.Headers{}}, + } + + resp, err := h.r.OAuth2Provider().NewDeviceResponse(ctx, request, session) + if err != nil { + h.r.OAuth2Provider().WriteAccessError(ctx, w, request, err) + return + } + + h.r.OAuth2Provider().WriteDeviceResponse(ctx, w, request, resp) +} + // Revoke OAuth 2.0 Access or Refresh Token Request // // swagger:parameters revokeOAuth2Token diff --git a/spec/swagger.json b/spec/swagger.json index a49801ddeb1..ee15a6687d0 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -2311,6 +2311,45 @@ } } }, + "deviceAuthorization": { + "description": "OAuth 2.0 Device Authorization endpoint", + "type": "object", + "title": "OAuth2 Device Flow", + "properties": { + "device_code": { + "description": "The device verification code.", + "type": "string", + "example": "ory_dc_smldfksmdfkl.mslkmlkmlk" + }, + "expires_in": { + "description": "The lifetime in seconds of the \"device_code\" and \"user_code\".", + "type": "integer", + "format": "int64", + "example": 16830 + }, + "interval": { + "description": "The minimum amount of time in seconds that the client\nSHOULD wait between polling requests to the token endpoint. If no\nvalue is provided, clients MUST use 5 as the default.", + "type": "integer", + "format": "int64", + "example": 5 + }, + "user_code": { + "description": "The end-user verification code.", + "type": "string", + "example": "AAAAAA" + }, + "verification_uri": { + "description": "The end-user verification URI on the authorization\nserver. The URI should be short and easy to remember as end users\nwill be asked to manually type it into their user agent.", + "type": "string", + "example": "https://auth.ory.sh/tv" + }, + "verification_uri_complete": { + "description": "A verification URI that includes the \"user_code\" (or\nother information with the same function as the \"user_code\"),\nwhich is designed for non-textual transmission.", + "type": "string", + "example": "https://auth.ory.sh/tv?user_code=AAAAAA" + } + } + }, "errorOAuth2": { "description": "Error", "type": "object", From dbe3333b4abe9a3e78579ea7760b58b0c8afa3a1 Mon Sep 17 00:00:00 2001 From: Nikos Date: Fri, 9 Feb 2024 17:38:47 +0200 Subject: [PATCH 4/8] chore: update tests --- internal/.hydra.yaml | 5 +++++ oauth2/oauth2_provider_mock_test.go | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/internal/.hydra.yaml b/internal/.hydra.yaml index bb02d986ad6..7442fe036f2 100644 --- a/internal/.hydra.yaml +++ b/internal/.hydra.yaml @@ -74,6 +74,7 @@ webfinger: auth_url: https://example.com/auth token_url: https://example.com/token client_registration_url: https://example.com + device_authorization_url: https://example.com/device_authorization supported_claims: - username supported_scope: @@ -100,6 +101,7 @@ urls: consent: https://consent logout: https://logout error: https://error + device_verification: https://device post_logout_redirect: https://post_logout strategies: @@ -112,12 +114,15 @@ ttl: refresh_token: 2h id_token: 2h auth_code: 2h + device_user_code: 2h oauth2: expose_internal_errors: true hashers: bcrypt: cost: 20 + device_authorization: + token_polling_interval: 2h pkce: enforced: true enforced_for_public_clients: true diff --git a/oauth2/oauth2_provider_mock_test.go b/oauth2/oauth2_provider_mock_test.go index e99c959fc4f..8149e206b28 100644 --- a/oauth2/oauth2_provider_mock_test.go +++ b/oauth2/oauth2_provider_mock_test.go @@ -133,18 +133,18 @@ func (mr *MockOAuth2ProviderMockRecorder) NewDeviceRequest(arg0, arg1 interface{ } // NewDeviceResponse mocks base method. -func (m *MockOAuth2Provider) NewDeviceResponse(arg0 context.Context, arg1 fosite.DeviceRequester) (fosite.DeviceResponder, error) { +func (m *MockOAuth2Provider) NewDeviceResponse(arg0 context.Context, arg1 fosite.DeviceRequester, arg2 fosite.Session) (fosite.DeviceResponder, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "NewDeviceResponse", arg0, arg1) + ret := m.ctrl.Call(m, "NewDeviceResponse", arg0, arg1, arg2) ret0, _ := ret[0].(fosite.DeviceResponder) ret1, _ := ret[1].(error) return ret0, ret1 } // NewDeviceResponse indicates an expected call of NewDeviceResponse. -func (mr *MockOAuth2ProviderMockRecorder) NewDeviceResponse(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockOAuth2ProviderMockRecorder) NewDeviceResponse(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDeviceResponse", reflect.TypeOf((*MockOAuth2Provider)(nil).NewDeviceResponse), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewDeviceResponse", reflect.TypeOf((*MockOAuth2Provider)(nil).NewDeviceResponse), arg0, arg1, arg2) } // NewIntrospectionRequest mocks base method. @@ -207,7 +207,7 @@ func (mr *MockOAuth2ProviderMockRecorder) NewRevocationRequest(arg0, arg1 interf } // WriteAccessError mocks base method. -func (m *MockOAuth2Provider) WriteAccessError(arg0 context.Context, arg1 http.ResponseWriter, arg2 fosite.AccessRequester, arg3 error) { +func (m *MockOAuth2Provider) WriteAccessError(arg0 context.Context, arg1 http.ResponseWriter, arg2 fosite.Requester, arg3 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "WriteAccessError", arg0, arg1, arg2, arg3) } From bd2abb4f3fc3b116d307f533797300eda3cae2ea Mon Sep 17 00:00:00 2001 From: Nikos Date: Mon, 12 Feb 2024 14:48:26 +0200 Subject: [PATCH 5/8] chore: add godocs --- driver/config/provider.go | 4 ++++ driver/registry_base.go | 1 + fositex/config.go | 2 ++ oauth2/handler.go | 2 +- persistence/sql/persister_oauth2.go | 6 ++++++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/driver/config/provider.go b/driver/config/provider.go index 84280440418..8382bcd7b65 100644 --- a/driver/config/provider.go +++ b/driver/config/provider.go @@ -376,10 +376,12 @@ func (p *DefaultProvider) fallbackURL(ctx context.Context, path string, host str return &u } +// GetDeviceAndUserCodeLifespan returns the device_code and user_code lifespan. Defaults to 15 minutes. func (p *DefaultProvider) GetDeviceAndUserCodeLifespan(ctx context.Context) time.Duration { return p.p.DurationF(KeyDeviceAndUserCodeLifespan, time.Minute*15) } +// GetDeviceAuthTokenPollingInterval returns device grant token endpoint polling interval. Defaults to 5 seconds. func (p *DefaultProvider) GetDeviceAuthTokenPollingInterval(ctx context.Context) time.Duration { return p.p.DurationF(KeyDeviceAuthTokenPollingInterval, time.Second*5) } @@ -404,6 +406,7 @@ func (p *DefaultProvider) ErrorURL(ctx context.Context) *url.URL { return urlRoot(p.getProvider(ctx).RequestURIF(KeyErrorURL, p.publicFallbackURL(ctx, "oauth2/fallbacks/error"))) } +// DeviceVerificationURL returns user_code verification page URL. Defaults to "oauth2/fallbacks/device". func (p *DefaultProvider) DeviceVerificationURL(ctx context.Context) *url.URL { return urlRoot(p.getProvider(ctx).URIF(KeyDeviceVerificationURL, p.publicFallbackURL(ctx, "oauth2/fallbacks/device"))) } @@ -465,6 +468,7 @@ func (p *DefaultProvider) OAuth2AuthURL(ctx context.Context) *url.URL { return p.getProvider(ctx).RequestURIF(KeyOAuth2AuthURL, urlx.AppendPaths(p.PublicURL(ctx), "/oauth2/auth")) } +// OAuth2DeviceAuthorisationURL returns device authorization endpoint. Defaults to "/oauth2/device/auth". func (p *DefaultProvider) OAuth2DeviceAuthorisationURL(ctx context.Context) *url.URL { return p.getProvider(ctx).RequestURIF(KeyOAuth2DeviceAuthorisationURL, urlx.AppendPaths(p.PublicURL(ctx), "/oauth2/device/auth")) } diff --git a/driver/registry_base.go b/driver/registry_base.go index cf05892185b..4f899724a2e 100644 --- a/driver/registry_base.go +++ b/driver/registry_base.go @@ -411,6 +411,7 @@ func (m *RegistryBase) OAuth2HMACStrategy() *foauth2.HMACSHAStrategy { return m.hmacs } +// RFC8628HMACStrategy returns the rfc8628 strategy func (m *RegistryBase) RFC8628HMACStrategy() rfc8628.RFC8628CodeStrategy { if m.deviceHmac != nil { return m.deviceHmac diff --git a/fositex/config.go b/fositex/config.go index 7c2018971f6..f699eb5ab5e 100644 --- a/fositex/config.go +++ b/fositex/config.go @@ -119,6 +119,7 @@ func (c *Config) GetRevocationHandlers(context.Context) fosite.RevocationHandler return c.revocationHandlers } +// GetDeviceEndpointHandlers returns the deviceEndpointHandlers func (c *Config) GetDeviceEndpointHandlers(ctx context.Context) fosite.DeviceEndpointHandlers { return c.deviceEndpointHandlers } @@ -216,6 +217,7 @@ func (c *Config) GetTokenURLs(ctx context.Context) []string { }) } +// GetDeviceVerificationURL returns the device verification url func (c *Config) GetDeviceVerificationURL(ctx context.Context) string { return urlx.AppendPaths(c.deps.Config().PublicURL(ctx), oauth2.DeviceAuthPath).String() } diff --git a/oauth2/handler.go b/oauth2/handler.go index 7749770fe2a..2a51d14d400 100644 --- a/oauth2/handler.go +++ b/oauth2/handler.go @@ -60,7 +60,7 @@ const ( RevocationPath = "/oauth2/revoke" DeleteTokensPath = "/oauth2/tokens" // #nosec G101 - // Device Grant Handler + // Device authorization endpoint DeviceAuthPath = "/oauth2/device/auth" ) diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index 251236373a7..2a0fe4ca4a4 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -571,18 +571,21 @@ func (p *Persister) DeleteAccessTokens(ctx context.Context, clientID string) (er ) } +// CreateDeviceCodeSession creates a new device code session and stores it in the database func (p *Persister) CreateDeviceCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.CreateDeviceCodeSession") defer otelx.End(span, &err) return p.createSession(ctx, signature, requester, sqlTableDeviceCode) } +// UpdateDeviceCodeSession updates a device code session func (p *Persister) UpdateDeviceCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UpdateDeviceCodeSession") defer otelx.End(span, &err) return p.updateSessionBySignature(ctx, signature, requester, sqlTableDeviceCode) } +// GetDeviceCodeSession returns a device code session from the database func (p *Persister) GetDeviceCodeSession(ctx context.Context, signature string, session fosite.Session) (_ fosite.Requester, err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.GetDeviceCodeSession") defer otelx.End(span, &err) @@ -605,18 +608,21 @@ func (p *Persister) InvalidateDeviceCodeSession(ctx context.Context, signature s ) } +// CreateUserCodeSession creates a new user code session and stores it in the database func (p *Persister) CreateUserCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.CreateUserCodeSession") defer otelx.End(span, &err) return p.createSession(ctx, signature, requester, sqlTableUserCode) } +// GetUserCodeSession returns a user code session from the database func (p *Persister) GetUserCodeSession(ctx context.Context, signature string, session fosite.Session) (_ fosite.Requester, err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.GetUserCodeSession") defer otelx.End(span, &err) return p.findSessionBySignature(ctx, signature, session, sqlTableUserCode) } +// InvalidateUserCodeSession invalidates a user code session func (p *Persister) InvalidateUserCodeSession(ctx context.Context, signature string) (err error) { ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.InvalidateUserCodeSession") defer otelx.End(span, &err) From c9f94f653081cc469f8ce9a4feab1792c6deb447 Mon Sep 17 00:00:00 2001 From: Nikos Date: Mon, 12 Feb 2024 17:51:09 +0200 Subject: [PATCH 6/8] fix: update license --- internal/mock/config_cookie.go | 3 +++ jwk/registry_mock_test.go | 4 ++++ oauth2/oauth2_provider_mock_test.go | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/internal/mock/config_cookie.go b/internal/mock/config_cookie.go index d6898a7b8d8..d146e10cd6e 100644 --- a/internal/mock/config_cookie.go +++ b/internal/mock/config_cookie.go @@ -1,3 +1,6 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + // Code generated by MockGen. DO NOT EDIT. // Source: github.com/ory/hydra/v2/x (interfaces: CookieConfigProvider) diff --git a/jwk/registry_mock_test.go b/jwk/registry_mock_test.go index 68de41ca30b..f9624dc2b75 100644 --- a/jwk/registry_mock_test.go +++ b/jwk/registry_mock_test.go @@ -1,3 +1,6 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + // Code generated by MockGen. DO NOT EDIT. // Source: jwk/registry.go @@ -8,6 +11,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + herodot "github.com/ory/herodot" aead "github.com/ory/hydra/v2/aead" config "github.com/ory/hydra/v2/driver/config" diff --git a/oauth2/oauth2_provider_mock_test.go b/oauth2/oauth2_provider_mock_test.go index 8149e206b28..7dd35e6a157 100644 --- a/oauth2/oauth2_provider_mock_test.go +++ b/oauth2/oauth2_provider_mock_test.go @@ -1,3 +1,6 @@ +// Copyright © 2024 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + // Code generated by MockGen. DO NOT EDIT. // Source: github.com/ory/fosite (interfaces: OAuth2Provider) @@ -10,6 +13,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + fosite "github.com/ory/fosite" ) From c81cf8c7d81026e3b93958514e445e44598f018d Mon Sep 17 00:00:00 2001 From: Nikos Date: Tue, 27 Feb 2024 14:05:19 +0200 Subject: [PATCH 7/8] chore: run go mod tidy --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9778bc460fa..bca9c41a482 100644 --- a/go.mod +++ b/go.mod @@ -256,4 +256,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/ory/fosite => github.com/canonical/fosite v0.0.0-20240131124711-821471ea939e +replace github.com/ory/fosite => github.com/canonical/fosite v0.0.0-20240227091618-fee676b7da75 diff --git a/go.sum b/go.sum index 0a0dc233ca8..cfcfaddd6df 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= -github.com/canonical/fosite v0.0.0-20240131124711-821471ea939e h1:tiksz1KigpLeZzucUtymyhq1XtVy8PLdi50oA8yxx1c= -github.com/canonical/fosite v0.0.0-20240131124711-821471ea939e/go.mod h1:kmWnOXEOHkGcerE/vDP5lEkKI7yJ2E8a6EFaos23xzw= +github.com/canonical/fosite v0.0.0-20240227091618-fee676b7da75 h1:LkVhc3oBLhYdNKhfbf4988ayFZf+kdN9bmy8NLQ0jnQ= +github.com/canonical/fosite v0.0.0-20240227091618-fee676b7da75/go.mod h1:G0cHiNH8Q4zdddEWosFlZDBjcphr8nlNSUMfz3iSzgo= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= From b8abc5a60190a1c9d495cba2cf633dd2dd464ca7 Mon Sep 17 00:00:00 2001 From: Nikos Date: Tue, 27 Feb 2024 14:31:36 +0200 Subject: [PATCH 8/8] chore: update sdk --- internal/httpclient/.openapi-generator/FILES | 4 + internal/httpclient/README.md | 2 + internal/httpclient/api/openapi.yaml | 68 ++++ internal/httpclient/api_v0alpha2.go | 133 ++++++++ internal/httpclient/client.go | 3 + .../httpclient/docs/DeviceAuthorization.md | 186 +++++++++++ internal/httpclient/docs/V0alpha2Api.md | 70 ++++ .../httpclient/model_device_authorization.go | 300 ++++++++++++++++++ oauth2/handler.go | 43 +++ spec/api.json | 71 +++++ spec/swagger.json | 33 +- 11 files changed, 912 insertions(+), 1 deletion(-) create mode 100644 internal/httpclient/api_v0alpha2.go create mode 100644 internal/httpclient/docs/DeviceAuthorization.md create mode 100644 internal/httpclient/docs/V0alpha2Api.md create mode 100644 internal/httpclient/model_device_authorization.go diff --git a/internal/httpclient/.openapi-generator/FILES b/internal/httpclient/.openapi-generator/FILES index 8fd9b406238..1794ade67de 100644 --- a/internal/httpclient/.openapi-generator/FILES +++ b/internal/httpclient/.openapi-generator/FILES @@ -7,6 +7,7 @@ api_jwk.go api_metadata.go api_o_auth2.go api_oidc.go +api_v0alpha2.go api_wellknown.go client.go configuration.go @@ -16,6 +17,7 @@ docs/AcceptOAuth2LoginRequest.md docs/CreateJsonWebKeySet.md docs/CreateVerifiableCredentialRequestBody.md docs/CredentialSupportedDraft00.md +docs/DeviceAuthorization.md docs/ErrorOAuth2.md docs/GenericError.md docs/GetVersion200Response.md @@ -54,6 +56,7 @@ docs/TokenPaginationResponseHeaders.md docs/TrustOAuth2JwtGrantIssuer.md docs/TrustedOAuth2JwtGrantIssuer.md docs/TrustedOAuth2JwtGrantJsonWebKey.md +docs/V0alpha2Api.md docs/VerifiableCredentialPrimingResponse.md docs/VerifiableCredentialProof.md docs/VerifiableCredentialResponse.md @@ -68,6 +71,7 @@ model_accept_o_auth2_login_request.go model_create_json_web_key_set.go model_create_verifiable_credential_request_body.go model_credential_supported_draft00.go +model_device_authorization.go model_error_o_auth2.go model_generic_error.go model_get_version_200_response.go diff --git a/internal/httpclient/README.md b/internal/httpclient/README.md index 54e38678e69..bd43a011263 100644 --- a/internal/httpclient/README.md +++ b/internal/httpclient/README.md @@ -125,6 +125,7 @@ Class | Method | HTTP request | Description *OidcApi* | [**GetOidcUserInfo**](docs/OidcApi.md#getoidcuserinfo) | **Get** /userinfo | OpenID Connect Userinfo *OidcApi* | [**RevokeOidcSession**](docs/OidcApi.md#revokeoidcsession) | **Get** /oauth2/sessions/logout | OpenID Connect Front- and Back-channel Enabled Logout *OidcApi* | [**SetOidcDynamicClient**](docs/OidcApi.md#setoidcdynamicclient) | **Put** /oauth2/register/{id} | Set OAuth2 Client using OpenID Dynamic Client Registration +*V0alpha2Api* | [**PerformOAuth2DeviceFlow**](docs/V0alpha2Api.md#performoauth2deviceflow) | **Post** /oauth2/device/auth | The OAuth 2.0 Device Authorize Endpoint *WellknownApi* | [**DiscoverJsonWebKeys**](docs/WellknownApi.md#discoverjsonwebkeys) | **Get** /.well-known/jwks.json | Discover Well-Known JSON Web Keys @@ -136,6 +137,7 @@ Class | Method | HTTP request | Description - [CreateJsonWebKeySet](docs/CreateJsonWebKeySet.md) - [CreateVerifiableCredentialRequestBody](docs/CreateVerifiableCredentialRequestBody.md) - [CredentialSupportedDraft00](docs/CredentialSupportedDraft00.md) + - [DeviceAuthorization](docs/DeviceAuthorization.md) - [ErrorOAuth2](docs/ErrorOAuth2.md) - [GenericError](docs/GenericError.md) - [GetVersion200Response](docs/GetVersion200Response.md) diff --git a/internal/httpclient/api/openapi.yaml b/internal/httpclient/api/openapi.yaml index 1d1de8ec753..abaa040aab2 100644 --- a/internal/httpclient/api/openapi.yaml +++ b/internal/httpclient/api/openapi.yaml @@ -1475,6 +1475,29 @@ paths: summary: OAuth 2.0 Authorize Endpoint tags: - oAuth2 + /oauth2/device/auth: + post: + description: "This endpoint is not documented here because you should never\ + \ use your own implementation to perform OAuth2 flows.\nOAuth2 is a very popular\ + \ protocol and a library for your programming language will exists.\n\nTo\ + \ learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc8628" + operationId: performOAuth2DeviceFlow + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/deviceAuthorization' + description: deviceAuthorization + default: + content: + application/json: + schema: + $ref: '#/components/schemas/errorOAuth2' + description: errorOAuth2 + summary: The OAuth 2.0 Device Authorize Endpoint + tags: + - v0alpha2 /oauth2/register: post: description: |- @@ -2086,6 +2109,51 @@ components: type: array title: Verifiable Credentials Metadata (Draft 00) type: object + deviceAuthorization: + description: '# Ory''s OAuth 2.0 Device Authorization API' + example: + user_code: AAAAAA + device_code: ory_dc_smldfksmdfkl.mslkmlkmlk + interval: 5 + verification_uri_complete: https://auth.ory.sh/tv?user_code=AAAAAA + verification_uri: https://auth.ory.sh/tv + expires_in: 16830 + properties: + device_code: + description: The device verification code. + example: ory_dc_smldfksmdfkl.mslkmlkmlk + type: string + expires_in: + description: The lifetime in seconds of the "device_code" and "user_code". + example: 16830 + format: int64 + type: integer + interval: + description: "The minimum amount of time in seconds that the client\nSHOULD\ + \ wait between polling requests to the token endpoint. If no\nvalue is\ + \ provided, clients MUST use 5 as the default." + example: 5 + format: int64 + type: integer + user_code: + description: The end-user verification code. + example: AAAAAA + type: string + verification_uri: + description: |- + The end-user verification URI on the authorization + server. The URI should be short and easy to remember as end users + will be asked to manually type it into their user agent. + example: https://auth.ory.sh/tv + type: string + verification_uri_complete: + description: "A verification URI that includes the \"user_code\" (or\nother\ + \ information with the same function as the \"user_code\"),\nwhich is\ + \ designed for non-textual transmission." + example: https://auth.ory.sh/tv?user_code=AAAAAA + type: string + title: OAuth2 Device Flow + type: object errorOAuth2: description: Error example: diff --git a/internal/httpclient/api_v0alpha2.go b/internal/httpclient/api_v0alpha2.go new file mode 100644 index 00000000000..3de6111ca03 --- /dev/null +++ b/internal/httpclient/api_v0alpha2.go @@ -0,0 +1,133 @@ +/* +Ory Hydra API + +Documentation for all of Ory Hydra's APIs. + +API version: +Contact: hi@ory.sh +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "bytes" + "context" + "io/ioutil" + "net/http" + "net/url" +) + +// V0alpha2ApiService V0alpha2Api service +type V0alpha2ApiService service + +type ApiPerformOAuth2DeviceFlowRequest struct { + ctx context.Context + ApiService *V0alpha2ApiService +} + +func (r ApiPerformOAuth2DeviceFlowRequest) Execute() (*DeviceAuthorization, *http.Response, error) { + return r.ApiService.PerformOAuth2DeviceFlowExecute(r) +} + +/* +PerformOAuth2DeviceFlow The OAuth 2.0 Device Authorize Endpoint + +This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows. +OAuth2 is a very popular protocol and a library for your programming language will exists. + +To learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc8628 + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiPerformOAuth2DeviceFlowRequest +*/ +func (a *V0alpha2ApiService) PerformOAuth2DeviceFlow(ctx context.Context) ApiPerformOAuth2DeviceFlowRequest { + return ApiPerformOAuth2DeviceFlowRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// +// @return DeviceAuthorization +func (a *V0alpha2ApiService) PerformOAuth2DeviceFlowExecute(r ApiPerformOAuth2DeviceFlowRequest) (*DeviceAuthorization, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *DeviceAuthorization + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "V0alpha2ApiService.PerformOAuth2DeviceFlow") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/oauth2/device/auth" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + var v ErrorOAuth2 + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/internal/httpclient/client.go b/internal/httpclient/client.go index fe7ccccad0b..2aea075c664 100644 --- a/internal/httpclient/client.go +++ b/internal/httpclient/client.go @@ -58,6 +58,8 @@ type APIClient struct { OidcApi *OidcApiService + V0alpha2Api *V0alpha2ApiService + WellknownApi *WellknownApiService } @@ -81,6 +83,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { c.MetadataApi = (*MetadataApiService)(&c.common) c.OAuth2Api = (*OAuth2ApiService)(&c.common) c.OidcApi = (*OidcApiService)(&c.common) + c.V0alpha2Api = (*V0alpha2ApiService)(&c.common) c.WellknownApi = (*WellknownApiService)(&c.common) return c diff --git a/internal/httpclient/docs/DeviceAuthorization.md b/internal/httpclient/docs/DeviceAuthorization.md new file mode 100644 index 00000000000..4ba933a4b24 --- /dev/null +++ b/internal/httpclient/docs/DeviceAuthorization.md @@ -0,0 +1,186 @@ +# DeviceAuthorization + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**DeviceCode** | Pointer to **string** | The device verification code. | [optional] +**ExpiresIn** | Pointer to **int64** | The lifetime in seconds of the \"device_code\" and \"user_code\". | [optional] +**Interval** | Pointer to **int64** | The minimum amount of time in seconds that the client SHOULD wait between polling requests to the token endpoint. If no value is provided, clients MUST use 5 as the default. | [optional] +**UserCode** | Pointer to **string** | The end-user verification code. | [optional] +**VerificationUri** | Pointer to **string** | The end-user verification URI on the authorization server. The URI should be short and easy to remember as end users will be asked to manually type it into their user agent. | [optional] +**VerificationUriComplete** | Pointer to **string** | A verification URI that includes the \"user_code\" (or other information with the same function as the \"user_code\"), which is designed for non-textual transmission. | [optional] + +## Methods + +### NewDeviceAuthorization + +`func NewDeviceAuthorization() *DeviceAuthorization` + +NewDeviceAuthorization instantiates a new DeviceAuthorization object +This constructor will assign default values to properties that have it defined, +and makes sure properties required by API are set, but the set of arguments +will change when the set of required properties is changed + +### NewDeviceAuthorizationWithDefaults + +`func NewDeviceAuthorizationWithDefaults() *DeviceAuthorization` + +NewDeviceAuthorizationWithDefaults instantiates a new DeviceAuthorization object +This constructor will only assign default values to properties that have it defined, +but it doesn't guarantee that properties required by API are set + +### GetDeviceCode + +`func (o *DeviceAuthorization) GetDeviceCode() string` + +GetDeviceCode returns the DeviceCode field if non-nil, zero value otherwise. + +### GetDeviceCodeOk + +`func (o *DeviceAuthorization) GetDeviceCodeOk() (*string, bool)` + +GetDeviceCodeOk returns a tuple with the DeviceCode field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetDeviceCode + +`func (o *DeviceAuthorization) SetDeviceCode(v string)` + +SetDeviceCode sets DeviceCode field to given value. + +### HasDeviceCode + +`func (o *DeviceAuthorization) HasDeviceCode() bool` + +HasDeviceCode returns a boolean if a field has been set. + +### GetExpiresIn + +`func (o *DeviceAuthorization) GetExpiresIn() int64` + +GetExpiresIn returns the ExpiresIn field if non-nil, zero value otherwise. + +### GetExpiresInOk + +`func (o *DeviceAuthorization) GetExpiresInOk() (*int64, bool)` + +GetExpiresInOk returns a tuple with the ExpiresIn field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetExpiresIn + +`func (o *DeviceAuthorization) SetExpiresIn(v int64)` + +SetExpiresIn sets ExpiresIn field to given value. + +### HasExpiresIn + +`func (o *DeviceAuthorization) HasExpiresIn() bool` + +HasExpiresIn returns a boolean if a field has been set. + +### GetInterval + +`func (o *DeviceAuthorization) GetInterval() int64` + +GetInterval returns the Interval field if non-nil, zero value otherwise. + +### GetIntervalOk + +`func (o *DeviceAuthorization) GetIntervalOk() (*int64, bool)` + +GetIntervalOk returns a tuple with the Interval field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetInterval + +`func (o *DeviceAuthorization) SetInterval(v int64)` + +SetInterval sets Interval field to given value. + +### HasInterval + +`func (o *DeviceAuthorization) HasInterval() bool` + +HasInterval returns a boolean if a field has been set. + +### GetUserCode + +`func (o *DeviceAuthorization) GetUserCode() string` + +GetUserCode returns the UserCode field if non-nil, zero value otherwise. + +### GetUserCodeOk + +`func (o *DeviceAuthorization) GetUserCodeOk() (*string, bool)` + +GetUserCodeOk returns a tuple with the UserCode field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetUserCode + +`func (o *DeviceAuthorization) SetUserCode(v string)` + +SetUserCode sets UserCode field to given value. + +### HasUserCode + +`func (o *DeviceAuthorization) HasUserCode() bool` + +HasUserCode returns a boolean if a field has been set. + +### GetVerificationUri + +`func (o *DeviceAuthorization) GetVerificationUri() string` + +GetVerificationUri returns the VerificationUri field if non-nil, zero value otherwise. + +### GetVerificationUriOk + +`func (o *DeviceAuthorization) GetVerificationUriOk() (*string, bool)` + +GetVerificationUriOk returns a tuple with the VerificationUri field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetVerificationUri + +`func (o *DeviceAuthorization) SetVerificationUri(v string)` + +SetVerificationUri sets VerificationUri field to given value. + +### HasVerificationUri + +`func (o *DeviceAuthorization) HasVerificationUri() bool` + +HasVerificationUri returns a boolean if a field has been set. + +### GetVerificationUriComplete + +`func (o *DeviceAuthorization) GetVerificationUriComplete() string` + +GetVerificationUriComplete returns the VerificationUriComplete field if non-nil, zero value otherwise. + +### GetVerificationUriCompleteOk + +`func (o *DeviceAuthorization) GetVerificationUriCompleteOk() (*string, bool)` + +GetVerificationUriCompleteOk returns a tuple with the VerificationUriComplete field if it's non-nil, zero value otherwise +and a boolean to check if the value has been set. + +### SetVerificationUriComplete + +`func (o *DeviceAuthorization) SetVerificationUriComplete(v string)` + +SetVerificationUriComplete sets VerificationUriComplete field to given value. + +### HasVerificationUriComplete + +`func (o *DeviceAuthorization) HasVerificationUriComplete() bool` + +HasVerificationUriComplete returns a boolean if a field has been set. + + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/internal/httpclient/docs/V0alpha2Api.md b/internal/httpclient/docs/V0alpha2Api.md new file mode 100644 index 00000000000..fa8afc05677 --- /dev/null +++ b/internal/httpclient/docs/V0alpha2Api.md @@ -0,0 +1,70 @@ +# \V0alpha2Api + +All URIs are relative to *http://localhost* + +Method | HTTP request | Description +------------- | ------------- | ------------- +[**PerformOAuth2DeviceFlow**](V0alpha2Api.md#PerformOAuth2DeviceFlow) | **Post** /oauth2/device/auth | The OAuth 2.0 Device Authorize Endpoint + + + +## PerformOAuth2DeviceFlow + +> DeviceAuthorization PerformOAuth2DeviceFlow(ctx).Execute() + +The OAuth 2.0 Device Authorize Endpoint + + + +### Example + +```go +package main + +import ( + "context" + "fmt" + "os" + openapiclient "./openapi" +) + +func main() { + + configuration := openapiclient.NewConfiguration() + apiClient := openapiclient.NewAPIClient(configuration) + resp, r, err := apiClient.V0alpha2Api.PerformOAuth2DeviceFlow(context.Background()).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `V0alpha2Api.PerformOAuth2DeviceFlow``: %v\n", err) + fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r) + } + // response from `PerformOAuth2DeviceFlow`: DeviceAuthorization + fmt.Fprintf(os.Stdout, "Response from `V0alpha2Api.PerformOAuth2DeviceFlow`: %v\n", resp) +} +``` + +### Path Parameters + +This endpoint does not need any parameter. + +### Other Parameters + +Other parameters are passed through a pointer to a apiPerformOAuth2DeviceFlowRequest struct via the builder pattern + + +### Return type + +[**DeviceAuthorization**](DeviceAuthorization.md) + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) +[[Back to Model list]](../README.md#documentation-for-models) +[[Back to README]](../README.md) + diff --git a/internal/httpclient/model_device_authorization.go b/internal/httpclient/model_device_authorization.go new file mode 100644 index 00000000000..8aa32ff623b --- /dev/null +++ b/internal/httpclient/model_device_authorization.go @@ -0,0 +1,300 @@ +/* +Ory Hydra API + +Documentation for all of Ory Hydra's APIs. + +API version: +Contact: hi@ory.sh +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// DeviceAuthorization # Ory's OAuth 2.0 Device Authorization API +type DeviceAuthorization struct { + // The device verification code. + DeviceCode *string `json:"device_code,omitempty"` + // The lifetime in seconds of the \"device_code\" and \"user_code\". + ExpiresIn *int64 `json:"expires_in,omitempty"` + // The minimum amount of time in seconds that the client SHOULD wait between polling requests to the token endpoint. If no value is provided, clients MUST use 5 as the default. + Interval *int64 `json:"interval,omitempty"` + // The end-user verification code. + UserCode *string `json:"user_code,omitempty"` + // The end-user verification URI on the authorization server. The URI should be short and easy to remember as end users will be asked to manually type it into their user agent. + VerificationUri *string `json:"verification_uri,omitempty"` + // A verification URI that includes the \"user_code\" (or other information with the same function as the \"user_code\"), which is designed for non-textual transmission. + VerificationUriComplete *string `json:"verification_uri_complete,omitempty"` +} + +// NewDeviceAuthorization instantiates a new DeviceAuthorization object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewDeviceAuthorization() *DeviceAuthorization { + this := DeviceAuthorization{} + return &this +} + +// NewDeviceAuthorizationWithDefaults instantiates a new DeviceAuthorization object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewDeviceAuthorizationWithDefaults() *DeviceAuthorization { + this := DeviceAuthorization{} + return &this +} + +// GetDeviceCode returns the DeviceCode field value if set, zero value otherwise. +func (o *DeviceAuthorization) GetDeviceCode() string { + if o == nil || o.DeviceCode == nil { + var ret string + return ret + } + return *o.DeviceCode +} + +// GetDeviceCodeOk returns a tuple with the DeviceCode field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DeviceAuthorization) GetDeviceCodeOk() (*string, bool) { + if o == nil || o.DeviceCode == nil { + return nil, false + } + return o.DeviceCode, true +} + +// HasDeviceCode returns a boolean if a field has been set. +func (o *DeviceAuthorization) HasDeviceCode() bool { + if o != nil && o.DeviceCode != nil { + return true + } + + return false +} + +// SetDeviceCode gets a reference to the given string and assigns it to the DeviceCode field. +func (o *DeviceAuthorization) SetDeviceCode(v string) { + o.DeviceCode = &v +} + +// GetExpiresIn returns the ExpiresIn field value if set, zero value otherwise. +func (o *DeviceAuthorization) GetExpiresIn() int64 { + if o == nil || o.ExpiresIn == nil { + var ret int64 + return ret + } + return *o.ExpiresIn +} + +// GetExpiresInOk returns a tuple with the ExpiresIn field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DeviceAuthorization) GetExpiresInOk() (*int64, bool) { + if o == nil || o.ExpiresIn == nil { + return nil, false + } + return o.ExpiresIn, true +} + +// HasExpiresIn returns a boolean if a field has been set. +func (o *DeviceAuthorization) HasExpiresIn() bool { + if o != nil && o.ExpiresIn != nil { + return true + } + + return false +} + +// SetExpiresIn gets a reference to the given int64 and assigns it to the ExpiresIn field. +func (o *DeviceAuthorization) SetExpiresIn(v int64) { + o.ExpiresIn = &v +} + +// GetInterval returns the Interval field value if set, zero value otherwise. +func (o *DeviceAuthorization) GetInterval() int64 { + if o == nil || o.Interval == nil { + var ret int64 + return ret + } + return *o.Interval +} + +// GetIntervalOk returns a tuple with the Interval field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DeviceAuthorization) GetIntervalOk() (*int64, bool) { + if o == nil || o.Interval == nil { + return nil, false + } + return o.Interval, true +} + +// HasInterval returns a boolean if a field has been set. +func (o *DeviceAuthorization) HasInterval() bool { + if o != nil && o.Interval != nil { + return true + } + + return false +} + +// SetInterval gets a reference to the given int64 and assigns it to the Interval field. +func (o *DeviceAuthorization) SetInterval(v int64) { + o.Interval = &v +} + +// GetUserCode returns the UserCode field value if set, zero value otherwise. +func (o *DeviceAuthorization) GetUserCode() string { + if o == nil || o.UserCode == nil { + var ret string + return ret + } + return *o.UserCode +} + +// GetUserCodeOk returns a tuple with the UserCode field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DeviceAuthorization) GetUserCodeOk() (*string, bool) { + if o == nil || o.UserCode == nil { + return nil, false + } + return o.UserCode, true +} + +// HasUserCode returns a boolean if a field has been set. +func (o *DeviceAuthorization) HasUserCode() bool { + if o != nil && o.UserCode != nil { + return true + } + + return false +} + +// SetUserCode gets a reference to the given string and assigns it to the UserCode field. +func (o *DeviceAuthorization) SetUserCode(v string) { + o.UserCode = &v +} + +// GetVerificationUri returns the VerificationUri field value if set, zero value otherwise. +func (o *DeviceAuthorization) GetVerificationUri() string { + if o == nil || o.VerificationUri == nil { + var ret string + return ret + } + return *o.VerificationUri +} + +// GetVerificationUriOk returns a tuple with the VerificationUri field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DeviceAuthorization) GetVerificationUriOk() (*string, bool) { + if o == nil || o.VerificationUri == nil { + return nil, false + } + return o.VerificationUri, true +} + +// HasVerificationUri returns a boolean if a field has been set. +func (o *DeviceAuthorization) HasVerificationUri() bool { + if o != nil && o.VerificationUri != nil { + return true + } + + return false +} + +// SetVerificationUri gets a reference to the given string and assigns it to the VerificationUri field. +func (o *DeviceAuthorization) SetVerificationUri(v string) { + o.VerificationUri = &v +} + +// GetVerificationUriComplete returns the VerificationUriComplete field value if set, zero value otherwise. +func (o *DeviceAuthorization) GetVerificationUriComplete() string { + if o == nil || o.VerificationUriComplete == nil { + var ret string + return ret + } + return *o.VerificationUriComplete +} + +// GetVerificationUriCompleteOk returns a tuple with the VerificationUriComplete field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DeviceAuthorization) GetVerificationUriCompleteOk() (*string, bool) { + if o == nil || o.VerificationUriComplete == nil { + return nil, false + } + return o.VerificationUriComplete, true +} + +// HasVerificationUriComplete returns a boolean if a field has been set. +func (o *DeviceAuthorization) HasVerificationUriComplete() bool { + if o != nil && o.VerificationUriComplete != nil { + return true + } + + return false +} + +// SetVerificationUriComplete gets a reference to the given string and assigns it to the VerificationUriComplete field. +func (o *DeviceAuthorization) SetVerificationUriComplete(v string) { + o.VerificationUriComplete = &v +} + +func (o DeviceAuthorization) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.DeviceCode != nil { + toSerialize["device_code"] = o.DeviceCode + } + if o.ExpiresIn != nil { + toSerialize["expires_in"] = o.ExpiresIn + } + if o.Interval != nil { + toSerialize["interval"] = o.Interval + } + if o.UserCode != nil { + toSerialize["user_code"] = o.UserCode + } + if o.VerificationUri != nil { + toSerialize["verification_uri"] = o.VerificationUri + } + if o.VerificationUriComplete != nil { + toSerialize["verification_uri_complete"] = o.VerificationUriComplete + } + return json.Marshal(toSerialize) +} + +type NullableDeviceAuthorization struct { + value *DeviceAuthorization + isSet bool +} + +func (v NullableDeviceAuthorization) Get() *DeviceAuthorization { + return v.value +} + +func (v *NullableDeviceAuthorization) Set(val *DeviceAuthorization) { + v.value = val + v.isSet = true +} + +func (v NullableDeviceAuthorization) IsSet() bool { + return v.isSet +} + +func (v *NullableDeviceAuthorization) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableDeviceAuthorization(val *DeviceAuthorization) *NullableDeviceAuthorization { + return &NullableDeviceAuthorization{value: val, isSet: true} +} + +func (v NullableDeviceAuthorization) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableDeviceAuthorization) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/oauth2/handler.go b/oauth2/handler.go index 2a51d14d400..b0a0e44f07c 100644 --- a/oauth2/handler.go +++ b/oauth2/handler.go @@ -692,6 +692,49 @@ func (h *Handler) getOidcUserInfo(w http.ResponseWriter, r *http.Request) { } } +// OAuth2 Device Flow +// +// # Ory's OAuth 2.0 Device Authorization API +// +// swagger:model deviceAuthorization +type deviceAuthorization struct { + // The device verification code. + // + // example: ory_dc_smldfksmdfkl.mslkmlkmlk + DeviceCode string `json:"device_code"` + + // The end-user verification code. + // + // example: AAAAAA + UserCode string `json:"user_code"` + + // The end-user verification URI on the authorization + // server. The URI should be short and easy to remember as end users + // will be asked to manually type it into their user agent. + // + // example: https://auth.ory.sh/tv + VerificationUri string `json:"verification_uri"` + + // A verification URI that includes the "user_code" (or + // other information with the same function as the "user_code"), + // which is designed for non-textual transmission. + // + // example: https://auth.ory.sh/tv?user_code=AAAAAA + VerificationUriComplete string `json:"verification_uri_complete"` + + // The lifetime in seconds of the "device_code" and "user_code". + // + // example: 16830 + ExpiresIn int `json:"expires_in"` + + // The minimum amount of time in seconds that the client + // SHOULD wait between polling requests to the token endpoint. If no + // value is provided, clients MUST use 5 as the default. + // + // example: 5 + Interval int `json:"interval"` +} + // swagger:route POST /oauth2/device/auth v0alpha2 performOAuth2DeviceFlow // // # The OAuth 2.0 Device Authorize Endpoint diff --git a/spec/api.json b/spec/api.json index c0b4fbdd7d4..111cc6133fa 100644 --- a/spec/api.json +++ b/spec/api.json @@ -286,6 +286,45 @@ "title": "Verifiable Credentials Metadata (Draft 00)", "type": "object" }, + "deviceAuthorization": { + "description": "# Ory's OAuth 2.0 Device Authorization API", + "properties": { + "device_code": { + "description": "The device verification code.", + "example": "ory_dc_smldfksmdfkl.mslkmlkmlk", + "type": "string" + }, + "expires_in": { + "description": "The lifetime in seconds of the \"device_code\" and \"user_code\".", + "example": 16830, + "format": "int64", + "type": "integer" + }, + "interval": { + "description": "The minimum amount of time in seconds that the client\nSHOULD wait between polling requests to the token endpoint. If no\nvalue is provided, clients MUST use 5 as the default.", + "example": 5, + "format": "int64", + "type": "integer" + }, + "user_code": { + "description": "The end-user verification code.", + "example": "AAAAAA", + "type": "string" + }, + "verification_uri": { + "description": "The end-user verification URI on the authorization\nserver. The URI should be short and easy to remember as end users\nwill be asked to manually type it into their user agent.", + "example": "https://auth.ory.sh/tv", + "type": "string" + }, + "verification_uri_complete": { + "description": "A verification URI that includes the \"user_code\" (or\nother information with the same function as the \"user_code\"),\nwhich is designed for non-textual transmission.", + "example": "https://auth.ory.sh/tv?user_code=AAAAAA", + "type": "string" + } + }, + "title": "OAuth2 Device Flow", + "type": "object" + }, "errorOAuth2": { "description": "Error", "properties": { @@ -3460,6 +3499,38 @@ ] } }, + "/oauth2/device/auth": { + "post": { + "description": "This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows.\nOAuth2 is a very popular protocol and a library for your programming language will exists.\n\nTo learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc8628", + "operationId": "performOAuth2DeviceFlow", + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/deviceAuthorization" + } + } + }, + "description": "deviceAuthorization" + }, + "default": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/errorOAuth2" + } + } + }, + "description": "errorOAuth2" + } + }, + "summary": "The OAuth 2.0 Device Authorize Endpoint", + "tags": [ + "v0alpha2" + ] + } + }, "/oauth2/register": { "post": { "description": "This endpoint behaves like the administrative counterpart (`createOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nPlease note that using this endpoint you are not able to choose the `client_secret` nor the `client_id` as those\nvalues will be server generated when specifying `token_endpoint_auth_method` as `client_secret_basic` or\n`client_secret_post`.\n\nThe `client_secret` will be returned in the response and you will not be able to retrieve it later on.\nWrite the secret down and keep it somewhere safe.", diff --git a/spec/swagger.json b/spec/swagger.json index ee15a6687d0..57a40ff2edf 100755 --- a/spec/swagger.json +++ b/spec/swagger.json @@ -1713,6 +1713,37 @@ } } }, + "/oauth2/device/auth": { + "post": { + "description": "This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows.\nOAuth2 is a very popular protocol and a library for your programming language will exists.\n\nTo learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc8628", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "v0alpha2" + ], + "summary": "The OAuth 2.0 Device Authorize Endpoint", + "operationId": "performOAuth2DeviceFlow", + "responses": { + "200": { + "description": "deviceAuthorization", + "schema": { + "$ref": "#/definitions/deviceAuthorization" + } + }, + "default": { + "description": "errorOAuth2", + "schema": { + "$ref": "#/definitions/errorOAuth2" + } + } + } + } + }, "/oauth2/register": { "post": { "description": "This endpoint behaves like the administrative counterpart (`createOAuth2Client`) but is capable of facing the\npublic internet directly and can be used in self-service. It implements the OpenID Connect\nDynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint\nis disabled by default. It can be enabled by an administrator.\n\nPlease note that using this endpoint you are not able to choose the `client_secret` nor the `client_id` as those\nvalues will be server generated when specifying `token_endpoint_auth_method` as `client_secret_basic` or\n`client_secret_post`.\n\nThe `client_secret` will be returned in the response and you will not be able to retrieve it later on.\nWrite the secret down and keep it somewhere safe.", @@ -2312,7 +2343,7 @@ } }, "deviceAuthorization": { - "description": "OAuth 2.0 Device Authorization endpoint", + "description": "# Ory's OAuth 2.0 Device Authorization API", "type": "object", "title": "OAuth2 Device Flow", "properties": {