diff --git a/Gopkg.lock b/Gopkg.lock index aeae22d4f8..4f76e1ee78 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -304,8 +304,8 @@ "token/hmac", "token/jwt" ] - revision = "7820fb2e380ca737277095876c7f91b5ebee1467" - version = "v0.21.1" + revision = "0fcbfea741d0f0bb2a96d5fa08a2797a109a4a33" + version = "v0.21.2" [[projects]] name = "github.com/ory/go-convenience" @@ -637,6 +637,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "420e8699174d9d75569d5f9c42206487ef0e232c1147444d2380fd05ab344add" + inputs-digest = "700a36282813a0278a88f04f52beabb3d91b479c4333be0521e132425e0da2bd" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 47994818dc..9ef5612aa8 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -75,7 +75,7 @@ [[constraint]] name = "github.com/ory/fosite" - version = "0.21.1" + version = "0.21.2" [[constraint]] name = "github.com/ory/graceful" diff --git a/client/sdk_test.go b/client/sdk_test.go index 3a48b992e7..f68715022c 100644 --- a/client/sdk_test.go +++ b/client/sdk_test.go @@ -53,6 +53,7 @@ func createTestClient(prefix string) hydra.OAuth2Client { ClientSecretExpiresAt: 0, TokenEndpointAuthMethod: "client_secret_basic", UserinfoSignedResponseAlg: "none", + SubjectType: "public", //SectorIdentifierUri: "https://sector.com/foo", } } diff --git a/client/validator.go b/client/validator.go index b563a95a60..0301f38805 100644 --- a/client/validator.go +++ b/client/validator.go @@ -111,7 +111,7 @@ func (v *Validator) Validate(c *Client) error { return errors.WithStack(fosite.ErrInvalidRequest.WithHint(fmt.Sprintf("Subject type %s is not supported by server, only %v are allowed.", c.SubjectType, v.SubjectTypes))) } } else { - if !stringslice.Has(v.SubjectTypes, "public") { + if stringslice.Has(v.SubjectTypes, "public") { c.SubjectType = "public" } else { c.SubjectType = v.SubjectTypes[0] diff --git a/client/validator_test.go b/client/validator_test.go index a347a6bceb..d2c064c204 100644 --- a/client/validator_test.go +++ b/client/validator_test.go @@ -34,12 +34,13 @@ import ( func TestValidate(t *testing.T) { v := &Validator{ DefaultClientScopes: []string{"openid"}, - SubjectTypes: []string{"public", "pairwise"}, + SubjectTypes: []string{"pairwise", "public"}, } for k, tc := range []struct { in *Client check func(t *testing.T, c *Client) expectErr bool + v *Validator }{ { in: new(Client), @@ -79,6 +80,16 @@ func TestValidate(t *testing.T) { assert.Equal(t, "public", c.SubjectType) }, }, + { + v: &Validator{ + DefaultClientScopes: []string{"openid"}, + SubjectTypes: []string{"pairwise"}, + }, + in: &Client{ClientID: "foo"}, + check: func(t *testing.T, c *Client) { + assert.Equal(t, "pairwise", c.SubjectType) + }, + }, { in: &Client{ClientID: "foo", SubjectType: "pairwise"}, check: func(t *testing.T, c *Client) { @@ -91,7 +102,10 @@ func TestValidate(t *testing.T) { }, } { t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) { - err := v.Validate(tc.in) + if tc.v == nil { + tc.v = v + } + err := tc.v.Validate(tc.in) if tc.expectErr { require.Error(t, err) } else { diff --git a/cmd/cli/handler_client.go b/cmd/cli/handler_client.go index c0d74b2499..c28190b015 100644 --- a/cmd/cli/handler_client.go +++ b/cmd/cli/handler_client.go @@ -100,6 +100,7 @@ func (h *ClientHandler) CreateClient(cmd *cobra.Command, args []string) { policyUri, _ := cmd.Flags().GetString("policy-uri") logoUri, _ := cmd.Flags().GetString("logo-uri") clientUri, _ := cmd.Flags().GetString("client-uri") + subjectType, _ := cmd.Flags().GetString("subject-type") if secret == "" { var secretb []byte @@ -124,6 +125,7 @@ func (h *ClientHandler) CreateClient(cmd *cobra.Command, args []string) { PolicyUri: policyUri, LogoUri: logoUri, ClientUri: clientUri, + SubjectType: subjectType, } result, response, err := m.CreateOAuth2Client(cc) diff --git a/cmd/clients_create.go b/cmd/clients_create.go index ec666b8047..a557f3c8ee 100644 --- a/cmd/clients_create.go +++ b/cmd/clients_create.go @@ -53,6 +53,7 @@ func init() { clientsCreateCmd.Flags().String("tos-uri", "", "A URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client") clientsCreateCmd.Flags().String("client-uri", "", "A URL string of a web page providing information about the client") clientsCreateCmd.Flags().String("logo-uri", "", "A URL string that references a logo for the client") + clientsCreateCmd.Flags().String("subject-type", "public", "A URL string that references a logo for the client") clientsCreateCmd.Flags().String("secret", "", "Provide the client's secret") clientsCreateCmd.Flags().StringP("name", "n", "", "The client's name") } diff --git a/cmd/root.go b/cmd/root.go index a7e8327355..eae71de929 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -191,6 +191,9 @@ func initConfig() { viper.BindEnv("OIDC_SUBJECT_TYPES_SUPPORTED") viper.SetDefault("OIDC_SUBJECT_TYPES_SUPPORTED", "public") + viper.BindEnv("OIDC_SUBJECT_TYPE_PAIRWISE_SALT") + viper.SetDefault("OIDC_SUBJECT_TYPE_PAIRWISE_SALT", "public") + // If a config file is found, read it in. if err := viper.ReadInConfig(); err != nil { fmt.Printf(`Config file not found because "%s"`, err) diff --git a/cmd/serve.go b/cmd/serve.go index 110527e362..c34594cef3 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -140,10 +140,20 @@ OPENID CONNECT CONTROLS "scope" key in the registration payload, effectively disabling the concept of whitelisted scopes. Example: OIDC_DYNAMIC_CLIENT_REGISTRATION_DEFAULT_SCOPE=openid,offline,scope-a,scope-b -- OIDC_SUBJECT_TYPES_SUPPORTED: Sets which pairwise identifier algorithms (comma-separated) should be supported. - Can be "public" or "pairwise" or both. Defaults to "public". +- OIDC_SUBJECT_TYPES_SUPPORTED: Sets which identifier algorithms (comma-separated) should be supported. + Can be "public" or "pairwise" or both. Defaults to "public". Please note that "pairwise" does not work with the + JWT OAuth 2.0 Access Token Strategy. Example: OIDC_SUBJECT_TYPES_SUPPORTED=public,pairwise +- OIDC_SUBJECT_TYPE_PAIRWISE_SALT: Is the salt of the pairwise identifier algorithm and must be set if pairwise is enabled. + The length must be longer than 8 characters. + + !! Warning !! + This value should not be changed once set in production. Changing it will cause all client applications + to receive new user IDs from ORY Hydra which will lead to serious complications with authentication on their side! + + Example: OIDC_SUBJECT_TYPE_PAIRWISE_SALT=5be780ef690045aebf50845d56acd72c + HTTPS CONTROLS ============== diff --git a/cmd/server/handler.go b/cmd/server/handler.go index 950dcfa3fa..a6717e0189 100644 --- a/cmd/server/handler.go +++ b/cmd/server/handler.go @@ -62,6 +62,7 @@ func enhanceRouter(c *config.Config, cmd *cobra.Command, serverHandler *Handler, func RunServeAdmin(c *config.Config) func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { + c.MustValidate() checkDatabaseAllowed(c) serverHandler, _, backend, mws := setup(c, cmd, args, "admin") @@ -78,6 +79,7 @@ func RunServeAdmin(c *config.Config) func(cmd *cobra.Command, args []string) { func RunServePublic(c *config.Config) func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { + c.MustValidate() checkDatabaseAllowed(c) serverHandler, frontend, _, mws := setup(c, cmd, args, "public") @@ -94,6 +96,7 @@ func RunServePublic(c *config.Config) func(cmd *cobra.Command, args []string) { func RunServeAll(c *config.Config) func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { + c.MustValidate() serverHandler, frontend, backend, mws := setup(c, cmd, args, "all") var wg sync.WaitGroup diff --git a/cmd/server/handler_oauth2_factory.go b/cmd/server/handler_oauth2_factory.go index 9dffd7dfdc..7ad00d9851 100644 --- a/cmd/server/handler_oauth2_factory.go +++ b/cmd/server/handler_oauth2_factory.go @@ -32,6 +32,7 @@ import ( "github.com/ory/fosite/compose" foauth2 "github.com/ory/fosite/handler/oauth2" "github.com/ory/fosite/handler/openid" + "github.com/ory/go-convenience/stringslice" "github.com/ory/herodot" "github.com/ory/hydra/client" "github.com/ory/hydra/config" @@ -170,6 +171,14 @@ func newOAuth2Handler(c *config.Config, frontend, backend *httprouter.Router, cm } } + sias := map[string]consent.SubjectIdentifierAlgorithm{} + if stringslice.Has(c.GetSubjectTypesSupported(), "pairwise") { + sias["pairwise"] = consent.NewSubjectIdentifierAlgorithmPairwise([]byte(c.SubjectIdentifierAlgorithmSalt)) + } + if stringslice.Has(c.GetSubjectTypesSupported(), "pairwise") { + sias["public"] = consent.NewSubjectIdentifierAlgorithmPublic() + } + handler := &oauth2.Handler{ ScopesSupported: c.OpenIDDiscoveryScopesSupported, UserinfoEndpoint: c.OpenIDDiscoveryUserinfoEndpoint, @@ -184,6 +193,7 @@ func newOAuth2Handler(c *config.Config, frontend, backend *httprouter.Router, cm !c.ForceHTTP, time.Minute*15, oidcStrategy, openid.NewOpenIDConnectRequestValidator(nil, oidcStrategy), + sias, ), Storage: c.Context().FositeStore, ErrorURL: *errorURL, diff --git a/config/config.go b/config/config.go index 5bc347afda..58ae544734 100644 --- a/config/config.go +++ b/config/config.go @@ -36,6 +36,7 @@ import ( "github.com/ory/fosite" foauth2 "github.com/ory/fosite/handler/oauth2" "github.com/ory/fosite/token/hmac" + "github.com/ory/go-convenience/stringslice" "github.com/ory/go-convenience/urlx" "github.com/ory/hydra/health" "github.com/ory/hydra/metrics/prometheus" @@ -63,7 +64,6 @@ type Config struct { ConsentURL string `mapstructure:"OAUTH2_CONSENT_URL" yaml:"-"` LoginURL string `mapstructure:"OAUTH2_LOGIN_URL" yaml:"-"` DefaultClientScope string `mapstructure:"OIDC_DYNAMIC_CLIENT_REGISTRATION_DEFAULT_SCOPE" yaml:"-"` - SubjectTypesSupported string `mapstructure:"OIDC_SUBJECT_TYPES_SUPPORTED" yaml:"-"` ErrorURL string `mapstructure:"OAUTH2_ERROR_URL" yaml:"-"` AllowTLSTermination string `mapstructure:"HTTPS_ALLOW_TERMINATION_FROM" yaml:"-"` BCryptWorkFactor int `mapstructure:"BCRYPT_COST" yaml:"-"` @@ -76,6 +76,8 @@ type Config struct { LogLevel string `mapstructure:"LOG_LEVEL" yaml:"-"` LogFormat string `mapstructure:"LOG_FORMAT" yaml:"-"` AccessControlResourcePrefix string `mapstructure:"RESOURCE_NAME_PREFIX" yaml:"-"` + SubjectTypesSupported string `mapstructure:"OIDC_SUBJECT_TYPES_SUPPORTED" yaml:"-"` + SubjectIdentifierAlgorithmSalt string `mapstructure:"OIDC_SUBJECT_TYPE_PAIRWISE_SALT" yaml:"-"` OpenIDDiscoveryClaimsSupported string `mapstructure:"OIDC_DISCOVERY_CLAIMS_SUPPORTED" yaml:"-"` OpenIDDiscoveryScopesSupported string `mapstructure:"OIDC_DISCOVERY_SCOPES_SUPPORTED" yaml:"-"` OpenIDDiscoveryUserinfoEndpoint string `mapstructure:"OIDC_DISCOVERY_USERINFO_ENDPOINT" yaml:"-"` @@ -94,6 +96,16 @@ type Config struct { systemSecret []byte `yaml:"-"` } +func (c *Config) MustValidate() { + if stringslice.Has(c.GetSubjectTypesSupported(), "pairwise") && c.OAuth2AccessTokenStrategy == "jwt" { + c.logger.Fatalf(`The pairwise subject identifier algorithm is not supported by the JWT OAuth 2.0 Access Token Strategy. Please remove "pairwise" from OIDC_SUBJECT_TYPES_SUPPORTED or set OAUTH2_ACCESS_TOKEN_STRATEGY to "opaque"`) + } + + if stringslice.Has(c.GetSubjectTypesSupported(), "pairwise") && len(c.SubjectIdentifierAlgorithmSalt) < 8 { + c.logger.Fatalf(`The pairwise subject identifier algorithm was set but length of OIDC_SUBJECT_TYPE_PAIRWISE_SALT is too small (%d < 8), please set OIDC_SUBJECT_TYPE_PAIRWISE_SALT to a random string with 8 characters or more`, len(c.SubjectIdentifierAlgorithmSalt)) + } +} + func (c *Config) GetSubjectTypesSupported() []string { types := strings.Split(c.SubjectTypesSupported, ",") if len(types) == 0 { diff --git a/consent/manager.go b/consent/manager.go index 2e9fd37ee4..4d0dc0fb4e 100644 --- a/consent/manager.go +++ b/consent/manager.go @@ -20,6 +20,12 @@ package consent +type ForcedObfuscatedAuthenticationSession struct { + ClientID string `db:"client_id"` + Subject string `db:"subject"` + SubjectObfuscated string `db:"subject_obfuscated"` +} + type Manager interface { CreateConsentRequest(*ConsentRequest) error GetConsentRequest(challenge string) (*ConsentRequest, error) @@ -41,4 +47,7 @@ type Manager interface { GetAuthenticationRequest(challenge string) (*AuthenticationRequest, error) HandleAuthenticationRequest(challenge string, r *HandledAuthenticationRequest) (*AuthenticationRequest, error) VerifyAndInvalidateAuthenticationRequest(verifier string) (*HandledAuthenticationRequest, error) + + CreateForcedObfuscatedAuthenticationSession(*ForcedObfuscatedAuthenticationSession) error + GetForcedObfuscatedAuthenticationSession(client, obfuscated string) (*ForcedObfuscatedAuthenticationSession, error) } diff --git a/consent/manager_memory.go b/consent/manager_memory.go index 46572608e9..f9f9c7eac2 100644 --- a/consent/manager_memory.go +++ b/consent/manager_memory.go @@ -36,6 +36,7 @@ type MemoryManager struct { authRequests map[string]AuthenticationRequest handledAuthRequests map[string]HandledAuthenticationRequest authSessions map[string]AuthenticationSession + pairwise []ForcedObfuscatedAuthenticationSession m map[string]*sync.RWMutex store pkg.FositeStorer } @@ -47,6 +48,7 @@ func NewMemoryManager(store pkg.FositeStorer) *MemoryManager { authRequests: map[string]AuthenticationRequest{}, handledAuthRequests: map[string]HandledAuthenticationRequest{}, authSessions: map[string]AuthenticationSession{}, + pairwise: []ForcedObfuscatedAuthenticationSession{}, store: store, m: map[string]*sync.RWMutex{ "consentRequests": new(sync.RWMutex), @@ -58,6 +60,28 @@ func NewMemoryManager(store pkg.FositeStorer) *MemoryManager { } } +func (m *MemoryManager) CreateForcedObfuscatedAuthenticationSession(s *ForcedObfuscatedAuthenticationSession) error { + for k, v := range m.pairwise { + if v.Subject == s.Subject && v.ClientID == s.ClientID { + m.pairwise[k] = *s + return nil + } + } + + m.pairwise = append(m.pairwise, *s) + return nil +} + +func (m *MemoryManager) GetForcedObfuscatedAuthenticationSession(client, obfuscated string) (*ForcedObfuscatedAuthenticationSession, error) { + for _, v := range m.pairwise { + if v.SubjectObfuscated == obfuscated && v.ClientID == client { + return &v, nil + } + } + + return nil, errors.WithStack(pkg.ErrNotFound) +} + func (m *MemoryManager) RevokeUserConsentSession(user string) error { return m.RevokeUserClientConsentSession(user, "") } diff --git a/consent/manager_sql.go b/consent/manager_sql.go index 58f1e7c159..ffe17921dd 100644 --- a/consent/manager_sql.go +++ b/consent/manager_sql.go @@ -150,6 +150,52 @@ func (m *SQLManager) RevokeUserAuthenticationSession(user string) error { return nil } +func (m *SQLManager) CreateForcedObfuscatedAuthenticationSession(s *ForcedObfuscatedAuthenticationSession) error { + tx, err := m.db.Beginx() + if err != nil { + return sqlcon.HandleError(err) + } + + if _, err := m.db.Exec( + m.db.Rebind("DELETE FROM hydra_oauth2_obfuscated_authentication_session WHERE client_id=? AND subject=?"), + s.ClientID, + s.Subject, + ); err != nil { + if err := tx.Rollback(); err != nil { + return sqlcon.HandleError(err) + } + return sqlcon.HandleError(err) + } + + if _, err := m.db.NamedExec( + "INSERT INTO hydra_oauth2_obfuscated_authentication_session (subject, client_id, subject_obfuscated) VALUES (:subject, :client_id, :subject_obfuscated)", + s, + ); err != nil { + if err := tx.Rollback(); err != nil { + return sqlcon.HandleError(err) + } + return sqlcon.HandleError(err) + } + + if err := tx.Commit(); err != nil { + return sqlcon.HandleError(err) + } + return nil +} + +func (m *SQLManager) GetForcedObfuscatedAuthenticationSession(client, obfuscated string) (*ForcedObfuscatedAuthenticationSession, error) { + var d ForcedObfuscatedAuthenticationSession + + if err := m.db.Get(&d, m.db.Rebind("SELECT * FROM hydra_oauth2_obfuscated_authentication_session WHERE client_id=? AND subject_obfuscated=?"), client, obfuscated); err != nil { + if err == sql.ErrNoRows { + return nil, errors.WithStack(pkg.ErrNotFound) + } + return nil, sqlcon.HandleError(err) + } + + return &d, nil +} + func (m *SQLManager) CreateConsentRequest(c *ConsentRequest) error { d, err := newSQLConsentRequest(c) if err != nil { @@ -158,8 +204,8 @@ func (m *SQLManager) CreateConsentRequest(c *ConsentRequest) error { if _, err := m.db.NamedExec(fmt.Sprintf( "INSERT INTO hydra_oauth2_consent_request (%s) VALUES (%s)", - strings.Join(sqlParamsRequest, ", "), - ":"+strings.Join(sqlParamsRequest, ", :"), + strings.Join(sqlParamsConsentRequest, ", "), + ":"+strings.Join(sqlParamsConsentRequest, ", :"), ), d); err != nil { return sqlcon.HandleError(err) } @@ -168,7 +214,7 @@ func (m *SQLManager) CreateConsentRequest(c *ConsentRequest) error { } func (m *SQLManager) GetConsentRequest(challenge string) (*ConsentRequest, error) { - var d sqlRequest + var d sqlConsentRequest if err := m.db.Get(&d, m.db.Rebind("SELECT * FROM hydra_oauth2_consent_request WHERE challenge=?"), challenge); err != nil { if err == sql.ErrNoRows { @@ -193,8 +239,8 @@ func (m *SQLManager) CreateAuthenticationRequest(c *AuthenticationRequest) error if _, err := m.db.NamedExec(fmt.Sprintf( "INSERT INTO hydra_oauth2_authentication_request (%s) VALUES (%s)", - strings.Join(sqlParamsRequest, ", "), - ":"+strings.Join(sqlParamsRequest, ", :"), + strings.Join(sqlParamsAuthenticationRequest, ", "), + ":"+strings.Join(sqlParamsAuthenticationRequest, ", :"), ), d); err != nil { return sqlcon.HandleError(err) } @@ -203,7 +249,7 @@ func (m *SQLManager) CreateAuthenticationRequest(c *AuthenticationRequest) error } func (m *SQLManager) GetAuthenticationRequest(challenge string) (*AuthenticationRequest, error) { - var d sqlRequest + var d sqlConsentRequest if err := m.db.Get(&d, m.db.Rebind("SELECT * FROM hydra_oauth2_authentication_request WHERE challenge=?"), challenge); err != nil { if err == sql.ErrNoRows { diff --git a/consent/manager_test.go b/consent/manager_test.go index 867989ccc9..045bd458a5 100644 --- a/consent/manager_test.go +++ b/consent/manager_test.go @@ -63,6 +63,8 @@ func mockConsentRequest(key string, remember bool, rememberFor int, hasError boo RequestedScope: []string{"scopea" + key, "scopeb" + key}, Verifier: "verifier" + key, CSRF: "csrf" + key, + ForceSubjectIdentifier: "forced-subject", + SubjectIdentifier: "forced-subject", } var err *RequestDeniedError @@ -126,16 +128,17 @@ func mockAuthRequest(key string, authAt bool) (c *AuthenticationRequest, h *Hand } h = &HandledAuthenticationRequest{ - AuthenticationRequest: c, - RememberFor: 120, - Remember: true, - Challenge: "challenge" + key, - RequestedAt: time.Now().UTC().Add(-time.Minute), - AuthenticatedAt: authenticatedAt, - Error: err, - Subject: c.Subject, - ACR: "acr", - WasUsed: false, + AuthenticationRequest: c, + RememberFor: 120, + Remember: true, + Challenge: "challenge" + key, + RequestedAt: time.Now().UTC().Add(-time.Minute), + AuthenticatedAt: authenticatedAt, + Error: err, + Subject: c.Subject, + ACR: "acr", + WasUsed: false, + ForceSubjectIdentifier: "forced-subject", } return c, h @@ -540,6 +543,39 @@ func TestManagers(t *testing.T) { }) } + t.Run("case=obfuscated", func(t *testing.T) { + for k, m := range managers { + t.Run(fmt.Sprintf("manager=%s", k), func(t *testing.T) { + got, err := m.GetForcedObfuscatedAuthenticationSession("client-1", "obfuscated-1") + require.EqualError(t, err, pkg.ErrNotFound.Error()) + + expect := &ForcedObfuscatedAuthenticationSession{ + ClientID: "client-1", + Subject: "subject-1", + SubjectObfuscated: "obfuscated-1", + } + require.NoError(t, m.CreateForcedObfuscatedAuthenticationSession(expect)) + + got, err = m.GetForcedObfuscatedAuthenticationSession("client-1", "obfuscated-1") + require.NoError(t, err) + assert.EqualValues(t, expect, got) + + expect = &ForcedObfuscatedAuthenticationSession{ + ClientID: "client-1", + Subject: "subject-1", + SubjectObfuscated: "obfuscated-2", + } + require.NoError(t, m.CreateForcedObfuscatedAuthenticationSession(expect)) + + got, err = m.GetForcedObfuscatedAuthenticationSession("client-1", "obfuscated-2") + require.NoError(t, err) + assert.EqualValues(t, expect, got) + + got, err = m.GetForcedObfuscatedAuthenticationSession("client-1", "obfuscated-1") + require.EqualError(t, err, pkg.ErrNotFound.Error()) + }) + } + }) }) } diff --git a/consent/sql_helper.go b/consent/sql_helper.go index bfb0d5d0fb..dc04df2df0 100644 --- a/consent/sql_helper.go +++ b/consent/sql_helper.go @@ -105,6 +105,24 @@ var migrations = &migrate.MemoryMigrationSource{ "DROP TABLE hydra_oauth2_authentication_request_handled", }, }, + { + Id: "2", + Up: []string{ + `ALTER TABLE hydra_oauth2_consent_request ADD forced_subject_identifier VARCHAR(255) NULL DEFAULT ''`, + `ALTER TABLE hydra_oauth2_authentication_request_handled ADD forced_subject_identifier VARCHAR(255) NULL DEFAULT ''`, + `CREATE TABLE hydra_oauth2_obfuscated_authentication_session ( + subject varchar(255) NOT NULL, + client_id varchar(255) NOT NULL, + subject_obfuscated varchar(255) NOT NULL, + PRIMARY KEY(subject, client_id) +)`, + }, + Down: []string{ + `ALTER TABLE hydra_oauth2_consent_request DROP COLUMN forced_subject_identifier`, + `ALTER TABLE hydra_oauth2_authentication_request_handled DROP COLUMN forced_subject_identifier`, + "DROP TABLE hydra_oauth2_obfuscated_authentication_session", + }, + }, }, } @@ -118,9 +136,10 @@ var sqlParamsAuthenticationRequestHandled = []string{ "authenticated_at", "acr", "was_used", + "forced_subject_identifier", } -var sqlParamsRequest = []string{ +var sqlParamsAuthenticationRequest = []string{ "challenge", "verifier", "client_id", @@ -133,6 +152,9 @@ var sqlParamsRequest = []string{ "csrf", "oidc_context", } + +var sqlParamsConsentRequest = append(sqlParamsAuthenticationRequest, "forced_subject_identifier") + var sqlParamsConsentRequestHandled = []string{ "challenge", "granted_scope", @@ -145,13 +167,14 @@ var sqlParamsConsentRequestHandled = []string{ "session_id_token", "was_used", } + var sqlParamsAuthSession = []string{ "id", "authenticated_at", "subject", } -type sqlRequest struct { +type sqlAuthenticationRequest struct { OpenIDConnectContext string `db:"oidc_context"` Client string `db:"client_id"` Subject string `db:"subject"` @@ -165,6 +188,11 @@ type sqlRequest struct { RequestedAt time.Time `db:"requested_at"` } +type sqlConsentRequest struct { + sqlAuthenticationRequest + ForcedSubjectIdentifier string `db:"forced_subject_identifier"` +} + func toMySQLDateHack(t time.Time) *time.Time { if t.IsZero() { return nil @@ -179,13 +207,37 @@ func fromMySQLDateHack(t *time.Time) time.Time { return *t } -func newSQLConsentRequest(c *ConsentRequest) (*sqlRequest, error) { +func newSQLConsentRequest(c *ConsentRequest) (*sqlConsentRequest, error) { oidc, err := json.Marshal(c.OpenIDConnectContext) if err != nil { return nil, errors.WithStack(err) } - return &sqlRequest{ + return &sqlConsentRequest{ + sqlAuthenticationRequest: sqlAuthenticationRequest{ + OpenIDConnectContext: string(oidc), + Client: c.Client.GetID(), + Subject: c.Subject, + RequestURL: c.RequestURL, + Skip: c.Skip, + Challenge: c.Challenge, + RequestedScope: strings.Join(c.RequestedScope, "|"), + Verifier: c.Verifier, + CSRF: c.CSRF, + AuthenticatedAt: toMySQLDateHack(c.AuthenticatedAt), + RequestedAt: c.RequestedAt, + }, + ForcedSubjectIdentifier: c.ForceSubjectIdentifier, + }, nil +} + +func newSQLAuthenticationRequest(c *AuthenticationRequest) (*sqlAuthenticationRequest, error) { + oidc, err := json.Marshal(c.OpenIDConnectContext) + if err != nil { + return nil, errors.WithStack(err) + } + + return &sqlAuthenticationRequest{ OpenIDConnectContext: string(oidc), Client: c.Client.GetID(), Subject: c.Subject, @@ -200,30 +252,13 @@ func newSQLConsentRequest(c *ConsentRequest) (*sqlRequest, error) { }, nil } -func newSQLAuthenticationRequest(c *AuthenticationRequest) (*sqlRequest, error) { - var cc ConsentRequest - cc = ConsentRequest(*c) - return newSQLConsentRequest(&cc) -} - -func (s *sqlRequest) toAuthenticationRequest(client *client.Client) (*AuthenticationRequest, error) { - cr, err := s.toConsentRequest(client) - if err != nil { - return nil, err - } - - var ar AuthenticationRequest - ar = AuthenticationRequest(*cr) - return &ar, nil -} - -func (s *sqlRequest) toConsentRequest(client *client.Client) (*ConsentRequest, error) { +func (s *sqlAuthenticationRequest) toAuthenticationRequest(client *client.Client) (*AuthenticationRequest, error) { var oidc OpenIDConnectContext if err := json.Unmarshal([]byte(s.OpenIDConnectContext), &oidc); err != nil { return nil, errors.WithStack(err) } - return &ConsentRequest{ + return &AuthenticationRequest{ OpenIDConnectContext: &oidc, Client: client, Subject: s.Subject, @@ -238,6 +273,28 @@ func (s *sqlRequest) toConsentRequest(client *client.Client) (*ConsentRequest, e }, nil } +func (s *sqlConsentRequest) toConsentRequest(client *client.Client) (*ConsentRequest, error) { + var oidc OpenIDConnectContext + if err := json.Unmarshal([]byte(s.OpenIDConnectContext), &oidc); err != nil { + return nil, errors.WithStack(err) + } + + return &ConsentRequest{ + OpenIDConnectContext: &oidc, + Client: client, + Subject: s.Subject, + RequestURL: s.RequestURL, + Skip: s.Skip, + Challenge: s.Challenge, + RequestedScope: stringsx.Splitx(s.RequestedScope, "|"), + Verifier: s.Verifier, + CSRF: s.CSRF, + AuthenticatedAt: fromMySQLDateHack(s.AuthenticatedAt), + ForceSubjectIdentifier: s.ForcedSubjectIdentifier, + RequestedAt: s.RequestedAt, + }, nil +} + type sqlHandledConsentRequest struct { GrantedScope string `db:"granted_scope"` SessionIDToken string `db:"session_id_token"` @@ -333,15 +390,16 @@ func (s *sqlHandledConsentRequest) toHandledConsentRequest(r *ConsentRequest) (* } type sqlHandledAuthenticationRequest struct { - Remember bool `db:"remember"` - RememberFor int `db:"remember_for"` - ACR string `db:"acr"` - Subject string `db:"subject"` - Error string `db:"error"` - Challenge string `db:"challenge"` - RequestedAt time.Time `db:"requested_at"` - WasUsed bool `db:"was_used"` - AuthenticatedAt *time.Time `db:"authenticated_at"` + Remember bool `db:"remember"` + RememberFor int `db:"remember_for"` + ACR string `db:"acr"` + Subject string `db:"subject"` + Error string `db:"error"` + Challenge string `db:"challenge"` + RequestedAt time.Time `db:"requested_at"` + WasUsed bool `db:"was_used"` + AuthenticatedAt *time.Time `db:"authenticated_at"` + ForceSubjectIdentifier string `db:"forced_subject_identifier"` } func newSQLHandledAuthenticationRequest(c *HandledAuthenticationRequest) (*sqlHandledAuthenticationRequest, error) { @@ -356,15 +414,16 @@ func newSQLHandledAuthenticationRequest(c *HandledAuthenticationRequest) (*sqlHa } return &sqlHandledAuthenticationRequest{ - ACR: c.ACR, - Subject: c.Subject, - Remember: c.Remember, - RememberFor: c.RememberFor, - Error: e, - Challenge: c.Challenge, - RequestedAt: c.RequestedAt, - WasUsed: c.WasUsed, - AuthenticatedAt: toMySQLDateHack(c.AuthenticatedAt), + ACR: c.ACR, + Subject: c.Subject, + Remember: c.Remember, + RememberFor: c.RememberFor, + Error: e, + Challenge: c.Challenge, + RequestedAt: c.RequestedAt, + WasUsed: c.WasUsed, + AuthenticatedAt: toMySQLDateHack(c.AuthenticatedAt), + ForceSubjectIdentifier: c.ForceSubjectIdentifier, }, nil } @@ -379,13 +438,14 @@ func (s *sqlHandledAuthenticationRequest) toHandledAuthenticationRequest(a *Auth } return &HandledAuthenticationRequest{ - RememberFor: s.RememberFor, - Remember: s.Remember, - Challenge: s.Challenge, - RequestedAt: s.RequestedAt, - WasUsed: s.WasUsed, - ACR: s.ACR, - Error: e, + ForceSubjectIdentifier: s.ForceSubjectIdentifier, + RememberFor: s.RememberFor, + Remember: s.Remember, + Challenge: s.Challenge, + RequestedAt: s.RequestedAt, + WasUsed: s.WasUsed, + ACR: s.ACR, + Error: e, AuthenticationRequest: a, Subject: s.Subject, AuthenticatedAt: fromMySQLDateHack(s.AuthenticatedAt), diff --git a/consent/sql_helper_test.go b/consent/sql_helper_test.go index 7da3ca2061..f1d1d74d1c 100644 --- a/consent/sql_helper_test.go +++ b/consent/sql_helper_test.go @@ -70,7 +70,8 @@ func TestSQLAuthenticationConverter(t *testing.T) { Code: 100, Debug: "error_debug,omitempty", }, - Subject: "subject2", + Subject: "subject2", + ForceSubjectIdentifier: "foo-id", ACR: "acr", WasUsed: true, } @@ -100,16 +101,17 @@ func TestSQLConsentConverter(t *testing.T) { LoginHint: "popup", IDTokenHintClaims: map[string]interface{}{"foo": "bar"}, }, - RequestedAt: time.Now().UTC().Add(-time.Hour), - Client: &client.Client{ClientID: "client"}, - Subject: "subject", - RequestURL: "https://request-url/path", - Skip: true, - Challenge: "challenge", - RequestedScope: []string{"scopea", "scopeb"}, - Verifier: "verifier", - CSRF: "csrf", - AuthenticatedAt: time.Now().UTC().Add(-time.Minute), + ForceSubjectIdentifier: "foo-id", + RequestedAt: time.Now().UTC().Add(-time.Hour), + Client: &client.Client{ClientID: "client"}, + Subject: "subject", + RequestURL: "https://request-url/path", + Skip: true, + Challenge: "challenge", + RequestedScope: []string{"scopea", "scopeb"}, + Verifier: "verifier", + CSRF: "csrf", + AuthenticatedAt: time.Now().UTC().Add(-time.Minute), } b := &HandledConsentRequest{ diff --git a/consent/strategy_default.go b/consent/strategy_default.go index ef25a828b6..7dc716dd04 100644 --- a/consent/strategy_default.go +++ b/consent/strategy_default.go @@ -21,6 +21,7 @@ package consent import ( + "fmt" "net/http" "net/url" "strconv" @@ -36,6 +37,7 @@ import ( "github.com/ory/go-convenience/stringslice" "github.com/ory/go-convenience/stringsx" "github.com/ory/go-convenience/urlx" + "github.com/ory/hydra/client" "github.com/ory/hydra/pkg" "github.com/pborman/uuid" "github.com/pkg/errors" @@ -61,6 +63,7 @@ type DefaultStrategy struct { RequestMaxAge time.Duration JWTStrategy jwt.JWTStrategy OpenIDConnectRequestValidator *openid.OpenIDConnectRequestValidator + SubjectIdentifierAlgorithm map[string]SubjectIdentifierAlgorithm } func NewStrategy( @@ -75,6 +78,7 @@ func NewStrategy( requestMaxAge time.Duration, jwtStrategy jwt.JWTStrategy, openIDConnectRequestValidator *openid.OpenIDConnectRequestValidator, + subjectIdentifierAlgorithm map[string]SubjectIdentifierAlgorithm, ) *DefaultStrategy { return &DefaultStrategy{ AuthenticationURL: authenticationURL, @@ -88,6 +92,7 @@ func NewStrategy( RequestMaxAge: requestMaxAge, JWTStrategy: jwtStrategy, OpenIDConnectRequestValidator: openIDConnectRequestValidator, + SubjectIdentifierAlgorithm: subjectIdentifierAlgorithm, } } @@ -145,11 +150,24 @@ func (s *DefaultStrategy) requestAuthentication(w http.ResponseWriter, r *http.R return err } + var hintSub, obfuscatedUserID, forcedObfuscatedUserID string if hintClaims, ok := token.Claims.(jwtgo.MapClaims); !ok { return errors.WithStack(fosite.ErrInvalidRequest.WithDebug("Failed to validate OpenID Connect request as decoding id token from id_token_hint to *jwt.StandardClaims failed")) } else if hintSub, _ := hintClaims["sub"].(string); hintSub == "" { return errors.WithStack(fosite.ErrInvalidRequest.WithDebug("Failed to validate OpenID Connect request because provided id token from id_token_hint does not have a subject")) - } else if hintSub != session.Subject { + } else if obfuscatedUserID, err = s.obfuscateSubjectIdentifier(session.Subject, ar, ""); err != nil { + return err + } + + if s, err := s.M.GetForcedObfuscatedAuthenticationSession(ar.GetClient().GetID(), hintSub); errors.Cause(err) == pkg.ErrNotFound { + // do nothing + } else if err != nil { + return err + } else { + forcedObfuscatedUserID = s.SubjectObfuscated + } + + if hintSub != session.Subject && hintSub != obfuscatedUserID && hintSub != forcedObfuscatedUserID { return errors.WithStack(fosite.ErrLoginRequired.WithDebug("Request failed because subject claim from id_token_hint does not match subject from authentication session")) } else { return s.forwardAuthenticationRequest(w, r, ar, session.Subject, session.AuthenticatedAt) @@ -256,6 +274,24 @@ func (s *DefaultStrategy) revokeAuthenticationSession(w http.ResponseWriter, r * return s.M.DeleteAuthenticationSession(sid) } +func (s *DefaultStrategy) obfuscateSubjectIdentifier(subject string, req fosite.AuthorizeRequester, forcedIdentifier string) (string, error) { + if c, ok := req.GetClient().(*client.Client); ok && c.SubjectType == "pairwise" { + algorithm, ok := s.SubjectIdentifierAlgorithm[c.SubjectType] + if !ok { + return "", errors.WithStack(fosite.ErrInvalidRequest.WithHint(fmt.Sprintf(`Subject Identifier Algorithm "%s" was requested by OAuth 2.0 Client "%s", but is not configured.`, c.SubjectType, c.ClientID))) + } + + if len(forcedIdentifier) > 0 { + return forcedIdentifier, nil + } + + return algorithm.Obfuscate(subject, c) + } else if !ok { + return "", errors.New("Unable to type assert OAuth 2.0 Client to *client.Client") + } + return subject, nil +} + func (s *DefaultStrategy) verifyAuthentication(w http.ResponseWriter, r *http.Request, req fosite.AuthorizeRequester, verifier string) (*HandledAuthenticationRequest, error) { session, err := s.M.VerifyAndInvalidateAuthenticationRequest(verifier) if errors.Cause(err) == pkg.ErrNotFound { @@ -289,6 +325,11 @@ func (s *DefaultStrategy) verifyAuthentication(w http.ResponseWriter, r *http.Re return nil, errors.WithStack(fosite.ErrServerError.WithDebug("The login request is marked as remember, but the subject from the login confirmation does not match the original subject from the cookie.")) } + subjectIdentifier, err := s.obfuscateSubjectIdentifier(session.Subject, req, session.ForceSubjectIdentifier) + if err != nil { + return nil, err + } + if err := s.OpenIDConnectRequestValidator.ValidatePrompt(&fosite.AuthorizeRequest{ ResponseTypes: req.GetResponseTypes(), RedirectURI: req.GetRedirectURI(), @@ -303,7 +344,7 @@ func (s *DefaultStrategy) verifyAuthentication(w http.ResponseWriter, r *http.Re Form: req.GetRequestForm(), Session: &openid.DefaultSession{ Claims: &jwt.IDTokenClaims{ - Subject: session.Subject, + Subject: subjectIdentifier, IssuedAt: time.Now().UTC(), // doesn't matter ExpiresAt: time.Now().Add(time.Hour).UTC(), // doesn't matter AuthTime: session.AuthenticatedAt, @@ -324,6 +365,16 @@ func (s *DefaultStrategy) verifyAuthentication(w http.ResponseWriter, r *http.Re return nil, err } + if session.ForceSubjectIdentifier != "" { + if err := s.M.CreateForcedObfuscatedAuthenticationSession(&ForcedObfuscatedAuthenticationSession{ + Subject: session.Subject, + ClientID: req.GetClient().GetID(), + SubjectObfuscated: session.ForceSubjectIdentifier, + }); err != nil { + return nil, err + } + } + if !session.Remember { if !session.AuthenticationRequest.Skip { // If the session should not be remembered (and we're actually not skipping), than the user clearly don't @@ -433,17 +484,18 @@ func (s *DefaultStrategy) forwardConsentRequest(w http.ResponseWriter, r *http.R if err := s.M.CreateConsentRequest( &ConsentRequest{ - Challenge: challenge, - Verifier: verifier, - CSRF: csrf, - Skip: skip, - RequestedScope: []string(ar.GetRequestedScopes()), - Subject: as.Subject, - Client: sanitizeClientFromRequest(ar), - RequestURL: as.AuthenticationRequest.RequestURL, - AuthenticatedAt: as.AuthenticatedAt, - RequestedAt: as.RequestedAt, - OpenIDConnectContext: as.AuthenticationRequest.OpenIDConnectContext, + Challenge: challenge, + Verifier: verifier, + CSRF: csrf, + Skip: skip, + RequestedScope: []string(ar.GetRequestedScopes()), + Subject: as.Subject, + Client: sanitizeClientFromRequest(ar), + RequestURL: as.AuthenticationRequest.RequestURL, + AuthenticatedAt: as.AuthenticatedAt, + RequestedAt: as.RequestedAt, + ForceSubjectIdentifier: as.ForceSubjectIdentifier, + OpenIDConnectContext: as.AuthenticationRequest.OpenIDConnectContext, }, ); err != nil { return errors.WithStack(err) @@ -492,6 +544,12 @@ func (s *DefaultStrategy) verifyConsent(w http.ResponseWriter, r *http.Request, return nil, err } + pw, err := s.obfuscateSubjectIdentifier(session.ConsentRequest.Subject, req, session.ConsentRequest.ForceSubjectIdentifier) + if err != nil { + return nil, err + } + + session.ConsentRequest.SubjectIdentifier = pw session.AuthenticatedAt = session.ConsentRequest.AuthenticatedAt return session, nil } diff --git a/consent/strategy_default_test.go b/consent/strategy_default_test.go index a7e9b37b40..792c71e0a5 100644 --- a/consent/strategy_default_test.go +++ b/consent/strategy_default_test.go @@ -95,12 +95,27 @@ func TestStrategy(t *testing.T) { }).ToMapClaims(), jwt.NewHeaders()) require.NoError(t, err) + forcedAuthUserIDToken, _, err := jwts.Generate((jwt.IDTokenClaims{ + Subject: "forced-auth-user", + ExpiresAt: time.Now().Add(time.Hour), + IssuedAt: time.Now(), + }).ToMapClaims(), jwt.NewHeaders()) + require.NoError(t, err) + + pairwiseIDToken, _, err := jwts.Generate((jwt.IDTokenClaims{ + Subject: "c737d5e1fec8896d096d49f6b1a73eb45ac7becb87de9ac3f0a350bad2a9c9fd", + ExpiresAt: time.Now().Add(time.Hour), + IssuedAt: time.Now(), + }).ToMapClaims(), jwt.NewHeaders()) + require.NoError(t, err) + writer := herodot.NewJSONWriter(nil) manager := NewMemoryManager(nil) handler := NewHandler(writer, manager) router := httprouter.New() handler.SetRoutes(router) api := httptest.NewServer(router) + strategy := NewStrategy( lp.URL, cp.URL, @@ -113,11 +128,17 @@ func TestStrategy(t *testing.T) { time.Hour, jwts, openid.NewOpenIDConnectRequestValidator(nil, jwts), + map[string]SubjectIdentifierAlgorithm{ + "pairwise": NewSubjectIdentifierAlgorithmPairwise([]byte("76d5d2bf-747f-4592-9fbd-d2b895a54b3a")), + "public": NewSubjectIdentifierAlgorithmPublic(), + }, ) apiClient := swagger.NewOAuth2ApiWithBasePath(api.URL) persistentCJ := newCookieJar() persistentCJ2 := newCookieJar() + persistentCJ3 := newCookieJar() + persistentCJ4 := newCookieJar() nonexistentCJ, _ := cookiejar.New(&cookiejar.Options{}) apURL, _ := url.Parse(ap.URL) @@ -292,7 +313,7 @@ func TestStrategy(t *testing.T) { expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, expectErr: []bool{true, true, false}, expectSession: &HandledConsentRequest{ - ConsentRequest: &ConsentRequest{Subject: "user"}, + ConsentRequest: &ConsentRequest{Subject: "user", SubjectIdentifier: "user"}, GrantedScope: []string{"scope-a"}, Remember: false, RememberFor: 0, @@ -312,7 +333,7 @@ func TestStrategy(t *testing.T) { expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, expectErr: []bool{true, true, false}, expectSession: &HandledConsentRequest{ - ConsentRequest: &ConsentRequest{Subject: "user"}, + ConsentRequest: &ConsentRequest{Subject: "user", SubjectIdentifier: "user"}, GrantedScope: []string{"scope-a"}, Remember: true, RememberFor: 0, @@ -477,7 +498,7 @@ func TestStrategy(t *testing.T) { expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, expectErr: []bool{true, true, false}, expectSession: &HandledConsentRequest{ - ConsentRequest: &ConsentRequest{Subject: "user"}, + ConsentRequest: &ConsentRequest{Subject: "user", SubjectIdentifier: "user"}, GrantedScope: []string{"scope-a"}, Remember: false, RememberFor: 0, @@ -537,7 +558,7 @@ func TestStrategy(t *testing.T) { expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, expectErr: []bool{true, true, false}, expectSession: &HandledConsentRequest{ - ConsentRequest: &ConsentRequest{Subject: "user"}, + ConsentRequest: &ConsentRequest{Subject: "user", SubjectIdentifier: "user"}, GrantedScope: []string{"scope-a"}, Remember: true, RememberFor: 0, @@ -600,7 +621,7 @@ func TestStrategy(t *testing.T) { expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, expectErr: []bool{true, true, false}, expectSession: &HandledConsentRequest{ - ConsentRequest: &ConsentRequest{Subject: "user"}, + ConsentRequest: &ConsentRequest{Subject: "user", SubjectIdentifier: "user"}, GrantedScope: []string{"scope-a"}, Remember: false, RememberFor: 0, @@ -876,7 +897,7 @@ func TestStrategy(t *testing.T) { expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, expectErr: []bool{true, true, false}, expectSession: &HandledConsentRequest{ - ConsentRequest: &ConsentRequest{Subject: "foouser"}, + ConsentRequest: &ConsentRequest{Subject: "foouser", SubjectIdentifier: "foouser"}, GrantedScope: []string{"scope-a"}, Remember: false, RememberFor: 0, @@ -887,6 +908,159 @@ func TestStrategy(t *testing.T) { }, }, + // Pairwise auth + { + d: "This should pass as regularly and create a new session with pairwise subject set by hydra", + req: fosite.AuthorizeRequest{ResponseTypes: fosite.Arguments{"token", "code", "id_token"}, Request: fosite.Request{Client: &client.Client{ClientID: "client-id", SubjectType: "pairwise", SectorIdentifierURI: "foo"}, Scopes: []string{"scope-a"}}}, + jar: persistentCJ3, + lph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, _, _ := apiClient.AcceptLoginRequest(r.URL.Query().Get("login_challenge"), swagger.AcceptLoginRequest{ + Subject: "auth-user", + Remember: true, + }) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + cph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, _, _ := apiClient.AcceptConsentRequest(r.URL.Query().Get("consent_challenge"), swagger.AcceptConsentRequest{GrantScope: []string{"scope-a"}}) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + expectFinalStatusCode: http.StatusOK, + expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, + expectErr: []bool{true, true, false}, + expectSession: &HandledConsentRequest{ + ConsentRequest: &ConsentRequest{ + Subject: "auth-user", + SubjectIdentifier: "c737d5e1fec8896d096d49f6b1a73eb45ac7becb87de9ac3f0a350bad2a9c9fd", // this is sha256("fooauth-user76d5d2bf-747f-4592-9fbd-d2b895a54b3a") + }, + GrantedScope: []string{"scope-a"}, + Remember: false, + RememberFor: 0, + Session: &ConsentRequestSessionData{ + AccessToken: map[string]interface{}{"foo": "bar"}, + IDToken: map[string]interface{}{"bar": "baz"}, + }, + }, + }, // these tests depend on one another + { + d: "This should pass as regularly and create a new session with pairwise subject and also with the ID token set", + req: fosite.AuthorizeRequest{ResponseTypes: fosite.Arguments{"token", "code", "id_token"}, Request: fosite.Request{Client: &client.Client{ClientID: "client-id", SubjectType: "pairwise", SectorIdentifierURI: "foo"}, Scopes: []string{"scope-a"}}}, + jar: persistentCJ3, + idTokenHint: pairwiseIDToken, + lph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, res, err := apiClient.AcceptLoginRequest(r.URL.Query().Get("login_challenge"), swagger.AcceptLoginRequest{ + Subject: "auth-user", + Remember: false, + }) + require.NoError(t, err) + require.EqualValues(t, http.StatusOK, res.StatusCode) + require.NotEmpty(t, v.RedirectTo) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + cph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, _, _ := apiClient.AcceptConsentRequest(r.URL.Query().Get("consent_challenge"), swagger.AcceptConsentRequest{GrantScope: []string{"scope-a"}}) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + expectFinalStatusCode: http.StatusOK, + expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, + expectErr: []bool{true, true, false}, + expectSession: &HandledConsentRequest{ + ConsentRequest: &ConsentRequest{ + Subject: "auth-user", + SubjectIdentifier: "c737d5e1fec8896d096d49f6b1a73eb45ac7becb87de9ac3f0a350bad2a9c9fd", + }, + GrantedScope: []string{"scope-a"}, + Remember: false, + RememberFor: 0, + Session: &ConsentRequestSessionData{ + AccessToken: map[string]interface{}{"foo": "bar"}, + IDToken: map[string]interface{}{"bar": "baz"}, + }, + }, + }, + { + d: "This should pass as regularly and create a new session with pairwise subject set login request", + req: fosite.AuthorizeRequest{ResponseTypes: fosite.Arguments{"token", "code", "id_token"}, Request: fosite.Request{Client: &client.Client{ClientID: "client-id", SubjectType: "pairwise", SectorIdentifierURI: "foo"}, Scopes: []string{"scope-a"}}}, + jar: persistentCJ4, + lph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, _, _ := apiClient.AcceptLoginRequest(r.URL.Query().Get("login_challenge"), swagger.AcceptLoginRequest{ + Subject: "auth-user", + ForceSubjectIdentifier: "forced-auth-user", + Remember: true, + }) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + cph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, _, _ := apiClient.AcceptConsentRequest(r.URL.Query().Get("consent_challenge"), swagger.AcceptConsentRequest{GrantScope: []string{"scope-a"}}) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + expectFinalStatusCode: http.StatusOK, + expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, + expectErr: []bool{true, true, false}, + expectSession: &HandledConsentRequest{ + ConsentRequest: &ConsentRequest{ + Subject: "auth-user", + SubjectIdentifier: "forced-auth-user", + }, + GrantedScope: []string{"scope-a"}, + Remember: false, + RememberFor: 0, + Session: &ConsentRequestSessionData{ + AccessToken: map[string]interface{}{"foo": "bar"}, + IDToken: map[string]interface{}{"bar": "baz"}, + }, + }, + }, // these tests depend on one another + { + d: "This should pass as regularly and create a new session with pairwise subject set on login request and also with the ID token set", + req: fosite.AuthorizeRequest{ResponseTypes: fosite.Arguments{"token", "code", "id_token"}, Request: fosite.Request{Client: &client.Client{ClientID: "client-id", SubjectType: "pairwise", SectorIdentifierURI: "foo"}, Scopes: []string{"scope-a"}}}, + jar: persistentCJ3, + idTokenHint: forcedAuthUserIDToken, + lph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, _, _ := apiClient.AcceptLoginRequest(r.URL.Query().Get("login_challenge"), swagger.AcceptLoginRequest{ + Subject: "auth-user", + ForceSubjectIdentifier: "forced-auth-user", + Remember: false, + }) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + cph: func(t *testing.T) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + v, _, _ := apiClient.AcceptConsentRequest(r.URL.Query().Get("consent_challenge"), swagger.AcceptConsentRequest{GrantScope: []string{"scope-a"}}) + http.Redirect(w, r, v.RedirectTo, http.StatusFound) + } + }, + expectFinalStatusCode: http.StatusOK, + expectErrType: []error{ErrAbortOAuth2Request, ErrAbortOAuth2Request, nil}, + expectErr: []bool{true, true, false}, + expectSession: &HandledConsentRequest{ + ConsentRequest: &ConsentRequest{ + Subject: "auth-user", + SubjectIdentifier: "forced-auth-user", + }, + GrantedScope: []string{"scope-a"}, + Remember: false, + RememberFor: 0, + Session: &ConsentRequestSessionData{ + AccessToken: map[string]interface{}{"foo": "bar"}, + IDToken: map[string]interface{}{"bar": "baz"}, + }, + }, + }, + // checks revoking sessions { d: "This should pass as regularly and create a new session", @@ -1066,6 +1240,7 @@ func TestStrategy(t *testing.T) { assert.EqualValues(t, tc.expectSession.Remember, c.Remember) assert.EqualValues(t, tc.expectSession.RememberFor, c.RememberFor) assert.EqualValues(t, tc.expectSession.ConsentRequest.Subject, c.ConsentRequest.Subject) + assert.EqualValues(t, tc.expectSession.ConsentRequest.SubjectIdentifier, c.ConsentRequest.SubjectIdentifier) } } diff --git a/consent/subject_identifier_algorithm.go b/consent/subject_identifier_algorithm.go new file mode 100644 index 0000000000..f0b77d8d65 --- /dev/null +++ b/consent/subject_identifier_algorithm.go @@ -0,0 +1,28 @@ +/* + * Copyright © 2015-2018 Aeneas Rekkas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Aeneas Rekkas + * @Copyright 2017-2018 Aeneas Rekkas + * @license Apache-2.0 + */ + +package consent + +import "github.com/ory/hydra/client" + +type SubjectIdentifierAlgorithm interface { + // Obfuscate derives a pairwise subject identifier from the given string. + Obfuscate(subject string, client *client.Client) (string, error) +} diff --git a/consent/subject_identifier_algorithm_pairwise.go b/consent/subject_identifier_algorithm_pairwise.go new file mode 100644 index 0000000000..c44995e657 --- /dev/null +++ b/consent/subject_identifier_algorithm_pairwise.go @@ -0,0 +1,59 @@ +/* + * Copyright © 2015-2018 Aeneas Rekkas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Aeneas Rekkas + * @Copyright 2017-2018 Aeneas Rekkas + * @license Apache-2.0 + */ + +package consent + +import ( + "crypto/sha256" + "fmt" + "net/url" + + "github.com/ory/fosite" + "github.com/ory/hydra/client" + "github.com/pkg/errors" +) + +type SubjectIdentifierAlgorithmPairwise struct { + Salt []byte +} + +func NewSubjectIdentifierAlgorithmPairwise(salt []byte) *SubjectIdentifierAlgorithmPairwise { + return &SubjectIdentifierAlgorithmPairwise{Salt: salt} +} + +func (g *SubjectIdentifierAlgorithmPairwise) Obfuscate(subject string, client *client.Client) (string, error) { + // sub = SHA-256 ( sector_identifier || local_account_id || salt ). + var id string + if len(client.SectorIdentifierURI) == 0 && len(client.RedirectURIs) > 1 { + return "", errors.WithStack(fosite.ErrInvalidRequest.WithHint(fmt.Sprintf("OAuth 2.0 Client %s has multiple redirect_uris but no sector_identifier_uri was set which is not allowed when performing using subject type pairwise. Please reconfigure the OAuth 2.0 client properly.", client.ClientID))) + } else if len(client.SectorIdentifierURI) == 0 && len(client.RedirectURIs) == 0 { + return "", errors.WithStack(fosite.ErrInvalidRequest.WithHint(fmt.Sprintf("OAuth 2.0 Client %s neither specifies a sector_identifier_uri nor a redirect_uri which is not allowed when performing using subject type pairwise. Please reconfigure the OAuth 2.0 client properly.", client.ClientID))) + } else if len(client.SectorIdentifierURI) > 0 { + id = client.SectorIdentifierURI + } else { + redirectURL, err := url.Parse(client.RedirectURIs[0]) + if err != nil { + return "", errors.WithStack(err) + } + id = redirectURL.Host + } + + return fmt.Sprintf("%x", sha256.Sum256(append(append([]byte{}, []byte(id+subject)...), g.Salt...))), nil +} diff --git a/consent/subject_identifier_algorithm_public.go b/consent/subject_identifier_algorithm_public.go new file mode 100644 index 0000000000..150ebd2864 --- /dev/null +++ b/consent/subject_identifier_algorithm_public.go @@ -0,0 +1,33 @@ +/* + * Copyright © 2015-2018 Aeneas Rekkas + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Aeneas Rekkas + * @Copyright 2017-2018 Aeneas Rekkas + * @license Apache-2.0 + */ + +package consent + +import "github.com/ory/hydra/client" + +type SubjectIdentifierAlgorithmPublic struct{} + +func NewSubjectIdentifierAlgorithmPublic() *SubjectIdentifierAlgorithmPublic { + return &SubjectIdentifierAlgorithmPublic{} +} + +func (g *SubjectIdentifierAlgorithmPublic) Obfuscate(subject string, client *client.Client) (string, error) { + return subject, nil +} diff --git a/consent/types.go b/consent/types.go index 96883b24bb..6c08e67632 100644 --- a/consent/types.go +++ b/consent/types.go @@ -149,6 +149,25 @@ type HandledAuthenticationRequest struct { // Subject is the user ID of the end-user that authenticated. Subject string `json:"subject"` + // ForceSubjectIdentifier forces the "pairwise" user ID of the end-user that authenticated. The "pairwise" user ID refers to the + // (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID + // Connect specification. It allows you to set an obfuscated subject ("user") identifier that is unique to the client. + // + // Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the + // sub claim in the OAuth 2.0 Introspection. + // + // Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself + // you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in + // ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's + // configuration). + // + // Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies + // that you have to compute this value on every authentication process (probably depending on the client ID or some + // other unique value). + // + // If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. + ForceSubjectIdentifier string `json:"force_subject_identifier"` + AuthenticationRequest *AuthenticationRequest `json:"-"` Error *RequestDeniedError `json:"-"` Challenge string `json:"-"` @@ -218,7 +237,8 @@ type AuthenticationRequest struct { Skip bool `json:"skip"` // Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope - // requested by the OAuth 2.0 client. + // requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type + // when accepting the login request, or the request will fail. Subject string `json:"subject"` // OpenIDConnectContext provides context for the (potential) OpenID Connect context. Implementation of these @@ -233,10 +253,11 @@ type AuthenticationRequest struct { // might come in handy if you want to deal with additional request parameters. RequestURL string `json:"request_url"` - Verifier string `json:"-"` - CSRF string `json:"-"` - AuthenticatedAt time.Time `json:"-"` - RequestedAt time.Time `json:"-"` + ForceSubjectIdentifier string `json:"-"` // this is here but has no meaning apart from sql_helper working properly. + Verifier string `json:"-"` + CSRF string `json:"-"` + AuthenticatedAt time.Time `json:"-"` + RequestedAt time.Time `json:"-"` } // Contains information on an ongoing consent request. @@ -271,10 +292,13 @@ type ConsentRequest struct { // might come in handy if you want to deal with additional request parameters. RequestURL string `json:"request_url"` - Verifier string `json:"-"` - CSRF string `json:"-"` - AuthenticatedAt time.Time `json:"-"` - RequestedAt time.Time `json:"-"` + // ForceSubjectIdentifier is the value from authentication (if set). + ForceSubjectIdentifier string `json:"-"` + SubjectIdentifier string `json:"-"` + Verifier string `json:"-"` + CSRF string `json:"-"` + AuthenticatedAt time.Time `json:"-"` + RequestedAt time.Time `json:"-"` } // Used to pass session data to a consent request. diff --git a/docker-compose.yml b/docker-compose.yml index 897f551f72..29ed534753 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,9 @@ services: # - DATABASE_URL=mysql://root:secret@tcp(mysqld:3306)/mysql?parseTime=true - SYSTEM_SECRET=youReallyNeedToChangeThis - OAUTH2_SHARE_ERROR_DEBUG=1 -# - OAUTH2_ACCESS_TOKEN_STRATEGY=jwt + - OIDC_SUBJECT_TYPES_SUPPORTED=public,pairwise + - OIDC_SUBJECT_TYPE_PAIRWISE_SALT=youReallyNeedToChangeThis +# - OAUTH2_ACCESS_TOKEN_STRATEGY=jwt restart: unless-stopped consent: diff --git a/docs/api.swagger.json b/docs/api.swagger.json index dd93b734b2..383a3585b4 100644 --- a/docs/api.swagger.json +++ b/docs/api.swagger.json @@ -2033,6 +2033,11 @@ "type": "string", "x-go-name": "ACR" }, + "force_subject_identifier": { + "description": "ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the\n(Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID\nConnect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client.\n\nPlease note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the\nsub claim in the OAuth 2.0 Introspection.\n\nPer default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself\nyou can use this field. Please note that setting this field has no effect if `pairwise` is not configured in\nORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's\nconfiguration).\n\nPlease also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies\nthat you have to compute this value on every authentication process (probably depending on the client ID or some\nother unique value).\n\nIf you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.", + "type": "string", + "x-go-name": "ForceSubjectIdentifier" + }, "remember": { "description": "Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store\na cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she\nwill not be asked to log in again.", "type": "boolean", @@ -2344,7 +2349,7 @@ "x-go-name": "Skip" }, "subject": { - "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client.", + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type\nwhen accepting the login request, or the request will fail.", "type": "string", "x-go-name": "Subject" } @@ -2463,6 +2468,11 @@ "type": "string", "x-go-name": "SectorIdentifierURI" }, + "subject_type": { + "description": "SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a\nlist of the supported subject_type values for this server. Valid types include `pairwise` and `public`.", + "type": "string", + "x-go-name": "SubjectType" + }, "token_endpoint_auth_method": { "description": "Requested Client Authentication method for the Token Endpoint. The options are client_secret_post,\nclient_secret_basic, private_key_jwt, and none.", "type": "string", @@ -2538,6 +2548,11 @@ "format": "int64", "x-go-name": "NotBefore" }, + "obfuscated_subject": { + "description": "ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization.\nIt is the `sub` value of the ID Token that was issued.", + "type": "string", + "x-go-name": "ObfuscatedSubject" + }, "scope": { "description": "Scope is a JSON string containing a space-separated list of\nscopes associated with this token.", "type": "string", diff --git a/oauth2/handler.go b/oauth2/handler.go index 73445f8778..0b9fcbc7ee 100644 --- a/oauth2/handler.go +++ b/oauth2/handler.go @@ -25,6 +25,7 @@ import ( "encoding/json" "fmt" "net/http" + "reflect" "strings" "time" @@ -417,19 +418,33 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request, _ ht exp = resp.GetAccessRequester().GetRequestedAt().Add(h.AccessTokenLifespan) } + session, ok := resp.GetAccessRequester().GetSession().(*Session) + if !ok { + err := errors.WithStack(fosite.ErrServerError.WithHint("Expected session to be of type *Session, but got another type.").WithDebug(fmt.Sprintf("Got type %s", reflect.TypeOf(resp.GetAccessRequester().GetSession())))) + pkg.LogError(err, h.L) + h.OAuth2.WriteIntrospectionError(w, err) + return + } + + var obfuscated string + if len(session.Claims.Subject) > 0 && session.Claims.Subject != session.Subject { + obfuscated = session.Claims.Subject + } + w.Header().Set("Content-Type", "application/json;charset=UTF-8") if err = json.NewEncoder(w).Encode(&Introspection{ - Active: resp.IsActive(), - ClientID: resp.GetAccessRequester().GetClient().GetID(), - Scope: strings.Join(resp.GetAccessRequester().GetGrantedScopes(), " "), - ExpiresAt: exp.Unix(), - IssuedAt: resp.GetAccessRequester().GetRequestedAt().Unix(), - Subject: resp.GetAccessRequester().GetSession().GetSubject(), - Username: resp.GetAccessRequester().GetSession().GetUsername(), - Extra: resp.GetAccessRequester().GetSession().(*Session).Extra, - Audience: resp.GetAccessRequester().GetSession().(*Session).Audience, - Issuer: strings.TrimRight(h.IssuerURL, "/") + "/", - TokenType: string(resp.GetTokenType()), + Active: resp.IsActive(), + ClientID: resp.GetAccessRequester().GetClient().GetID(), + Scope: strings.Join(resp.GetAccessRequester().GetGrantedScopes(), " "), + ExpiresAt: exp.Unix(), + IssuedAt: resp.GetAccessRequester().GetRequestedAt().Unix(), + Subject: session.GetSubject(), + Username: session.GetUsername(), + Extra: session.Extra, + Audience: session.Audience, + Issuer: strings.TrimRight(h.IssuerURL, "/") + "/", + ObfuscatedSubject: obfuscated, + TokenType: string(resp.GetTokenType()), }); err != nil { pkg.LogError(errors.WithStack(err), h.L) } @@ -608,7 +623,7 @@ func (h *Handler) AuthHandler(w http.ResponseWriter, r *http.Request, _ httprout Claims: &jwt.IDTokenClaims{ // We do not need to pass the audience because it's included directly by ORY Fosite //Audience: []string{authorizeRequest.GetClient().GetID()}, - Subject: session.ConsentRequest.Subject, + Subject: session.ConsentRequest.SubjectIdentifier, Issuer: strings.TrimRight(h.IssuerURL, "/") + "/", IssuedAt: time.Now().UTC(), ExpiresAt: time.Now().Add(h.IDTokenLifespan).UTC(), diff --git a/oauth2/handler_test.go b/oauth2/handler_test.go index e18a5411b3..f7b69245b8 100644 --- a/oauth2/handler_test.go +++ b/oauth2/handler_test.go @@ -207,6 +207,37 @@ func TestUserinfo(t *testing.T) { assert.True(t, strings.Contains(string(body), `"sub":"alice"`), "%s", body) }, }, + { + setup: func(t *testing.T) { + op.EXPECT(). + IntrospectToken(gomock.Any(), gomock.Eq("access-token"), gomock.Eq(fosite.AccessToken), gomock.Any()). + DoAndReturn(func(_ context.Context, _ string, _ fosite.TokenType, session fosite.Session, _ ...string) (fosite.TokenType, fosite.AccessRequester, error) { + session = &oauth2.Session{ + DefaultSession: &openid.DefaultSession{ + Claims: &jwt.IDTokenClaims{ + Subject: "another-alice", + }, + Headers: new(jwt.Headers), + Subject: "alice", + }, + Audience: []string{}, + Extra: map[string]interface{}{}, + } + + return fosite.AccessToken, &fosite.AccessRequest{ + Request: fosite.Request{ + Client: &client.Client{}, + Session: session, + }, + }, nil + }) + }, + expectStatusCode: http.StatusOK, + check: func(t *testing.T, body []byte) { + assert.False(t, strings.Contains(string(body), `"sub":"alice"`), "%s", body) + assert.True(t, strings.Contains(string(body), `"sub":"another-alice"`), "%s", body) + }, + }, { setup: func(t *testing.T) { op.EXPECT(). @@ -329,6 +360,7 @@ func TestHandlerWellKnown(t *testing.T) { H: herodot.NewJSONWriter(nil), ScopeStrategy: fosite.HierarchicScopeStrategy, IssuerURL: "http://hydra.localhost", + SubjectTypes: []string{"pairwise", "public"}, } AuthPathT := "/oauth2/auth" diff --git a/oauth2/introspector.go b/oauth2/introspector.go index a2910faff0..b86af1f26f 100644 --- a/oauth2/introspector.go +++ b/oauth2/introspector.go @@ -50,6 +50,10 @@ type Introspection struct { // authorized this token. Subject string `json:"sub,omitempty"` + // ObfuscatedSubject is set when the subject identifier algorithm was set to "pairwise" during authorization. + // It is the `sub` value of the ID Token that was issued. + ObfuscatedSubject string `json:"obfuscated_subject,omitempty"` + // Expires at is an integer timestamp, measured in the number of seconds // since January 1 1970 UTC, indicating when this token will expire. ExpiresAt int64 `json:"exp,omitempty"` diff --git a/oauth2/introspector_test.go b/oauth2/introspector_test.go index 90d5fffbfb..6357f59804 100644 --- a/oauth2/introspector_test.go +++ b/oauth2/introspector_test.go @@ -44,7 +44,7 @@ import ( ) func TestIntrospectorSDK(t *testing.T) { - tokens := pkg.Tokens(3) + tokens := pkg.Tokens(4) memoryStore := storage.NewExampleStore() memoryStore.Clients["my-client"].(*fosite.DefaultClient).Scopes = []string{"fosite", "openid", "photos", "offline", "foo.*"} @@ -82,6 +82,7 @@ func TestIntrospectorSDK(t *testing.T) { createAccessTokenSession("alice", "my-client", tokens[0][0], now.Add(time.Hour), memoryStore, fosite.Arguments{"core", "foo.*"}) createAccessTokenSession("siri", "my-client", tokens[1][0], now.Add(-time.Hour), memoryStore, fosite.Arguments{"core", "foo.*"}) createAccessTokenSession("my-client", "my-client", tokens[2][0], now.Add(time.Hour), memoryStore, fosite.Arguments{"hydra.introspect"}) + createAccessTokenSessionPairwise("alice", "my-client", tokens[3][0], now.Add(time.Hour), memoryStore, fosite.Arguments{"core", "foo.*"}, "alice-obfuscated") t.Run("TestIntrospect", func(t *testing.T) { for k, c := range []struct { @@ -158,6 +159,16 @@ func TestIntrospectorSDK(t *testing.T) { assert.Equal(t, map[string]interface{}{"foo": "bar"}, c.Ext) }, }, + { + description: "should pass and check for obfuscated subject", + token: tokens[3][1], + expectInactive: false, + scopes: []string{"foo.bar"}, + assert: func(t *testing.T, c *hydra.OAuth2TokenIntrospection) { + assert.Equal(t, "alice", c.Sub) + assert.Equal(t, "alice-obfuscated", c.ObfuscatedSubject) + }, + }, } { t.Run(fmt.Sprintf("case=%d/description=%s", k, c.description), func(t *testing.T) { var client *hydra.OAuth2Api diff --git a/oauth2/oauth2_auth_code_test.go b/oauth2/oauth2_auth_code_test.go index 7eaf1daf41..935b0e9e4e 100644 --- a/oauth2/oauth2_auth_code_test.go +++ b/oauth2/oauth2_auth_code_test.go @@ -160,6 +160,10 @@ func TestAuthCodeWithDefaultStrategy(t *testing.T) { cookieStore, fosite.ExactScopeStrategy, false, time.Hour, jwts, openid.NewOpenIDConnectRequestValidator(nil, jwts), + map[string]consent.SubjectIdentifierAlgorithm{ + "pairwise": consent.NewSubjectIdentifierAlgorithmPairwise([]byte("76d5d2bf-747f-4592-9fbd-d2b895a54b3a")), + "public": consent.NewSubjectIdentifierAlgorithmPublic(), + }, ) jm := &jwk.MemoryManager{Keys: map[string]*jose.JSONWebKeySet{}} diff --git a/oauth2/oauth2_helper_test.go b/oauth2/oauth2_helper_test.go index 42c8ba76cb..1fac6c32fd 100644 --- a/oauth2/oauth2_helper_test.go +++ b/oauth2/oauth2_helper_test.go @@ -58,7 +58,8 @@ func (c *consentMock) HandleOAuth2AuthorizationRequest(w http.ResponseWriter, r return &consent.HandledConsentRequest{ ConsentRequest: &consent.ConsentRequest{ - Subject: "foo", + Subject: "foo", + SubjectIdentifier: "foo", }, AuthenticatedAt: c.authTime, GrantedScope: []string{"offline", "openid", "hydra.*"}, diff --git a/oauth2/revocator_test.go b/oauth2/revocator_test.go index fa275e094c..12e23f4ec9 100644 --- a/oauth2/revocator_test.go +++ b/oauth2/revocator_test.go @@ -42,6 +42,10 @@ import ( ) func createAccessTokenSession(subject, client string, token string, expiresAt time.Time, fs *storage.MemoryStore, scopes fosite.Arguments) { + createAccessTokenSessionPairwise(subject, client, token, expiresAt, fs, scopes, "") +} + +func createAccessTokenSessionPairwise(subject, client string, token string, expiresAt time.Time, fs *storage.MemoryStore, scopes fosite.Arguments, obfuscated string) { ar := fosite.NewAccessRequest(oauth2.NewSession(subject)) ar.GrantedScopes = fosite.Arguments{"core"} if scopes != nil { @@ -51,6 +55,10 @@ func createAccessTokenSession(subject, client string, token string, expiresAt ti ar.Client = &fosite.DefaultClient{ID: client} ar.Session.SetExpiresAt(fosite.AccessToken, expiresAt) ar.Session.(*oauth2.Session).Extra = map[string]interface{}{"foo": "bar"} + if obfuscated != "" { + ar.Session.(*oauth2.Session).Claims.Subject = obfuscated + } + fs.CreateAccessTokenSession(nil, token, ar) } diff --git a/sdk/go/hydra/swagger/accept_login_request.go b/sdk/go/hydra/swagger/accept_login_request.go index 2c61ddca07..a0bc9d866f 100644 --- a/sdk/go/hydra/swagger/accept_login_request.go +++ b/sdk/go/hydra/swagger/accept_login_request.go @@ -15,6 +15,9 @@ type AcceptLoginRequest struct { // ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. Acr string `json:"acr,omitempty"` + // ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. + ForceSubjectIdentifier string `json:"force_subject_identifier,omitempty"` + // Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. Remember bool `json:"remember,omitempty"` diff --git a/sdk/go/hydra/swagger/docs/AcceptLoginRequest.md b/sdk/go/hydra/swagger/docs/AcceptLoginRequest.md index aa8171fcf8..2ef2d77cd8 100644 --- a/sdk/go/hydra/swagger/docs/AcceptLoginRequest.md +++ b/sdk/go/hydra/swagger/docs/AcceptLoginRequest.md @@ -4,6 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **Acr** | **string** | ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. | [optional] [default to null] +**ForceSubjectIdentifier** | **string** | ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. | [optional] [default to null] **Remember** | **bool** | Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. | [optional] [default to null] **RememberFor** | **int64** | RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the authorization will be remembered indefinitely. | [optional] [default to null] **Subject** | **string** | Subject is the user ID of the end-user that authenticated. | [optional] [default to null] diff --git a/sdk/go/hydra/swagger/docs/LoginRequest.md b/sdk/go/hydra/swagger/docs/LoginRequest.md index 7a5b18fae7..d774bd21f3 100644 --- a/sdk/go/hydra/swagger/docs/LoginRequest.md +++ b/sdk/go/hydra/swagger/docs/LoginRequest.md @@ -9,7 +9,7 @@ Name | Type | Description | Notes **RequestUrl** | **string** | RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters. | [optional] [default to null] **RequestedScope** | **[]string** | RequestedScope contains all scopes requested by the OAuth 2.0 client. | [optional] [default to null] **Skip** | **bool** | Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL. This feature allows you to update / set session information. | [optional] [default to null] -**Subject** | **string** | Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. | [optional] [default to null] +**Subject** | **string** | Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail. | [optional] [default to null] [[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/sdk/go/hydra/swagger/docs/OAuth2Client.md b/sdk/go/hydra/swagger/docs/OAuth2Client.md index b84f51bd5f..0f7b799389 100644 --- a/sdk/go/hydra/swagger/docs/OAuth2Client.md +++ b/sdk/go/hydra/swagger/docs/OAuth2Client.md @@ -21,6 +21,7 @@ Name | Type | Description | Notes **ResponseTypes** | **[]string** | ResponseTypes is an array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint. | [optional] [default to null] **Scope** | **string** | Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens. | [optional] [default to null] **SectorIdentifierUri** | **string** | URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values. | [optional] [default to null] +**SubjectType** | **string** | SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`. | [optional] [default to null] **TokenEndpointAuthMethod** | **string** | Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none. | [optional] [default to null] **TosUri** | **string** | TermsOfServiceURI is a URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client. | [optional] [default to null] **UserinfoSignedResponseAlg** | **string** | JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type. | [optional] [default to null] diff --git a/sdk/go/hydra/swagger/docs/OAuth2TokenIntrospection.md b/sdk/go/hydra/swagger/docs/OAuth2TokenIntrospection.md index 7d0f081eb4..7435fc66db 100644 --- a/sdk/go/hydra/swagger/docs/OAuth2TokenIntrospection.md +++ b/sdk/go/hydra/swagger/docs/OAuth2TokenIntrospection.md @@ -11,6 +11,7 @@ Name | Type | Description | Notes **Iat** | **int64** | Issued at is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token was originally issued. | [optional] [default to null] **Iss** | **string** | IssuerURL is a string representing the issuer of this token | [optional] [default to null] **Nbf** | **int64** | NotBefore is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token is not to be used before. | [optional] [default to null] +**ObfuscatedSubject** | **string** | ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization. It is the `sub` value of the ID Token that was issued. | [optional] [default to null] **Scope** | **string** | Scope is a JSON string containing a space-separated list of scopes associated with this token. | [optional] [default to null] **Sub** | **string** | Subject of the token, as defined in JWT [RFC7519]. Usually a machine-readable identifier of the resource owner who authorized this token. | [optional] [default to null] **TokenType** | **string** | TokenType is the introspected token's type, for example `access_token` or `refresh_token`. | [optional] [default to null] diff --git a/sdk/go/hydra/swagger/login_request.go b/sdk/go/hydra/swagger/login_request.go index 772434f965..3043bc08e3 100644 --- a/sdk/go/hydra/swagger/login_request.go +++ b/sdk/go/hydra/swagger/login_request.go @@ -28,6 +28,6 @@ type LoginRequest struct { // Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL. This feature allows you to update / set session information. Skip bool `json:"skip,omitempty"` - // Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. + // Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail. Subject string `json:"subject,omitempty"` } diff --git a/sdk/go/hydra/swagger/o_auth2_client.go b/sdk/go/hydra/swagger/o_auth2_client.go index 97440e6dd0..889aba7a63 100644 --- a/sdk/go/hydra/swagger/o_auth2_client.go +++ b/sdk/go/hydra/swagger/o_auth2_client.go @@ -65,6 +65,9 @@ type OAuth2Client struct { // URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values. SectorIdentifierUri string `json:"sector_identifier_uri,omitempty"` + // SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`. + SubjectType string `json:"subject_type,omitempty"` + // Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none. TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"` diff --git a/sdk/go/hydra/swagger/o_auth2_token_introspection.go b/sdk/go/hydra/swagger/o_auth2_token_introspection.go index 4e0d12f7c2..75034c18a3 100644 --- a/sdk/go/hydra/swagger/o_auth2_token_introspection.go +++ b/sdk/go/hydra/swagger/o_auth2_token_introspection.go @@ -36,6 +36,9 @@ type OAuth2TokenIntrospection struct { // NotBefore is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token is not to be used before. Nbf int64 `json:"nbf,omitempty"` + // ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization. It is the `sub` value of the ID Token that was issued. + ObfuscatedSubject string `json:"obfuscated_subject,omitempty"` + // Scope is a JSON string containing a space-separated list of scopes associated with this token. Scope string `json:"scope,omitempty"` diff --git a/sdk/js/swagger/docs/AcceptLoginRequest.md b/sdk/js/swagger/docs/AcceptLoginRequest.md index ea93435328..582d5dddda 100644 --- a/sdk/js/swagger/docs/AcceptLoginRequest.md +++ b/sdk/js/swagger/docs/AcceptLoginRequest.md @@ -4,6 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **acr** | **String** | ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. | [optional] +**forceSubjectIdentifier** | **String** | ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. | [optional] **remember** | **Boolean** | Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. | [optional] **rememberFor** | **Number** | RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the authorization will be remembered indefinitely. | [optional] **subject** | **String** | Subject is the user ID of the end-user that authenticated. | [optional] diff --git a/sdk/js/swagger/docs/JSONWebKey.md b/sdk/js/swagger/docs/JSONWebKey.md index 43f77c9cc5..7bbc0c4805 100644 --- a/sdk/js/swagger/docs/JSONWebKey.md +++ b/sdk/js/swagger/docs/JSONWebKey.md @@ -1,24 +1,12 @@ -# OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JsonWebKey +# OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JSONWebKey ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**alg** | **String** | The \"alg\" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA \"JSON Web Signature and Encryption Algorithms\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. | [optional] -**crv** | **String** | | [optional] -**d** | **String** | | [optional] -**dp** | **String** | | [optional] -**dq** | **String** | | [optional] -**e** | **String** | | [optional] -**k** | **String** | | [optional] -**kid** | **String** | The \"kid\" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the \"kid\" value is unspecified. When \"kid\" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct \"kid\" values. (One example in which different keys might use the same \"kid\" value is if they have different \"kty\" (key type) values but are considered to be equivalent alternatives by the application using them.) The \"kid\" value is a case-sensitive string. | [optional] -**kty** | **String** | The \"kty\" (key type) parameter identifies the cryptographic algorithm family used with the key, such as \"RSA\" or \"EC\". \"kty\" values should either be registered in the IANA \"JSON Web Key Types\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The \"kty\" value is a case-sensitive string. | [optional] -**n** | **String** | | [optional] -**p** | **String** | | [optional] -**q** | **String** | | [optional] -**qi** | **String** | | [optional] -**use** | **String** | The \"use\" (public key use) parameter identifies the intended use of the public key. The \"use\" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly \"sig\" (signature) or \"enc\" (encryption). | [optional] -**x** | **String** | | [optional] -**x5c** | **[String]** | The \"x5c\" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate. | [optional] -**y** | **String** | | [optional] +**algorithm** | **String** | | [optional] +**certificates** | [**[Certificate]**](Certificate.md) | | [optional] +**key** | **Object** | | [optional] +**keyID** | **String** | | [optional] +**use** | **String** | | [optional] diff --git a/sdk/js/swagger/docs/JSONWebKeySet.md b/sdk/js/swagger/docs/JSONWebKeySet.md index c5a28fde64..15db72104b 100644 --- a/sdk/js/swagger/docs/JSONWebKeySet.md +++ b/sdk/js/swagger/docs/JSONWebKeySet.md @@ -1,8 +1,8 @@ -# OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JsonWebKeySet +# OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JSONWebKeySet ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**keys** | [**[JsonWebKey]**](JsonWebKey.md) | The value of the \"keys\" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired. | [optional] +**keys** | [**[JSONWebKey]**](JSONWebKey.md) | | [optional] diff --git a/sdk/js/swagger/docs/LoginRequest.md b/sdk/js/swagger/docs/LoginRequest.md index b42bb0c2ef..40055a30a7 100644 --- a/sdk/js/swagger/docs/LoginRequest.md +++ b/sdk/js/swagger/docs/LoginRequest.md @@ -9,6 +9,6 @@ Name | Type | Description | Notes **requestUrl** | **String** | RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters. | [optional] **requestedScope** | **[String]** | RequestedScope contains all scopes requested by the OAuth 2.0 client. | [optional] **skip** | **Boolean** | Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL. This feature allows you to update / set session information. | [optional] -**subject** | **String** | Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. | [optional] +**subject** | **String** | Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail. | [optional] diff --git a/sdk/js/swagger/docs/OAuth2Client.md b/sdk/js/swagger/docs/OAuth2Client.md index 85894657b1..7961e3f0b5 100644 --- a/sdk/js/swagger/docs/OAuth2Client.md +++ b/sdk/js/swagger/docs/OAuth2Client.md @@ -21,6 +21,7 @@ Name | Type | Description | Notes **responseTypes** | **[String]** | ResponseTypes is an array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint. | [optional] **scope** | **String** | Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens. | [optional] **sectorIdentifierUri** | **String** | URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values. | [optional] +**subjectType** | **String** | SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`. | [optional] **tokenEndpointAuthMethod** | **String** | Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none. | [optional] **tosUri** | **String** | TermsOfServiceURI is a URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client. | [optional] **userinfoSignedResponseAlg** | **String** | JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type. | [optional] diff --git a/sdk/js/swagger/docs/OAuth2TokenIntrospection.md b/sdk/js/swagger/docs/OAuth2TokenIntrospection.md index 49e2af56a4..e9f10671f7 100644 --- a/sdk/js/swagger/docs/OAuth2TokenIntrospection.md +++ b/sdk/js/swagger/docs/OAuth2TokenIntrospection.md @@ -11,6 +11,7 @@ Name | Type | Description | Notes **iat** | **Number** | Issued at is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token was originally issued. | [optional] **iss** | **String** | IssuerURL is a string representing the issuer of this token | [optional] **nbf** | **Number** | NotBefore is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token is not to be used before. | [optional] +**obfuscatedSubject** | **String** | ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization. It is the `sub` value of the ID Token that was issued. | [optional] **scope** | **String** | Scope is a JSON string containing a space-separated list of scopes associated with this token. | [optional] **sub** | **String** | Subject of the token, as defined in JWT [RFC7519]. Usually a machine-readable identifier of the resource owner who authorized this token. | [optional] **tokenType** | **String** | TokenType is the introspected token's type, for example `access_token` or `refresh_token`. | [optional] diff --git a/sdk/js/swagger/src/model/AcceptLoginRequest.js b/sdk/js/swagger/src/model/AcceptLoginRequest.js index f9b3c51090..bfede23e16 100644 --- a/sdk/js/swagger/src/model/AcceptLoginRequest.js +++ b/sdk/js/swagger/src/model/AcceptLoginRequest.js @@ -62,6 +62,12 @@ if (data.hasOwnProperty('acr')) { obj['acr'] = ApiClient.convertToType(data['acr'], 'String') } + if (data.hasOwnProperty('force_subject_identifier')) { + obj['force_subject_identifier'] = ApiClient.convertToType( + data['force_subject_identifier'], + 'String' + ) + } if (data.hasOwnProperty('remember')) { obj['remember'] = ApiClient.convertToType(data['remember'], 'Boolean') } @@ -83,6 +89,11 @@ * @member {String} acr */ exports.prototype['acr'] = undefined + /** + * ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. + * @member {String} force_subject_identifier + */ + exports.prototype['force_subject_identifier'] = undefined /** * Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. * @member {Boolean} remember diff --git a/sdk/js/swagger/src/model/JSONWebKey.js b/sdk/js/swagger/src/model/JSONWebKey.js index 2735265b78..6738aca8f7 100644 --- a/sdk/js/swagger/src/model/JSONWebKey.js +++ b/sdk/js/swagger/src/model/JSONWebKey.js @@ -17,31 +17,32 @@ ;(function(root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. - define(['ApiClient'], factory) + define(['ApiClient', 'model/Certificate'], factory) } else if (typeof module === 'object' && module.exports) { // CommonJS-like environments that support module.exports, like Node. - module.exports = factory(require('../ApiClient')) + module.exports = factory(require('../ApiClient'), require('./Certificate')) } else { // Browser globals (root is window) if (!root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer) { root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer = {} } - root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JsonWebKey = factory( - root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.ApiClient + root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JSONWebKey = factory( + root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.ApiClient, + root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.Certificate ) } -})(this, function(ApiClient) { +})(this, function(ApiClient, Certificate) { 'use strict' /** - * The JsonWebKey model module. - * @module model/JsonWebKey + * The JSONWebKey model module. + * @module model/JSONWebKey * @version Latest */ /** - * Constructs a new JsonWebKey. - * @alias module:model/JsonWebKey + * Constructs a new JSONWebKey. + * @alias module:model/JSONWebKey * @class */ var exports = function() { @@ -49,144 +50,57 @@ } /** - * Constructs a JsonWebKey from a plain JavaScript object, optionally creating a new instance. + * Constructs a JSONWebKey from a plain JavaScript object, optionally creating a new instance. * Copies all relevant properties from data to obj if supplied or a new instance if not. * @param {Object} data The plain JavaScript object bearing properties of interest. - * @param {module:model/JsonWebKey} obj Optional instance to populate. - * @return {module:model/JsonWebKey} The populated JsonWebKey instance. + * @param {module:model/JSONWebKey} obj Optional instance to populate. + * @return {module:model/JSONWebKey} The populated JSONWebKey instance. */ exports.constructFromObject = function(data, obj) { if (data) { obj = obj || new exports() - if (data.hasOwnProperty('alg')) { - obj['alg'] = ApiClient.convertToType(data['alg'], 'String') + if (data.hasOwnProperty('Algorithm')) { + obj['Algorithm'] = ApiClient.convertToType(data['Algorithm'], 'String') } - if (data.hasOwnProperty('crv')) { - obj['crv'] = ApiClient.convertToType(data['crv'], 'String') + if (data.hasOwnProperty('Certificates')) { + obj['Certificates'] = ApiClient.convertToType(data['Certificates'], [ + Certificate + ]) } - if (data.hasOwnProperty('d')) { - obj['d'] = ApiClient.convertToType(data['d'], 'String') + if (data.hasOwnProperty('Key')) { + obj['Key'] = ApiClient.convertToType(data['Key'], Object) } - if (data.hasOwnProperty('dp')) { - obj['dp'] = ApiClient.convertToType(data['dp'], 'String') + if (data.hasOwnProperty('KeyID')) { + obj['KeyID'] = ApiClient.convertToType(data['KeyID'], 'String') } - if (data.hasOwnProperty('dq')) { - obj['dq'] = ApiClient.convertToType(data['dq'], 'String') - } - if (data.hasOwnProperty('e')) { - obj['e'] = ApiClient.convertToType(data['e'], 'String') - } - if (data.hasOwnProperty('k')) { - obj['k'] = ApiClient.convertToType(data['k'], 'String') - } - if (data.hasOwnProperty('kid')) { - obj['kid'] = ApiClient.convertToType(data['kid'], 'String') - } - if (data.hasOwnProperty('kty')) { - obj['kty'] = ApiClient.convertToType(data['kty'], 'String') - } - if (data.hasOwnProperty('n')) { - obj['n'] = ApiClient.convertToType(data['n'], 'String') - } - if (data.hasOwnProperty('p')) { - obj['p'] = ApiClient.convertToType(data['p'], 'String') - } - if (data.hasOwnProperty('q')) { - obj['q'] = ApiClient.convertToType(data['q'], 'String') - } - if (data.hasOwnProperty('qi')) { - obj['qi'] = ApiClient.convertToType(data['qi'], 'String') - } - if (data.hasOwnProperty('use')) { - obj['use'] = ApiClient.convertToType(data['use'], 'String') - } - if (data.hasOwnProperty('x')) { - obj['x'] = ApiClient.convertToType(data['x'], 'String') - } - if (data.hasOwnProperty('x5c')) { - obj['x5c'] = ApiClient.convertToType(data['x5c'], ['String']) - } - if (data.hasOwnProperty('y')) { - obj['y'] = ApiClient.convertToType(data['y'], 'String') + if (data.hasOwnProperty('Use')) { + obj['Use'] = ApiClient.convertToType(data['Use'], 'String') } } return obj } /** - * The \"alg\" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA \"JSON Web Signature and Encryption Algorithms\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. - * @member {String} alg - */ - exports.prototype['alg'] = undefined - /** - * @member {String} crv - */ - exports.prototype['crv'] = undefined - /** - * @member {String} d - */ - exports.prototype['d'] = undefined - /** - * @member {String} dp - */ - exports.prototype['dp'] = undefined - /** - * @member {String} dq - */ - exports.prototype['dq'] = undefined - /** - * @member {String} e - */ - exports.prototype['e'] = undefined - /** - * @member {String} k - */ - exports.prototype['k'] = undefined - /** - * The \"kid\" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the \"kid\" value is unspecified. When \"kid\" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct \"kid\" values. (One example in which different keys might use the same \"kid\" value is if they have different \"kty\" (key type) values but are considered to be equivalent alternatives by the application using them.) The \"kid\" value is a case-sensitive string. - * @member {String} kid - */ - exports.prototype['kid'] = undefined - /** - * The \"kty\" (key type) parameter identifies the cryptographic algorithm family used with the key, such as \"RSA\" or \"EC\". \"kty\" values should either be registered in the IANA \"JSON Web Key Types\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The \"kty\" value is a case-sensitive string. - * @member {String} kty - */ - exports.prototype['kty'] = undefined - /** - * @member {String} n - */ - exports.prototype['n'] = undefined - /** - * @member {String} p - */ - exports.prototype['p'] = undefined - /** - * @member {String} q - */ - exports.prototype['q'] = undefined - /** - * @member {String} qi + * @member {String} Algorithm */ - exports.prototype['qi'] = undefined + exports.prototype['Algorithm'] = undefined /** - * The \"use\" (public key use) parameter identifies the intended use of the public key. The \"use\" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly \"sig\" (signature) or \"enc\" (encryption). - * @member {String} use + * @member {Array.} Certificates */ - exports.prototype['use'] = undefined + exports.prototype['Certificates'] = undefined /** - * @member {String} x + * @member {Object} Key */ - exports.prototype['x'] = undefined + exports.prototype['Key'] = undefined /** - * The \"x5c\" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate. - * @member {Array.} x5c + * @member {String} KeyID */ - exports.prototype['x5c'] = undefined + exports.prototype['KeyID'] = undefined /** - * @member {String} y + * @member {String} Use */ - exports.prototype['y'] = undefined + exports.prototype['Use'] = undefined return exports }) diff --git a/sdk/js/swagger/src/model/JSONWebKeySet.js b/sdk/js/swagger/src/model/JSONWebKeySet.js index 3744d51b82..6b07a379fa 100644 --- a/sdk/js/swagger/src/model/JSONWebKeySet.js +++ b/sdk/js/swagger/src/model/JSONWebKeySet.js @@ -17,32 +17,32 @@ ;(function(root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. - define(['ApiClient', 'model/JsonWebKey'], factory) + define(['ApiClient', 'model/JSONWebKey'], factory) } else if (typeof module === 'object' && module.exports) { // CommonJS-like environments that support module.exports, like Node. - module.exports = factory(require('../ApiClient'), require('./JsonWebKey')) + module.exports = factory(require('../ApiClient'), require('./JSONWebKey')) } else { // Browser globals (root is window) if (!root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer) { root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer = {} } - root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JsonWebKeySet = factory( + root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JSONWebKeySet = factory( root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.ApiClient, - root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JsonWebKey + root.OryHydraCloudNativeOAuth20AndOpenIdConnectServer.JSONWebKey ) } -})(this, function(ApiClient, JsonWebKey) { +})(this, function(ApiClient, JSONWebKey) { 'use strict' /** - * The JsonWebKeySet model module. - * @module model/JsonWebKeySet + * The JSONWebKeySet model module. + * @module model/JSONWebKeySet * @version Latest */ /** - * Constructs a new JsonWebKeySet. - * @alias module:model/JsonWebKeySet + * Constructs a new JSONWebKeySet. + * @alias module:model/JSONWebKeySet * @class */ var exports = function() { @@ -50,26 +50,25 @@ } /** - * Constructs a JsonWebKeySet from a plain JavaScript object, optionally creating a new instance. + * Constructs a JSONWebKeySet from a plain JavaScript object, optionally creating a new instance. * Copies all relevant properties from data to obj if supplied or a new instance if not. * @param {Object} data The plain JavaScript object bearing properties of interest. - * @param {module:model/JsonWebKeySet} obj Optional instance to populate. - * @return {module:model/JsonWebKeySet} The populated JsonWebKeySet instance. + * @param {module:model/JSONWebKeySet} obj Optional instance to populate. + * @return {module:model/JSONWebKeySet} The populated JSONWebKeySet instance. */ exports.constructFromObject = function(data, obj) { if (data) { obj = obj || new exports() if (data.hasOwnProperty('keys')) { - obj['keys'] = ApiClient.convertToType(data['keys'], [JsonWebKey]) + obj['keys'] = ApiClient.convertToType(data['keys'], [JSONWebKey]) } } return obj } /** - * The value of the \"keys\" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired. - * @member {Array.} keys + * @member {Array.} keys */ exports.prototype['keys'] = undefined diff --git a/sdk/js/swagger/src/model/LoginRequest.js b/sdk/js/swagger/src/model/LoginRequest.js index ef2b9a8cb4..da7aa19491 100644 --- a/sdk/js/swagger/src/model/LoginRequest.js +++ b/sdk/js/swagger/src/model/LoginRequest.js @@ -131,7 +131,7 @@ */ exports.prototype['skip'] = undefined /** - * Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. + * Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail. * @member {String} subject */ exports.prototype['subject'] = undefined diff --git a/sdk/js/swagger/src/model/OAuth2Client.js b/sdk/js/swagger/src/model/OAuth2Client.js index 7b62b0d20d..53c14b679d 100644 --- a/sdk/js/swagger/src/model/OAuth2Client.js +++ b/sdk/js/swagger/src/model/OAuth2Client.js @@ -147,6 +147,12 @@ 'String' ) } + if (data.hasOwnProperty('subject_type')) { + obj['subject_type'] = ApiClient.convertToType( + data['subject_type'], + 'String' + ) + } if (data.hasOwnProperty('token_endpoint_auth_method')) { obj['token_endpoint_auth_method'] = ApiClient.convertToType( data['token_endpoint_auth_method'], @@ -255,6 +261,11 @@ * @member {String} sector_identifier_uri */ exports.prototype['sector_identifier_uri'] = undefined + /** + * SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`. + * @member {String} subject_type + */ + exports.prototype['subject_type'] = undefined /** * Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none. * @member {String} token_endpoint_auth_method diff --git a/sdk/js/swagger/src/model/OAuth2TokenIntrospection.js b/sdk/js/swagger/src/model/OAuth2TokenIntrospection.js index 9ae6c44799..9325ee5fdf 100644 --- a/sdk/js/swagger/src/model/OAuth2TokenIntrospection.js +++ b/sdk/js/swagger/src/model/OAuth2TokenIntrospection.js @@ -87,6 +87,12 @@ if (data.hasOwnProperty('nbf')) { obj['nbf'] = ApiClient.convertToType(data['nbf'], 'Number') } + if (data.hasOwnProperty('obfuscated_subject')) { + obj['obfuscated_subject'] = ApiClient.convertToType( + data['obfuscated_subject'], + 'String' + ) + } if (data.hasOwnProperty('scope')) { obj['scope'] = ApiClient.convertToType(data['scope'], 'String') } @@ -145,6 +151,11 @@ * @member {Number} nbf */ exports.prototype['nbf'] = undefined + /** + * ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization. It is the `sub` value of the ID Token that was issued. + * @member {String} obfuscated_subject + */ + exports.prototype['obfuscated_subject'] = undefined /** * Scope is a JSON string containing a space-separated list of scopes associated with this token. * @member {String} scope diff --git a/sdk/php/swagger/docs/Model/AcceptLoginRequest.md b/sdk/php/swagger/docs/Model/AcceptLoginRequest.md index 64e75cedb5..5b25329026 100644 --- a/sdk/php/swagger/docs/Model/AcceptLoginRequest.md +++ b/sdk/php/swagger/docs/Model/AcceptLoginRequest.md @@ -4,6 +4,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **acr** | **string** | ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication. | [optional] +**force_subject_identifier** | **string** | ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. | [optional] **remember** | **bool** | Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again. | [optional] **remember_for** | **int** | RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the authorization will be remembered indefinitely. | [optional] **subject** | **string** | Subject is the user ID of the end-user that authenticated. | [optional] diff --git a/sdk/php/swagger/docs/Model/JSONWebKey.md b/sdk/php/swagger/docs/Model/JSONWebKey.md index 36119ee941..d693ac2a66 100644 --- a/sdk/php/swagger/docs/Model/JSONWebKey.md +++ b/sdk/php/swagger/docs/Model/JSONWebKey.md @@ -1,25 +1,13 @@ -# JsonWebKey +# JSONWebKey ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**alg** | **string** | The \"alg\" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA \"JSON Web Signature and Encryption Algorithms\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. | [optional] -**crv** | **string** | | [optional] -**d** | **string** | | [optional] -**dp** | **string** | | [optional] -**dq** | **string** | | [optional] -**e** | **string** | | [optional] -**k** | **string** | | [optional] -**kid** | **string** | The \"kid\" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the \"kid\" value is unspecified. When \"kid\" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct \"kid\" values. (One example in which different keys might use the same \"kid\" value is if they have different \"kty\" (key type) values but are considered to be equivalent alternatives by the application using them.) The \"kid\" value is a case-sensitive string. | [optional] -**kty** | **string** | The \"kty\" (key type) parameter identifies the cryptographic algorithm family used with the key, such as \"RSA\" or \"EC\". \"kty\" values should either be registered in the IANA \"JSON Web Key Types\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The \"kty\" value is a case-sensitive string. | [optional] -**n** | **string** | | [optional] -**p** | **string** | | [optional] -**q** | **string** | | [optional] -**qi** | **string** | | [optional] -**use** | **string** | The \"use\" (public key use) parameter identifies the intended use of the public key. The \"use\" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly \"sig\" (signature) or \"enc\" (encryption). | [optional] -**x** | **string** | | [optional] -**x5c** | **string[]** | The \"x5c\" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate. | [optional] -**y** | **string** | | [optional] +**algorithm** | **string** | | [optional] +**certificates** | [**\Hydra\SDK\Model\Certificate[]**](Certificate.md) | | [optional] +**key** | **object** | | [optional] +**key_id** | **string** | | [optional] +**use** | **string** | | [optional] [[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/sdk/php/swagger/docs/Model/JSONWebKeySet.md b/sdk/php/swagger/docs/Model/JSONWebKeySet.md index 41cebd564e..4d85289d79 100644 --- a/sdk/php/swagger/docs/Model/JSONWebKeySet.md +++ b/sdk/php/swagger/docs/Model/JSONWebKeySet.md @@ -1,9 +1,9 @@ -# JsonWebKeySet +# JSONWebKeySet ## Properties Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- -**keys** | [**\Hydra\SDK\Model\JsonWebKey[]**](JsonWebKey.md) | The value of the \"keys\" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired. | [optional] +**keys** | [**\Hydra\SDK\Model\JSONWebKey[]**](JSONWebKey.md) | | [optional] [[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/sdk/php/swagger/docs/Model/LoginRequest.md b/sdk/php/swagger/docs/Model/LoginRequest.md index 9fb65f50fa..309a44bd92 100644 --- a/sdk/php/swagger/docs/Model/LoginRequest.md +++ b/sdk/php/swagger/docs/Model/LoginRequest.md @@ -9,7 +9,7 @@ Name | Type | Description | Notes **request_url** | **string** | RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters. | [optional] **requested_scope** | **string[]** | RequestedScope contains all scopes requested by the OAuth 2.0 client. | [optional] **skip** | **bool** | Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL. This feature allows you to update / set session information. | [optional] -**subject** | **string** | Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. | [optional] +**subject** | **string** | Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail. | [optional] [[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/sdk/php/swagger/docs/Model/OAuth2Client.md b/sdk/php/swagger/docs/Model/OAuth2Client.md index 1e84e7077f..564841cba4 100644 --- a/sdk/php/swagger/docs/Model/OAuth2Client.md +++ b/sdk/php/swagger/docs/Model/OAuth2Client.md @@ -21,6 +21,7 @@ Name | Type | Description | Notes **response_types** | **string[]** | ResponseTypes is an array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint. | [optional] **scope** | **string** | Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens. | [optional] **sector_identifier_uri** | **string** | URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values. | [optional] +**subject_type** | **string** | SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`. | [optional] **token_endpoint_auth_method** | **string** | Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none. | [optional] **tos_uri** | **string** | TermsOfServiceURI is a URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client. | [optional] **userinfo_signed_response_alg** | **string** | JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type. | [optional] diff --git a/sdk/php/swagger/docs/Model/OAuth2TokenIntrospection.md b/sdk/php/swagger/docs/Model/OAuth2TokenIntrospection.md index feb542a932..27f69213c0 100644 --- a/sdk/php/swagger/docs/Model/OAuth2TokenIntrospection.md +++ b/sdk/php/swagger/docs/Model/OAuth2TokenIntrospection.md @@ -11,6 +11,7 @@ Name | Type | Description | Notes **iat** | **int** | Issued at is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token was originally issued. | [optional] **iss** | **string** | IssuerURL is a string representing the issuer of this token | [optional] **nbf** | **int** | NotBefore is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token is not to be used before. | [optional] +**obfuscated_subject** | **string** | ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization. It is the `sub` value of the ID Token that was issued. | [optional] **scope** | **string** | Scope is a JSON string containing a space-separated list of scopes associated with this token. | [optional] **sub** | **string** | Subject of the token, as defined in JWT [RFC7519]. Usually a machine-readable identifier of the resource owner who authorized this token. | [optional] **token_type** | **string** | TokenType is the introspected token's type, for example `access_token` or `refresh_token`. | [optional] diff --git a/sdk/php/swagger/lib/Model/AcceptLoginRequest.php b/sdk/php/swagger/lib/Model/AcceptLoginRequest.php index ae0e5548a0..e300ec8d51 100644 --- a/sdk/php/swagger/lib/Model/AcceptLoginRequest.php +++ b/sdk/php/swagger/lib/Model/AcceptLoginRequest.php @@ -55,6 +55,7 @@ class AcceptLoginRequest implements ArrayAccess */ protected static $swaggerTypes = [ 'acr' => 'string', + 'force_subject_identifier' => 'string', 'remember' => 'bool', 'remember_for' => 'int', 'subject' => 'string' @@ -66,6 +67,7 @@ class AcceptLoginRequest implements ArrayAccess */ protected static $swaggerFormats = [ 'acr' => null, + 'force_subject_identifier' => null, 'remember' => null, 'remember_for' => 'int64', 'subject' => null @@ -87,6 +89,7 @@ public static function swaggerFormats() */ protected static $attributeMap = [ 'acr' => 'acr', + 'force_subject_identifier' => 'force_subject_identifier', 'remember' => 'remember', 'remember_for' => 'remember_for', 'subject' => 'subject' @@ -99,6 +102,7 @@ public static function swaggerFormats() */ protected static $setters = [ 'acr' => 'setAcr', + 'force_subject_identifier' => 'setForceSubjectIdentifier', 'remember' => 'setRemember', 'remember_for' => 'setRememberFor', 'subject' => 'setSubject' @@ -111,6 +115,7 @@ public static function swaggerFormats() */ protected static $getters = [ 'acr' => 'getAcr', + 'force_subject_identifier' => 'getForceSubjectIdentifier', 'remember' => 'getRemember', 'remember_for' => 'getRememberFor', 'subject' => 'getSubject' @@ -148,6 +153,7 @@ public static function getters() public function __construct(array $data = null) { $this->container['acr'] = isset($data['acr']) ? $data['acr'] : null; + $this->container['force_subject_identifier'] = isset($data['force_subject_identifier']) ? $data['force_subject_identifier'] : null; $this->container['remember'] = isset($data['remember']) ? $data['remember'] : null; $this->container['remember_for'] = isset($data['remember_for']) ? $data['remember_for'] : null; $this->container['subject'] = isset($data['subject']) ? $data['subject'] : null; @@ -199,6 +205,27 @@ public function setAcr($acr) return $this; } + /** + * Gets force_subject_identifier + * @return string + */ + public function getForceSubjectIdentifier() + { + return $this->container['force_subject_identifier']; + } + + /** + * Sets force_subject_identifier + * @param string $force_subject_identifier ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail. + * @return $this + */ + public function setForceSubjectIdentifier($force_subject_identifier) + { + $this->container['force_subject_identifier'] = $force_subject_identifier; + + return $this; + } + /** * Gets remember * @return bool diff --git a/sdk/php/swagger/lib/Model/JSONWebKey.php b/sdk/php/swagger/lib/Model/JSONWebKey.php index 4101c09bc0..50080d87c7 100644 --- a/sdk/php/swagger/lib/Model/JSONWebKey.php +++ b/sdk/php/swagger/lib/Model/JSONWebKey.php @@ -1,6 +1,6 @@ 'string', - 'crv' => 'string', - 'd' => 'string', - 'dp' => 'string', - 'dq' => 'string', - 'e' => 'string', - 'k' => 'string', - 'kid' => 'string', - 'kty' => 'string', - 'n' => 'string', - 'p' => 'string', - 'q' => 'string', - 'qi' => 'string', - 'use' => 'string', - 'x' => 'string', - 'x5c' => 'string[]', - 'y' => 'string' + 'algorithm' => 'string', + 'certificates' => '\Hydra\SDK\Model\Certificate[]', + 'key' => 'object', + 'key_id' => 'string', + 'use' => 'string' ]; /** @@ -78,23 +66,11 @@ class JsonWebKey implements ArrayAccess * @var string[] */ protected static $swaggerFormats = [ - 'alg' => null, - 'crv' => null, - 'd' => null, - 'dp' => null, - 'dq' => null, - 'e' => null, - 'k' => null, - 'kid' => null, - 'kty' => null, - 'n' => null, - 'p' => null, - 'q' => null, - 'qi' => null, - 'use' => null, - 'x' => null, - 'x5c' => null, - 'y' => null + 'algorithm' => null, + 'certificates' => null, + 'key' => null, + 'key_id' => null, + 'use' => null ]; public static function swaggerTypes() @@ -112,23 +88,11 @@ public static function swaggerFormats() * @var string[] */ protected static $attributeMap = [ - 'alg' => 'alg', - 'crv' => 'crv', - 'd' => 'd', - 'dp' => 'dp', - 'dq' => 'dq', - 'e' => 'e', - 'k' => 'k', - 'kid' => 'kid', - 'kty' => 'kty', - 'n' => 'n', - 'p' => 'p', - 'q' => 'q', - 'qi' => 'qi', - 'use' => 'use', - 'x' => 'x', - 'x5c' => 'x5c', - 'y' => 'y' + 'algorithm' => 'Algorithm', + 'certificates' => 'Certificates', + 'key' => 'Key', + 'key_id' => 'KeyID', + 'use' => 'Use' ]; @@ -137,23 +101,11 @@ public static function swaggerFormats() * @var string[] */ protected static $setters = [ - 'alg' => 'setAlg', - 'crv' => 'setCrv', - 'd' => 'setD', - 'dp' => 'setDp', - 'dq' => 'setDq', - 'e' => 'setE', - 'k' => 'setK', - 'kid' => 'setKid', - 'kty' => 'setKty', - 'n' => 'setN', - 'p' => 'setP', - 'q' => 'setQ', - 'qi' => 'setQi', - 'use' => 'setUse', - 'x' => 'setX', - 'x5c' => 'setX5c', - 'y' => 'setY' + 'algorithm' => 'setAlgorithm', + 'certificates' => 'setCertificates', + 'key' => 'setKey', + 'key_id' => 'setKeyId', + 'use' => 'setUse' ]; @@ -162,23 +114,11 @@ public static function swaggerFormats() * @var string[] */ protected static $getters = [ - 'alg' => 'getAlg', - 'crv' => 'getCrv', - 'd' => 'getD', - 'dp' => 'getDp', - 'dq' => 'getDq', - 'e' => 'getE', - 'k' => 'getK', - 'kid' => 'getKid', - 'kty' => 'getKty', - 'n' => 'getN', - 'p' => 'getP', - 'q' => 'getQ', - 'qi' => 'getQi', - 'use' => 'getUse', - 'x' => 'getX', - 'x5c' => 'getX5c', - 'y' => 'getY' + 'algorithm' => 'getAlgorithm', + 'certificates' => 'getCertificates', + 'key' => 'getKey', + 'key_id' => 'getKeyId', + 'use' => 'getUse' ]; public static function attributeMap() @@ -212,23 +152,11 @@ public static function getters() */ public function __construct(array $data = null) { - $this->container['alg'] = isset($data['alg']) ? $data['alg'] : null; - $this->container['crv'] = isset($data['crv']) ? $data['crv'] : null; - $this->container['d'] = isset($data['d']) ? $data['d'] : null; - $this->container['dp'] = isset($data['dp']) ? $data['dp'] : null; - $this->container['dq'] = isset($data['dq']) ? $data['dq'] : null; - $this->container['e'] = isset($data['e']) ? $data['e'] : null; - $this->container['k'] = isset($data['k']) ? $data['k'] : null; - $this->container['kid'] = isset($data['kid']) ? $data['kid'] : null; - $this->container['kty'] = isset($data['kty']) ? $data['kty'] : null; - $this->container['n'] = isset($data['n']) ? $data['n'] : null; - $this->container['p'] = isset($data['p']) ? $data['p'] : null; - $this->container['q'] = isset($data['q']) ? $data['q'] : null; - $this->container['qi'] = isset($data['qi']) ? $data['qi'] : null; + $this->container['algorithm'] = isset($data['algorithm']) ? $data['algorithm'] : null; + $this->container['certificates'] = isset($data['certificates']) ? $data['certificates'] : null; + $this->container['key'] = isset($data['key']) ? $data['key'] : null; + $this->container['key_id'] = isset($data['key_id']) ? $data['key_id'] : null; $this->container['use'] = isset($data['use']) ? $data['use'] : null; - $this->container['x'] = isset($data['x']) ? $data['x'] : null; - $this->container['x5c'] = isset($data['x5c']) ? $data['x5c'] : null; - $this->container['y'] = isset($data['y']) ? $data['y'] : null; } /** @@ -257,274 +185,85 @@ public function valid() /** - * Gets alg + * Gets algorithm * @return string */ - public function getAlg() + public function getAlgorithm() { - return $this->container['alg']; + return $this->container['algorithm']; } /** - * Sets alg - * @param string $alg The \"alg\" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA \"JSON Web Signature and Encryption Algorithms\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. + * Sets algorithm + * @param string $algorithm * @return $this */ - public function setAlg($alg) + public function setAlgorithm($algorithm) { - $this->container['alg'] = $alg; + $this->container['algorithm'] = $algorithm; return $this; } /** - * Gets crv - * @return string - */ - public function getCrv() - { - return $this->container['crv']; - } - - /** - * Sets crv - * @param string $crv - * @return $this - */ - public function setCrv($crv) - { - $this->container['crv'] = $crv; - - return $this; - } - - /** - * Gets d - * @return string + * Gets certificates + * @return \Hydra\SDK\Model\Certificate[] */ - public function getD() + public function getCertificates() { - return $this->container['d']; + return $this->container['certificates']; } /** - * Sets d - * @param string $d + * Sets certificates + * @param \Hydra\SDK\Model\Certificate[] $certificates * @return $this */ - public function setD($d) + public function setCertificates($certificates) { - $this->container['d'] = $d; + $this->container['certificates'] = $certificates; return $this; } /** - * Gets dp - * @return string + * Gets key + * @return object */ - public function getDp() + public function getKey() { - return $this->container['dp']; + return $this->container['key']; } /** - * Sets dp - * @param string $dp + * Sets key + * @param object $key * @return $this */ - public function setDp($dp) + public function setKey($key) { - $this->container['dp'] = $dp; + $this->container['key'] = $key; return $this; } /** - * Gets dq + * Gets key_id * @return string */ - public function getDq() + public function getKeyId() { - return $this->container['dq']; + return $this->container['key_id']; } /** - * Sets dq - * @param string $dq + * Sets key_id + * @param string $key_id * @return $this */ - public function setDq($dq) + public function setKeyId($key_id) { - $this->container['dq'] = $dq; - - return $this; - } - - /** - * Gets e - * @return string - */ - public function getE() - { - return $this->container['e']; - } - - /** - * Sets e - * @param string $e - * @return $this - */ - public function setE($e) - { - $this->container['e'] = $e; - - return $this; - } - - /** - * Gets k - * @return string - */ - public function getK() - { - return $this->container['k']; - } - - /** - * Sets k - * @param string $k - * @return $this - */ - public function setK($k) - { - $this->container['k'] = $k; - - return $this; - } - - /** - * Gets kid - * @return string - */ - public function getKid() - { - return $this->container['kid']; - } - - /** - * Sets kid - * @param string $kid The \"kid\" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the \"kid\" value is unspecified. When \"kid\" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct \"kid\" values. (One example in which different keys might use the same \"kid\" value is if they have different \"kty\" (key type) values but are considered to be equivalent alternatives by the application using them.) The \"kid\" value is a case-sensitive string. - * @return $this - */ - public function setKid($kid) - { - $this->container['kid'] = $kid; - - return $this; - } - - /** - * Gets kty - * @return string - */ - public function getKty() - { - return $this->container['kty']; - } - - /** - * Sets kty - * @param string $kty The \"kty\" (key type) parameter identifies the cryptographic algorithm family used with the key, such as \"RSA\" or \"EC\". \"kty\" values should either be registered in the IANA \"JSON Web Key Types\" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The \"kty\" value is a case-sensitive string. - * @return $this - */ - public function setKty($kty) - { - $this->container['kty'] = $kty; - - return $this; - } - - /** - * Gets n - * @return string - */ - public function getN() - { - return $this->container['n']; - } - - /** - * Sets n - * @param string $n - * @return $this - */ - public function setN($n) - { - $this->container['n'] = $n; - - return $this; - } - - /** - * Gets p - * @return string - */ - public function getP() - { - return $this->container['p']; - } - - /** - * Sets p - * @param string $p - * @return $this - */ - public function setP($p) - { - $this->container['p'] = $p; - - return $this; - } - - /** - * Gets q - * @return string - */ - public function getQ() - { - return $this->container['q']; - } - - /** - * Sets q - * @param string $q - * @return $this - */ - public function setQ($q) - { - $this->container['q'] = $q; - - return $this; - } - - /** - * Gets qi - * @return string - */ - public function getQi() - { - return $this->container['qi']; - } - - /** - * Sets qi - * @param string $qi - * @return $this - */ - public function setQi($qi) - { - $this->container['qi'] = $qi; + $this->container['key_id'] = $key_id; return $this; } @@ -540,7 +279,7 @@ public function getUse() /** * Sets use - * @param string $use The \"use\" (public key use) parameter identifies the intended use of the public key. The \"use\" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly \"sig\" (signature) or \"enc\" (encryption). + * @param string $use * @return $this */ public function setUse($use) @@ -549,69 +288,6 @@ public function setUse($use) return $this; } - - /** - * Gets x - * @return string - */ - public function getX() - { - return $this->container['x']; - } - - /** - * Sets x - * @param string $x - * @return $this - */ - public function setX($x) - { - $this->container['x'] = $x; - - return $this; - } - - /** - * Gets x5c - * @return string[] - */ - public function getX5c() - { - return $this->container['x5c']; - } - - /** - * Sets x5c - * @param string[] $x5c The \"x5c\" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate. - * @return $this - */ - public function setX5c($x5c) - { - $this->container['x5c'] = $x5c; - - return $this; - } - - /** - * Gets y - * @return string - */ - public function getY() - { - return $this->container['y']; - } - - /** - * Sets y - * @param string $y - * @return $this - */ - public function setY($y) - { - $this->container['y'] = $y; - - return $this; - } /** * Returns true if offset exists. False otherwise. * @param integer $offset Offset diff --git a/sdk/php/swagger/lib/Model/JSONWebKeySet.php b/sdk/php/swagger/lib/Model/JSONWebKeySet.php index bcd3ceeea0..677d563a57 100644 --- a/sdk/php/swagger/lib/Model/JSONWebKeySet.php +++ b/sdk/php/swagger/lib/Model/JSONWebKeySet.php @@ -1,6 +1,6 @@ '\Hydra\SDK\Model\JsonWebKey[]' + 'keys' => '\Hydra\SDK\Model\JSONWebKey[]' ]; /** @@ -162,7 +162,7 @@ public function valid() /** * Gets keys - * @return \Hydra\SDK\Model\JsonWebKey[] + * @return \Hydra\SDK\Model\JSONWebKey[] */ public function getKeys() { @@ -171,7 +171,7 @@ public function getKeys() /** * Sets keys - * @param \Hydra\SDK\Model\JsonWebKey[] $keys The value of the \"keys\" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired. + * @param \Hydra\SDK\Model\JSONWebKey[] $keys * @return $this */ public function setKeys($keys) diff --git a/sdk/php/swagger/lib/Model/LoginRequest.php b/sdk/php/swagger/lib/Model/LoginRequest.php index d7537cab27..2698fc7869 100644 --- a/sdk/php/swagger/lib/Model/LoginRequest.php +++ b/sdk/php/swagger/lib/Model/LoginRequest.php @@ -333,7 +333,7 @@ public function getSubject() /** * Sets subject - * @param string $subject Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. + * @param string $subject Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail. * @return $this */ public function setSubject($subject) diff --git a/sdk/php/swagger/lib/Model/OAuth2Client.php b/sdk/php/swagger/lib/Model/OAuth2Client.php index 5edc64755b..43b99298ab 100644 --- a/sdk/php/swagger/lib/Model/OAuth2Client.php +++ b/sdk/php/swagger/lib/Model/OAuth2Client.php @@ -72,6 +72,7 @@ class OAuth2Client implements ArrayAccess 'response_types' => 'string[]', 'scope' => 'string', 'sector_identifier_uri' => 'string', + 'subject_type' => 'string', 'token_endpoint_auth_method' => 'string', 'tos_uri' => 'string', 'userinfo_signed_response_alg' => 'string' @@ -100,6 +101,7 @@ class OAuth2Client implements ArrayAccess 'response_types' => null, 'scope' => null, 'sector_identifier_uri' => null, + 'subject_type' => null, 'token_endpoint_auth_method' => null, 'tos_uri' => null, 'userinfo_signed_response_alg' => null @@ -138,6 +140,7 @@ public static function swaggerFormats() 'response_types' => 'response_types', 'scope' => 'scope', 'sector_identifier_uri' => 'sector_identifier_uri', + 'subject_type' => 'subject_type', 'token_endpoint_auth_method' => 'token_endpoint_auth_method', 'tos_uri' => 'tos_uri', 'userinfo_signed_response_alg' => 'userinfo_signed_response_alg' @@ -167,6 +170,7 @@ public static function swaggerFormats() 'response_types' => 'setResponseTypes', 'scope' => 'setScope', 'sector_identifier_uri' => 'setSectorIdentifierUri', + 'subject_type' => 'setSubjectType', 'token_endpoint_auth_method' => 'setTokenEndpointAuthMethod', 'tos_uri' => 'setTosUri', 'userinfo_signed_response_alg' => 'setUserinfoSignedResponseAlg' @@ -196,6 +200,7 @@ public static function swaggerFormats() 'response_types' => 'getResponseTypes', 'scope' => 'getScope', 'sector_identifier_uri' => 'getSectorIdentifierUri', + 'subject_type' => 'getSubjectType', 'token_endpoint_auth_method' => 'getTokenEndpointAuthMethod', 'tos_uri' => 'getTosUri', 'userinfo_signed_response_alg' => 'getUserinfoSignedResponseAlg' @@ -250,6 +255,7 @@ public function __construct(array $data = null) $this->container['response_types'] = isset($data['response_types']) ? $data['response_types'] : null; $this->container['scope'] = isset($data['scope']) ? $data['scope'] : null; $this->container['sector_identifier_uri'] = isset($data['sector_identifier_uri']) ? $data['sector_identifier_uri'] : null; + $this->container['subject_type'] = isset($data['subject_type']) ? $data['subject_type'] : null; $this->container['token_endpoint_auth_method'] = isset($data['token_endpoint_auth_method']) ? $data['token_endpoint_auth_method'] : null; $this->container['tos_uri'] = isset($data['tos_uri']) ? $data['tos_uri'] : null; $this->container['userinfo_signed_response_alg'] = isset($data['userinfo_signed_response_alg']) ? $data['userinfo_signed_response_alg'] : null; @@ -670,6 +676,27 @@ public function setSectorIdentifierUri($sector_identifier_uri) return $this; } + /** + * Gets subject_type + * @return string + */ + public function getSubjectType() + { + return $this->container['subject_type']; + } + + /** + * Sets subject_type + * @param string $subject_type SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`. + * @return $this + */ + public function setSubjectType($subject_type) + { + $this->container['subject_type'] = $subject_type; + + return $this; + } + /** * Gets token_endpoint_auth_method * @return string diff --git a/sdk/php/swagger/lib/Model/OAuth2TokenIntrospection.php b/sdk/php/swagger/lib/Model/OAuth2TokenIntrospection.php index 1485ba39f5..58d2d86fea 100644 --- a/sdk/php/swagger/lib/Model/OAuth2TokenIntrospection.php +++ b/sdk/php/swagger/lib/Model/OAuth2TokenIntrospection.php @@ -63,6 +63,7 @@ class OAuth2TokenIntrospection implements ArrayAccess 'iat' => 'int', 'iss' => 'string', 'nbf' => 'int', + 'obfuscated_subject' => 'string', 'scope' => 'string', 'sub' => 'string', 'token_type' => 'string', @@ -82,6 +83,7 @@ class OAuth2TokenIntrospection implements ArrayAccess 'iat' => 'int64', 'iss' => null, 'nbf' => 'int64', + 'obfuscated_subject' => null, 'scope' => null, 'sub' => null, 'token_type' => null, @@ -111,6 +113,7 @@ public static function swaggerFormats() 'iat' => 'iat', 'iss' => 'iss', 'nbf' => 'nbf', + 'obfuscated_subject' => 'obfuscated_subject', 'scope' => 'scope', 'sub' => 'sub', 'token_type' => 'token_type', @@ -131,6 +134,7 @@ public static function swaggerFormats() 'iat' => 'setIat', 'iss' => 'setIss', 'nbf' => 'setNbf', + 'obfuscated_subject' => 'setObfuscatedSubject', 'scope' => 'setScope', 'sub' => 'setSub', 'token_type' => 'setTokenType', @@ -151,6 +155,7 @@ public static function swaggerFormats() 'iat' => 'getIat', 'iss' => 'getIss', 'nbf' => 'getNbf', + 'obfuscated_subject' => 'getObfuscatedSubject', 'scope' => 'getScope', 'sub' => 'getSub', 'token_type' => 'getTokenType', @@ -196,6 +201,7 @@ public function __construct(array $data = null) $this->container['iat'] = isset($data['iat']) ? $data['iat'] : null; $this->container['iss'] = isset($data['iss']) ? $data['iss'] : null; $this->container['nbf'] = isset($data['nbf']) ? $data['nbf'] : null; + $this->container['obfuscated_subject'] = isset($data['obfuscated_subject']) ? $data['obfuscated_subject'] : null; $this->container['scope'] = isset($data['scope']) ? $data['scope'] : null; $this->container['sub'] = isset($data['sub']) ? $data['sub'] : null; $this->container['token_type'] = isset($data['token_type']) ? $data['token_type'] : null; @@ -401,6 +407,27 @@ public function setNbf($nbf) return $this; } + /** + * Gets obfuscated_subject + * @return string + */ + public function getObfuscatedSubject() + { + return $this->container['obfuscated_subject']; + } + + /** + * Sets obfuscated_subject + * @param string $obfuscated_subject ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization. It is the `sub` value of the ID Token that was issued. + * @return $this + */ + public function setObfuscatedSubject($obfuscated_subject) + { + $this->container['obfuscated_subject'] = $obfuscated_subject; + + return $this; + } + /** * Gets scope * @return string