From 751dbb546ea6cd541ebe81d1da114d7ea07e2eec Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Wed, 27 Oct 2021 17:24:25 -0600 Subject: [PATCH 01/29] add refresh_token grace period --- driver/config/provider.go | 5 ++ persistence/sql/persister_oauth2.go | 106 ++++++++++++++++++++++++---- spec/config.json | 16 ++++- 3 files changed, 114 insertions(+), 13 deletions(-) diff --git a/driver/config/provider.go b/driver/config/provider.go index e8bff7ef09..f5925047c8 100644 --- a/driver/config/provider.go +++ b/driver/config/provider.go @@ -65,6 +65,7 @@ const ( KeyOAuth2LegacyErrors = "oauth2.include_legacy_error_fields" KeyExcludeNotBeforeClaim = "oauth2.exclude_not_before_claim" KeyAllowedTopLevelClaims = "oauth2.allowed_top_level_claims" + KeyRefreshTokenRotationGracePeriod = "oauth2.refresh_token_rotation.grace_period" ) const DSNMemory = "memory" @@ -428,3 +429,7 @@ func (p *Provider) CGroupsV1AutoMaxProcsEnabled() bool { func (p *Provider) GrantAllClientCredentialsScopesPerDefault() bool { return p.p.Bool(KeyGrantAllClientCredentialsScopesPerDefault) } + +func(p *Provider) RefreshTokenRotationGracePeriod() time.Duration { + return p.p.DurationF(KeyRefreshTokenRotationGracePeriod, 0) +} diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index 1a1803597a..eaa898bc30 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -56,7 +56,11 @@ const ( ) func (r OAuth2RequestSQL) TableName() string { - return "hydra_oauth2_" + string(r.Table) + return r.Table.TableName() +} + +func (table tableName) TableName() string { + return "hydra_oauth2_" + string(table) } func (p *Persister) sqlSchemaFromRequest(rawSignature string, r fosite.Requester, table tableName) (*OAuth2RequestSQL, error) { @@ -67,19 +71,11 @@ func (p *Persister) sqlSchemaFromRequest(rawSignature string, r fosite.Requester subject = r.GetSession().GetSubject() } - session, err := json.Marshal(r.GetSession()) + session, err := p.marshalSession(r.GetSession()) if err != nil { return nil, errorsx.WithStack(err) } - if p.config.EncryptSessionData() { - ciphertext, err := p.r.KeyCipher().Encrypt(session) - if err != nil { - return nil, errorsx.WithStack(err) - } - session = []byte(ciphertext) - } - var challenge sql.NullString rr, ok := r.GetSession().(*oauth2.Session) if !ok && r.GetSession() != nil { @@ -108,6 +104,31 @@ func (p *Persister) sqlSchemaFromRequest(rawSignature string, r fosite.Requester }, nil } +func (p *Persister) marshalSession(session fosite.Session) ([]byte, error) { + sessionBytes, err := json.Marshal(session) + if err != nil { + return nil, err + } + + if sessionBytes, err = p.maybeEncryptSession(sessionBytes); err != nil { + return nil, err + } + return sessionBytes, nil +} + +// MaybeEncryptSession encrypt a session if configuration indicates it should +func (p *Persister) maybeEncryptSession(session []byte) ([]byte, error) { + if !p.config.EncryptSessionData() { + return session, nil + } + + ciphertext, err := p.r.KeyCipher().Encrypt(session) + if err != nil { + return nil, err + } + return []byte(ciphertext), nil +} + func (r *OAuth2RequestSQL) toRequest(ctx context.Context, session fosite.Session, p *Persister) (*fosite.Request, error) { sess := r.Session if !gjson.ValidBytes(sess) { @@ -219,6 +240,29 @@ func (p *Persister) createSession(ctx context.Context, signature string, request return nil } +func (p *Persister) updateSession(ctx context.Context, requestId string, session fosite.Session, table tableName) error { + _, ok := session.(*oauth2.Session) + if !ok && session != nil { + return errors.Errorf("Expected session to be of type *Session, but got: %T", session) + } + sessionBytes, err := p.marshalSession(session) + if err != nil { + return err + } + + updateSql := fmt.Sprintf("UPDATE %s SET session_data = ? WHERE request_id = ?", table.TableName()) + + return p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { + err := p.Connection(ctx).RawQuery(updateSql, sessionBytes, requestId).Exec() + if errors.Is(err, sql.ErrNoRows) { + return errorsx.WithStack(fosite.ErrNotFound) + } else if err != nil { + return sqlcon.HandleError(err) + } + return err + }) +} + func (p *Persister) findSessionBySignature(ctx context.Context, rawSignature string, session fosite.Session, table tableName) (fosite.Requester, error) { rawSignature = p.hashSignature(rawSignature, table) @@ -247,6 +291,33 @@ func (p *Persister) findSessionBySignature(ctx context.Context, rawSignature str }) } +func (p *Persister) findSessionByRequestId(ctx context.Context, requestId string, table tableName) (fosite.Requester, error) { + r := OAuth2RequestSQL{Table: table} + var fr fosite.Requester + session := new(oauth2.Session) + + return fr, p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { + err := p.Connection(ctx).Where("request_id = ?", requestId).First(&r) + if errors.Is(err, sql.ErrNoRows) { + return errorsx.WithStack(fosite.ErrNotFound) + } else if err != nil { + return sqlcon.HandleError(err) + } else if !r.Active { + fr, err = r.toRequest(ctx, session, p) + if err != nil { + return err + } else if table == sqlTableCode { + return errorsx.WithStack(fosite.ErrInvalidatedAuthorizeCode) + } + + return errorsx.WithStack(fosite.ErrInactiveToken) + } + + fr, err = r.toRequest(ctx, session, p) + return err + }) +} + func (p *Persister) deleteSessionBySignature(ctx context.Context, signature string, table tableName) error { signature = p.hashSignature(signature, table) @@ -353,8 +424,19 @@ func (p *Persister) DeletePKCERequestSession(ctx context.Context, signature stri return p.deleteSessionBySignature(ctx, signature, sqlTablePKCE) } -func (p *Persister) RevokeRefreshToken(ctx context.Context, id string) error { - return p.deactivateSessionByRequestID(ctx, id, sqlTableRefresh) +func (p *Persister) RevokeRefreshToken(ctx context.Context, requestId string) error { + gracePeriod := p.config.RefreshTokenRotationGracePeriod() + if gracePeriod > 0 { + if requester, err := p.findSessionByRequestId(ctx, requestId, sqlTableRefresh); err != nil { + p.l.Errorf("session_id: %s not found grace period not applied", requestId) + } else { + session := requester.GetSession() + session.SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(gracePeriod)) + p.updateSession(ctx, requestId, session, sqlTableRefresh) + return nil + } + } + return p.deactivateSessionByRequestID(ctx, requestId, sqlTableRefresh) } func (p *Persister) RevokeAccessToken(ctx context.Context, id string) error { diff --git a/spec/config.json b/spec/config.json index 3e5faf042b..67ca5f25c1 100644 --- a/spec/config.json +++ b/spec/config.json @@ -867,9 +867,23 @@ ] } } + }, + "refresh_token_rotation": { + "type": "object", + "properties": { + "grace_period": { + "description": "Configures how long a Refresh Token remains valid after it has been used.", + "default": "0h", + "allOf": [ + { + "$ref": "#/definitions/duration" + } + ] + } } } - }, + } + }, "secrets": { "type": "object", "additionalProperties": false, From 219a58e65bb15c8b2fd3415aa1beebb1a1f62395 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Fri, 29 Oct 2021 16:41:55 -0600 Subject: [PATCH 02/29] add migration for used on refresh token --- ...22152_add_refresh_token_used_flag.down.sql | 1 + ...5122152_add_refresh_token_used_flag.up.sql | 1 + scripts/create-migration.sh | 69 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.down.sql create mode 100644 persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.up.sql create mode 100644 scripts/create-migration.sh diff --git a/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.down.sql b/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.down.sql new file mode 100644 index 0000000000..94c35e4c72 --- /dev/null +++ b/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.down.sql @@ -0,0 +1 @@ +ALTER TABLE hydra_oauth2_refresh DROP COLUMN used; diff --git a/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.up.sql b/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.up.sql new file mode 100644 index 0000000000..fb92c2b17c --- /dev/null +++ b/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.up.sql @@ -0,0 +1 @@ +ALTER TABLE hydra_oauth2_refresh ADD used bool DEFAULT false; diff --git a/scripts/create-migration.sh b/scripts/create-migration.sh new file mode 100644 index 0000000000..f0db30f556 --- /dev/null +++ b/scripts/create-migration.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# create-migration.sh is a helper script to generate sql migration files. +# Migration file names follow the date-time naming convention established by Hydra. + +set -Eeo pipefail + +if [ -n "${DEBUG}" ]; then + set -x +fi + +migration_dir="$( dirname "${BASH_SOURCE[0]}" )/../persistence/sql/migrations/" +supported_dialects=(cockroach mysql postgres sqlite) + +if [ ! -d "$migration_dir" ]; then + echo "expected $migration_dir to exist" + exit 1 +fi + +function usage() { +cat << EOF + +Create up and down migration files for SQLite, CockroachDB, PostgreSQL + $0 --all-dialects + + --all-dialects create a file for each supported dialect (${supported_dialects[*]}) + + e.g. + $0 add_column_to_table + $0 add_column_to_table --all-dialects +EOF +} + +if [ -z "${1}" ]; then + usage + exit 1 +fi + +if [[ ! "${1}" =~ ^[A-Za-z_]+$ ]]; then + echo "invalid migration name: ${1} must be only A-Z, a-z, or _" + usage + exit 1 +fi + +timestamp=$(date -u +%Y%m%d%H%M%S%5N) +echo $timestamp + +file_prefix="${timestamp}_${1}" + +function create_files() { + dialect=$1 + filename="${file_prefix}" + + if [ ! -z "${dialect}" ]; then + filename="${filename}.${dialect}." + fi + + touch "${migration_dir}${filename}.up.sql" + touch "${migration_dir}${filename}.down.sql" +} + +if [ "${2}" == "--all-dialects" ]; then + for dialect in "${supported_dialects[@]}" + do + create_files $dialect + done +else + create_files +fi From efa3b8e5391f8564253ca728bc63670e2a456389 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Fri, 29 Oct 2021 16:42:21 -0600 Subject: [PATCH 03/29] add refresh token grace period --- persistence/sql/models/models.go | 1 + persistence/sql/persister_oauth2.go | 74 +++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 persistence/sql/models/models.go diff --git a/persistence/sql/models/models.go b/persistence/sql/models/models.go new file mode 100644 index 0000000000..2640e7f93e --- /dev/null +++ b/persistence/sql/models/models.go @@ -0,0 +1 @@ +package models diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index eaa898bc30..307d247521 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -240,7 +240,7 @@ func (p *Persister) createSession(ctx context.Context, signature string, request return nil } -func (p *Persister) updateSession(ctx context.Context, requestId string, session fosite.Session, table tableName) error { +func (p *Persister) updateRefreshSession(ctx context.Context, requestId string, session fosite.Session, used bool) error { _, ok := session.(*oauth2.Session) if !ok && session != nil { return errors.Errorf("Expected session to be of type *Session, but got: %T", session) @@ -250,16 +250,17 @@ func (p *Persister) updateSession(ctx context.Context, requestId string, session return err } - updateSql := fmt.Sprintf("UPDATE %s SET session_data = ? WHERE request_id = ?", table.TableName()) + updateSql := fmt.Sprintf("UPDATE %s SET session_data = ?, used = ? WHERE request_id = ?", + sqlTableRefresh.TableName()) return p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { - err := p.Connection(ctx).RawQuery(updateSql, sessionBytes, requestId).Exec() + err := p.Connection(ctx).RawQuery(updateSql, sessionBytes, used, requestId).Exec() if errors.Is(err, sql.ErrNoRows) { return errorsx.WithStack(fosite.ErrNotFound) } else if err != nil { return sqlcon.HandleError(err) } - return err + return nil }) } @@ -291,13 +292,14 @@ func (p *Persister) findSessionBySignature(ctx context.Context, rawSignature str }) } -func (p *Persister) findSessionByRequestId(ctx context.Context, requestId string, table tableName) (fosite.Requester, error) { - r := OAuth2RequestSQL{Table: table} +func (p *Persister) findRequesterBySignature(ctx context.Context, signature string, tableName tableName) (fosite.Requester, error) { + r := OAuth2RequestSQL{Table: tableName} + var fr fosite.Requester session := new(oauth2.Session) return fr, p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { - err := p.Connection(ctx).Where("request_id = ?", requestId).First(&r) + err := p.Connection(ctx).Where("signature = ?", signature).First(&r) if errors.Is(err, sql.ErrNoRows) { return errorsx.WithStack(fosite.ErrNotFound) } else if err != nil { @@ -306,10 +308,7 @@ func (p *Persister) findSessionByRequestId(ctx context.Context, requestId string fr, err = r.toRequest(ctx, session, p) if err != nil { return err - } else if table == sqlTableCode { - return errorsx.WithStack(fosite.ErrInvalidatedAuthorizeCode) } - return errorsx.WithStack(fosite.ErrInactiveToken) } @@ -351,13 +350,27 @@ func (p *Persister) deactivateSessionByRequestID(ctx context.Context, id string, return sqlcon.HandleError( p.Connection(ctx). RawQuery( - fmt.Sprintf("UPDATE %s SET active=false WHERE request_id=?", OAuth2RequestSQL{Table: table}.TableName()), + fmt.Sprintf("UPDATE %s SET active=false, used=true WHERE request_id=?", OAuth2RequestSQL{Table: table}.TableName()), id, ). Exec(), ) } +func (p *Persister) getRefreshTokenUsedStatusBySignature(ctx context.Context, signature string) (bool, error) { + var used bool + return used, p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { + query := fmt.Sprintf("SELECT used FROM %s WHERE signature = ?", sqlTableRefresh.TableName()) + err := p.Connection(ctx).RawQuery(query, signature).First(&used) + if errors.Is(err, sql.ErrNoRows) { + return errorsx.WithStack(fosite.ErrNotFound) + } else if err != nil { + return sqlcon.HandleError(err) + } + return err + }) +} + func (p *Persister) CreateAuthorizeCodeSession(ctx context.Context, signature string, requester fosite.Requester) (err error) { return p.createSession(ctx, signature, requester, sqlTableCode) } @@ -425,18 +438,39 @@ func (p *Persister) DeletePKCERequestSession(ctx context.Context, signature stri } func (p *Persister) RevokeRefreshToken(ctx context.Context, requestId string) error { + return p.deactivateSessionByRequestID(ctx, requestId, sqlTableRefresh) +} + +func (p *Persister) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requestId string, signature string) error { gracePeriod := p.config.RefreshTokenRotationGracePeriod() - if gracePeriod > 0 { - if requester, err := p.findSessionByRequestId(ctx, requestId, sqlTableRefresh); err != nil { - p.l.Errorf("session_id: %s not found grace period not applied", requestId) - } else { - session := requester.GetSession() - session.SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(gracePeriod)) - p.updateSession(ctx, requestId, session, sqlTableRefresh) - return nil + if gracePeriod <= 0 { + return p.RevokeRefreshToken(ctx, requestId) + } + + var requester fosite.Requester + var err error + if requester, err = p.findRequesterBySignature(ctx, signature, sqlTableRefresh); err != nil { + p.l.Errorf("signature: %s not found. grace period not applied", signature) + return errors.WithStack(err) + } + + var used bool + if used,err = p.getRefreshTokenUsedStatusBySignature(ctx, signature); err != nil { + p.l.Errorf("signature: %s used status not found. grace period not applied", signature) + return errors.WithStack(err) + } + + if ! used { + session := requester.GetSession() + session.SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(gracePeriod)) + if err = p.updateRefreshSession(ctx, requestId, session, true); err != nil { + p.l.Errorf("failed to update session with signature: %s", signature) + return errors.WithStack(err) } + } else { + p.l.Debugf("request_id: %s has already been used and is in the grace period", requestId) } - return p.deactivateSessionByRequestID(ctx, requestId, sqlTableRefresh) + return nil } func (p *Persister) RevokeAccessToken(ctx context.Context, id string) error { From 7a52b36786f4a5c1ecf7133ff8aafe3c0f37acac Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 09:38:49 -0600 Subject: [PATCH 04/29] replace migration with ory dev CLI cmd create migrations with command: ory dev pop migration create persistence/sql/migrations/ add_refresh_token_used_flag --- ...3405_add_refresh_token_used_flag.down.sql} | 0 ...093405_add_refresh_token_used_flag.up.sql} | 0 scripts/create-migration.sh | 69 ------------------- 3 files changed, 69 deletions(-) rename persistence/sql/migrations/{2021102823235122152_add_refresh_token_used_flag.down.sql => 20211101093405_add_refresh_token_used_flag.down.sql} (100%) rename persistence/sql/migrations/{2021102823235122152_add_refresh_token_used_flag.up.sql => 20211101093405_add_refresh_token_used_flag.up.sql} (100%) delete mode 100644 scripts/create-migration.sh diff --git a/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.down.sql b/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.down.sql similarity index 100% rename from persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.down.sql rename to persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.down.sql diff --git a/persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.up.sql b/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.up.sql similarity index 100% rename from persistence/sql/migrations/2021102823235122152_add_refresh_token_used_flag.up.sql rename to persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.up.sql diff --git a/scripts/create-migration.sh b/scripts/create-migration.sh deleted file mode 100644 index f0db30f556..0000000000 --- a/scripts/create-migration.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# create-migration.sh is a helper script to generate sql migration files. -# Migration file names follow the date-time naming convention established by Hydra. - -set -Eeo pipefail - -if [ -n "${DEBUG}" ]; then - set -x -fi - -migration_dir="$( dirname "${BASH_SOURCE[0]}" )/../persistence/sql/migrations/" -supported_dialects=(cockroach mysql postgres sqlite) - -if [ ! -d "$migration_dir" ]; then - echo "expected $migration_dir to exist" - exit 1 -fi - -function usage() { -cat << EOF - -Create up and down migration files for SQLite, CockroachDB, PostgreSQL - $0 --all-dialects - - --all-dialects create a file for each supported dialect (${supported_dialects[*]}) - - e.g. - $0 add_column_to_table - $0 add_column_to_table --all-dialects -EOF -} - -if [ -z "${1}" ]; then - usage - exit 1 -fi - -if [[ ! "${1}" =~ ^[A-Za-z_]+$ ]]; then - echo "invalid migration name: ${1} must be only A-Z, a-z, or _" - usage - exit 1 -fi - -timestamp=$(date -u +%Y%m%d%H%M%S%5N) -echo $timestamp - -file_prefix="${timestamp}_${1}" - -function create_files() { - dialect=$1 - filename="${file_prefix}" - - if [ ! -z "${dialect}" ]; then - filename="${filename}.${dialect}." - fi - - touch "${migration_dir}${filename}.up.sql" - touch "${migration_dir}${filename}.down.sql" -} - -if [ "${2}" == "--all-dialects" ]; then - for dialect in "${supported_dialects[@]}" - do - create_files $dialect - done -else - create_files -fi From 45b9aea45ac7e78da268428e16a7b31d030eb136 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 10:23:17 -0600 Subject: [PATCH 05/29] use existing GetRefreshtTokenSession --- persistence/sql/persister_oauth2.go | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index 307d247521..6cfa847ec8 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -292,31 +292,6 @@ func (p *Persister) findSessionBySignature(ctx context.Context, rawSignature str }) } -func (p *Persister) findRequesterBySignature(ctx context.Context, signature string, tableName tableName) (fosite.Requester, error) { - r := OAuth2RequestSQL{Table: tableName} - - var fr fosite.Requester - session := new(oauth2.Session) - - return fr, p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { - err := p.Connection(ctx).Where("signature = ?", signature).First(&r) - if errors.Is(err, sql.ErrNoRows) { - return errorsx.WithStack(fosite.ErrNotFound) - } else if err != nil { - return sqlcon.HandleError(err) - } else if !r.Active { - fr, err = r.toRequest(ctx, session, p) - if err != nil { - return err - } - return errorsx.WithStack(fosite.ErrInactiveToken) - } - - fr, err = r.toRequest(ctx, session, p) - return err - }) -} - func (p *Persister) deleteSessionBySignature(ctx context.Context, signature string, table tableName) error { signature = p.hashSignature(signature, table) @@ -449,7 +424,8 @@ func (p *Persister) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requ var requester fosite.Requester var err error - if requester, err = p.findRequesterBySignature(ctx, signature, sqlTableRefresh); err != nil { + session := new(oauth2.Session) + if requester, err = p.GetRefreshTokenSession(ctx, signature, session); err != nil { p.l.Errorf("signature: %s not found. grace period not applied", signature) return errors.WithStack(err) } From 7d572aa6d2cbc2e5049091f3988291a831c30158 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 13:13:34 -0600 Subject: [PATCH 06/29] make FositeStorer include oauth2.TokenRevocationStorage --- x/fosite_storer.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x/fosite_storer.go b/x/fosite_storer.go index 91d3f10fa1..fc79f7de39 100644 --- a/x/fosite_storer.go +++ b/x/fosite_storer.go @@ -33,13 +33,10 @@ import ( type FositeStorer interface { fosite.Storage oauth2.CoreStorage + oauth2.TokenRevocationStorage openid.OpenIDConnectRequestStorage pkce.PKCERequestStorage - RevokeRefreshToken(ctx context.Context, requestID string) error - - RevokeAccessToken(ctx context.Context, requestID string) error - // flush the access token requests from the database. // no data will be deleted after the 'notAfter' timeframe. FlushInactiveAccessTokens(ctx context.Context, notAfter time.Time, limit int, batchSize int) error From 957304175f3850c877fe39c7d3bb077143245d4f Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 13:13:44 -0600 Subject: [PATCH 07/29] WIP mabye grace period tests --- oauth2/fosite_store_helpers.go | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/oauth2/fosite_store_helpers.go b/oauth2/fosite_store_helpers.go index 87b8168d3c..a3f4d9b182 100644 --- a/oauth2/fosite_store_helpers.go +++ b/oauth2/fosite_store_helpers.go @@ -180,8 +180,10 @@ func TestHelperRunner(t *testing.T, store InternalRegistry, k string) { t.Run(fmt.Sprintf("case=testFositeStoreClientAssertionJWTValid/db=%s", k), testFositeStoreClientAssertionJWTValid(store)) t.Run(fmt.Sprintf("case=testHelperDeleteAccessTokens/db=%s", k), testHelperDeleteAccessTokens(store)) t.Run(fmt.Sprintf("case=testHelperRevokeAccessToken/db=%s", k), testHelperRevokeAccessToken(store)) + t.Run(fmt.Sprintf("case=testHelperRevokeRefreshTokenMaybeGracePeriod/db=%s", k), testHelperRevokeRefreshTokenMaybeGracePeriod(store)) } + func testHelperRequestIDMultiples(m InternalRegistry, _ string) func(t *testing.T) { return func(t *testing.T) { requestId := uuid.New() @@ -408,6 +410,70 @@ func testHelperRevokeAccessToken(x InternalRegistry) func(t *testing.T) { } } +func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *testing.T) { + + var revokesTokenWhenGracePeriodNotConfigured = func(t *testing.T){ + // SETUP + m := x.OAuth2Storage() + ctx := context.Background() + + refreshTokenSession := fmt.Sprintf("refresh_token_%d", time.Now().Unix()) + err := m.CreateRefreshTokenSession(ctx, refreshTokenSession, &defaultRequest) + assert.NoError(t, err, "precondition failed: could not create refresh token session") + + // ACT + err = m.RevokeRefreshTokenMaybeGracePeriod(ctx,defaultRequest.GetID(), refreshTokenSession) + + // ASSERT + assert.NoError(t, err) + + tmpSession := new(fosite.Session) + _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) + + // a revoked refresh token returns an error when getting the token again + assert.Error(t, err) + assert.True(t, errors.Is(err, fosite.ErrInactiveToken)) + } + + var tokenEntersGracePeriodWhenGracePeriodConfigured = func(t *testing.T){ + + /* TODO: figure out how to change config values and get a new/udpated OAuth2Storage instance + // SETUP + log := logrusx.New("testGracePeriod", "na") + option := configx.WithValue("oauth2.refresh_token_rotation.grace_period", "1m") + c := config.MustNew(log, option) + + //TODO make m := OAuth2Storage + + ctx := context.Background() + + refreshTokenSession := fmt.Sprintf("refresh_token_%d", time.Now().Unix()) + err := m.CreateRefreshTokenSession(ctx, refreshTokenSession, &defaultRequest) + assert.NoError(t, err, "precondition failed: could not create refresh token session") + + // ACT + err = m.RevokeRefreshTokenMaybeGracePeriod(ctx,defaultRequest.GetID(), refreshTokenSession) + + // ASSERT + assert.NoError(t, err) + + tmpSession := new(fosite.Session) + _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) + + // when grace period is configured the refresh token can be obtained within + // the grace period without error + assert.NoError(t, err) + + registry.WithConfig(oldConfig) + */ + } + + return func(t *testing.T) { + revokesTokenWhenGracePeriodNotConfigured(t) + tokenEntersGracePeriodWhenGracePeriodConfigured(t) + } +} + func testHelperCreateGetDeletePKCERequestSession(x InternalRegistry) func(t *testing.T) { return func(t *testing.T) { m := x.OAuth2Storage() From a203f4b0b8aac2142826f04ff7a7a30d93227df2 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 13:21:46 -0600 Subject: [PATCH 08/29] use test run instead of named functions --- oauth2/fosite_store_helpers.go | 84 +++++++++++++++++----------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/oauth2/fosite_store_helpers.go b/oauth2/fosite_store_helpers.go index a3f4d9b182..a0fa4658b1 100644 --- a/oauth2/fosite_store_helpers.go +++ b/oauth2/fosite_store_helpers.go @@ -412,66 +412,64 @@ func testHelperRevokeAccessToken(x InternalRegistry) func(t *testing.T) { func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *testing.T) { - var revokesTokenWhenGracePeriodNotConfigured = func(t *testing.T){ - // SETUP - m := x.OAuth2Storage() - ctx := context.Background() + return func(t *testing.T) { + t.Run("Revokes refresh token when grace period not configured", func(t *testing.T) { + // SETUP + m := x.OAuth2Storage() + ctx := context.Background() - refreshTokenSession := fmt.Sprintf("refresh_token_%d", time.Now().Unix()) - err := m.CreateRefreshTokenSession(ctx, refreshTokenSession, &defaultRequest) - assert.NoError(t, err, "precondition failed: could not create refresh token session") + refreshTokenSession := fmt.Sprintf("refresh_token_%d", time.Now().Unix()) + err := m.CreateRefreshTokenSession(ctx, refreshTokenSession, &defaultRequest) + assert.NoError(t, err, "precondition failed: could not create refresh token session") - // ACT - err = m.RevokeRefreshTokenMaybeGracePeriod(ctx,defaultRequest.GetID(), refreshTokenSession) + // ACT + err = m.RevokeRefreshTokenMaybeGracePeriod(ctx, defaultRequest.GetID(), refreshTokenSession) - // ASSERT - assert.NoError(t, err) + // ASSERT + assert.NoError(t, err) - tmpSession := new(fosite.Session) - _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) + tmpSession := new(fosite.Session) + _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) - // a revoked refresh token returns an error when getting the token again - assert.Error(t, err) - assert.True(t, errors.Is(err, fosite.ErrInactiveToken)) - } + // a revoked refresh token returns an error when getting the token again + assert.Error(t, err) + assert.True(t, errors.Is(err, fosite.ErrInactiveToken)) + }) - var tokenEntersGracePeriodWhenGracePeriodConfigured = func(t *testing.T){ + t.Run("refresh token enters grace period when configured,", func(t *testing.T) { - /* TODO: figure out how to change config values and get a new/udpated OAuth2Storage instance - // SETUP - log := logrusx.New("testGracePeriod", "na") - option := configx.WithValue("oauth2.refresh_token_rotation.grace_period", "1m") - c := config.MustNew(log, option) + /* TODO: figure out how to change config values and get a new/udpated OAuth2Storage instance + // SETUP + log := logrusx.New("testGracePeriod", "na") + option := configx.WithValue("oauth2.refresh_token_rotation.grace_period", "1m") + c := config.MustNew(log, option) - //TODO make m := OAuth2Storage + //TODO make m := OAuth2Storage - ctx := context.Background() + ctx := context.Background() - refreshTokenSession := fmt.Sprintf("refresh_token_%d", time.Now().Unix()) - err := m.CreateRefreshTokenSession(ctx, refreshTokenSession, &defaultRequest) - assert.NoError(t, err, "precondition failed: could not create refresh token session") + refreshTokenSession := fmt.Sprintf("refresh_token_%d", time.Now().Unix()) + err := m.CreateRefreshTokenSession(ctx, refreshTokenSession, &defaultRequest) + assert.NoError(t, err, "precondition failed: could not create refresh token session") - // ACT - err = m.RevokeRefreshTokenMaybeGracePeriod(ctx,defaultRequest.GetID(), refreshTokenSession) + // ACT + err = m.RevokeRefreshTokenMaybeGracePeriod(ctx,defaultRequest.GetID(), refreshTokenSession) - // ASSERT - assert.NoError(t, err) + // ASSERT + assert.NoError(t, err) - tmpSession := new(fosite.Session) - _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) + tmpSession := new(fosite.Session) + _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) - // when grace period is configured the refresh token can be obtained within - // the grace period without error - assert.NoError(t, err) + // when grace period is configured the refresh token can be obtained within + // the grace period without error + assert.NoError(t, err) - registry.WithConfig(oldConfig) - */ + registry.WithConfig(oldConfig) + */ + }) } - return func(t *testing.T) { - revokesTokenWhenGracePeriodNotConfigured(t) - tokenEntersGracePeriodWhenGracePeriodConfigured(t) - } } func testHelperCreateGetDeletePKCERequestSession(x InternalRegistry) func(t *testing.T) { From 1ebc99de60fe1484f7a792fa93dfdf2f19664b2a Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:06:02 -0600 Subject: [PATCH 09/29] add documentation for example config --- docs/docs/reference/configuration.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/docs/reference/configuration.md b/docs/docs/reference/configuration.md index 30db5077d4..5b03358c34 100644 --- a/docs/docs/reference/configuration.md +++ b/docs/docs/reference/configuration.md @@ -1329,6 +1329,23 @@ oauth2: # expose_internal_errors: true + ## refresh_token_rotation + # + # By default Refresh Tokens are rotated and invalidated with each use. See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2 for more details + # + refresh_token_rotation: + # + ## grace_period + # + # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after its first use. New refresh tokens will continue + # to be issued. + # + # Examples: + # - 5s + # - 1m + grace_period: 0s + + ## secrets ## # # The secrets section configures secrets used for encryption and signing of several systems. All secrets can be rotated, for more information on this topic go to: https://www.ory.sh/docs/hydra/advanced#rotation-of-hmac-token-signing-and-database-and-cookie-encryption-keys From 3bb1c20943dec9e2e4463f58f7f37918dcd7aada Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:07:40 -0600 Subject: [PATCH 10/29] add grace period to internal config --- internal/config/config.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/internal/config/config.yaml b/internal/config/config.yaml index 22a2281251..627a206327 100644 --- a/internal/config/config.yaml +++ b/internal/config/config.yaml @@ -413,6 +413,21 @@ secrets: - this-is-an-old-secret - this-is-another-old-secret + ## refresh_token_rotation + # + # By default Refresh Tokens are rotated and invalidated with each use. See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2 for more details + # + refresh_token_rotation: + # + ## grace_period + # + # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after its first use. New refresh tokens will continue + # to be issued. + # + # Examples: + # - 5s + # - 1m + grace_period: 0s # Enables profiling if set. Use "cpu" to enable cpu profiling and "mem" to enable memory profiling. For more details # on profiling, head over to: https://blog.golang.org/profiling-go-programs From 8de23dc68997fb23349633b89b248873fd86dcc4 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:21:14 -0600 Subject: [PATCH 11/29] add refresh token grace period to token-expiration doc --- docs/docs/guides/token-expiration.mdx | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/docs/guides/token-expiration.mdx b/docs/docs/guides/token-expiration.mdx index b6889f3ec2..4787064ee7 100644 --- a/docs/docs/guides/token-expiration.mdx +++ b/docs/docs/guides/token-expiration.mdx @@ -75,6 +75,40 @@ for refresh tokens to never expire. #.... ``` +### Refresh Token Rotation + +When a refresh token is used it is deactivated, which is known as Refresh Token Rotation. By default Hydra deactivates +the refresh token it receives and issues a new token. More information on Refresh Token Rotation can be found in the +Recommendations section of the OAuth 2.0 Security Best Practices document +[here](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2). + +There are some cases when a one time use refresh token may be undesirable, such as when a networking error occurs +and the newly issued refresh token is not received. Hydra may be configured to use a refresh token grace period which +allows a refresh token to be reused for the duration of the grace period. Note that a new refresh token is still generated +and sent back in the response; clients **must** store and use the new refresh token. + +**WARNING** Using the refresh token grace period is an increased security risk, as an intercepted refresh token may be +reused by a bad actor. Use this feature with appropriate consideration. + +``` + ## refresh_token_rotation + # + # By default Refresh Tokens are rotated and invalidated with each use. + # See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2 for more details + # + refresh_token_rotation: + # + ## grace_period + # + # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after + # its first use. New refresh tokens will continue to be issued. + # + # Examples: + # - 5s + # - 1m + grace_period: 0s +``` + ## ID Token Expiration Key `ttl.id_token` configures how long id tokens are valid. From 249264fbba7477387abd86a2a7eb35e28bfbfed2 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Tue, 2 Nov 2021 10:18:50 -0600 Subject: [PATCH 12/29] prettier --write --- docs/docs/guides/token-expiration.mdx | 20 ++++++++++++-------- docs/docs/reference/configuration.md | 5 ++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/docs/docs/guides/token-expiration.mdx b/docs/docs/guides/token-expiration.mdx index 4787064ee7..d4641c5bc4 100644 --- a/docs/docs/guides/token-expiration.mdx +++ b/docs/docs/guides/token-expiration.mdx @@ -77,18 +77,22 @@ for refresh tokens to never expire. ### Refresh Token Rotation -When a refresh token is used it is deactivated, which is known as Refresh Token Rotation. By default Hydra deactivates -the refresh token it receives and issues a new token. More information on Refresh Token Rotation can be found in the +When a refresh token is used it is deactivated, which is known as Refresh Token +Rotation. By default Hydra deactivates the refresh token it receives and issues +a new token. More information on Refresh Token Rotation can be found in the Recommendations section of the OAuth 2.0 Security Best Practices document [here](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2). -There are some cases when a one time use refresh token may be undesirable, such as when a networking error occurs -and the newly issued refresh token is not received. Hydra may be configured to use a refresh token grace period which -allows a refresh token to be reused for the duration of the grace period. Note that a new refresh token is still generated -and sent back in the response; clients **must** store and use the new refresh token. +There are some cases when a one time use refresh token may be undesirable, such +as when a networking error occurs and the newly issued refresh token is not +received. Hydra may be configured to use a refresh token grace period which +allows a refresh token to be reused for the duration of the grace period. Note +that a new refresh token is still generated and sent back in the response; +clients **must** store and use the new refresh token. -**WARNING** Using the refresh token grace period is an increased security risk, as an intercepted refresh token may be -reused by a bad actor. Use this feature with appropriate consideration. +**WARNING** Using the refresh token grace period is an increased security risk, +as an intercepted refresh token may be reused by a bad actor. Use this feature +with appropriate consideration. ``` ## refresh_token_rotation diff --git a/docs/docs/reference/configuration.md b/docs/docs/reference/configuration.md index 5b03358c34..d8630a0fd3 100644 --- a/docs/docs/reference/configuration.md +++ b/docs/docs/reference/configuration.md @@ -1336,16 +1336,15 @@ oauth2: refresh_token_rotation: # ## grace_period - # + # # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after its first use. New refresh tokens will continue - # to be issued. + # to be issued. # # Examples: # - 5s # - 1m grace_period: 0s - ## secrets ## # # The secrets section configures secrets used for encryption and signing of several systems. All secrets can be rotated, for more information on this topic go to: https://www.ory.sh/docs/hydra/advanced#rotation-of-hmac-token-signing-and-database-and-cookie-encryption-keys From 821fba4a22f1a04f22021de22e3cb3b907323218 Mon Sep 17 00:00:00 2001 From: bill-robbins-ss Date: Mon, 22 Nov 2021 09:17:08 -0700 Subject: [PATCH 13/29] Update persistence/sql/persister_oauth2.go Co-authored-by: hackerman <3372410+aeneasr@users.noreply.github.com> --- persistence/sql/persister_oauth2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index 6cfa847ec8..d548fca1ba 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -243,7 +243,7 @@ func (p *Persister) createSession(ctx context.Context, signature string, request func (p *Persister) updateRefreshSession(ctx context.Context, requestId string, session fosite.Session, used bool) error { _, ok := session.(*oauth2.Session) if !ok && session != nil { - return errors.Errorf("Expected session to be of type *Session, but got: %T", session) + return errors.Errorf("expected session to be of type *oauth2.Session but got: %T", session) } sessionBytes, err := p.marshalSession(session) if err != nil { From 2e14277ea04190f0689ec46abbb918b98168b04d Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:32:51 -0700 Subject: [PATCH 14/29] update docs: consequences of reusing a used refresh token --- docs/docs/guides/token-expiration.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/guides/token-expiration.mdx b/docs/docs/guides/token-expiration.mdx index d4641c5bc4..ef268befe0 100644 --- a/docs/docs/guides/token-expiration.mdx +++ b/docs/docs/guides/token-expiration.mdx @@ -79,7 +79,8 @@ for refresh tokens to never expire. When a refresh token is used it is deactivated, which is known as Refresh Token Rotation. By default Hydra deactivates the refresh token it receives and issues -a new token. More information on Refresh Token Rotation can be found in the +a new token. If a deactivated refresh token is used again, all tokens related to that refresh token +will also be deactivated. More information on Refresh Token Rotation can be found in the Recommendations section of the OAuth 2.0 Security Best Practices document [here](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2). From 07375a932cc65be016ad51e59c41172665821e56 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:40:50 -0700 Subject: [PATCH 15/29] add parent key for refresh_token_rotation --- docs/docs/guides/token-expiration.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/guides/token-expiration.mdx b/docs/docs/guides/token-expiration.mdx index ef268befe0..159edfe351 100644 --- a/docs/docs/guides/token-expiration.mdx +++ b/docs/docs/guides/token-expiration.mdx @@ -96,6 +96,7 @@ as an intercepted refresh token may be reused by a bad actor. Use this feature with appropriate consideration. ``` +oauth2: ## refresh_token_rotation # # By default Refresh Tokens are rotated and invalidated with each use. From 09d68016024ebb3b26c302e2c3e88e7f64e50fb1 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:41:17 -0700 Subject: [PATCH 16/29] move refresh token rotation to proper parent --- internal/config/config.yaml | 934 ++++++++++++++++++------------------ 1 file changed, 468 insertions(+), 466 deletions(-) diff --git a/internal/config/config.yaml b/internal/config/config.yaml index 627a206327..a1182586a0 100644 --- a/internal/config/config.yaml +++ b/internal/config/config.yaml @@ -1,466 +1,468 @@ -# ORY Hydra Configuration -# -# -# !!WARNING!! -# This configuration file is for documentation purposes only. Do not use it in production. As all configuration items -# are enabled, it will not work out of the box either. -# -# -# ORY Hydra can be configured using a configuration file and passing the file location using `--config path/to/config.yaml`. -# Per default, ORY Hydra will look up and load file ~/.hydra.yaml. All configuration keys can be set using environment -# variables as well. -# -# Setting environment variables is easy: -# -## Linux / OSX -# -# $ export MY_ENV_VAR=foo -# $ hydra ... -# -# alternatively: -# -# $ MY_ENV_VAR=foo hydra ... -# -## Windows -# -### Command Prompt -# -# > set MY_ENV_VAR=foo -# > hydra ... -# -### Powershell -# -# > $env:MY_ENV_VAR="foo" -# > hydra ... -# -## Docker -# -# $ docker run -e MY_ENV_VAR=foo oryd/hydra:... -# -# -# Assuming the following configuration layout: -# -# serve: -# public: -# port: 4444 -# something_else: foobar -# -# Key `something_else` can be set as an environment variable by uppercasing it's path: -# `serve.public.port.somethihng_else` -> `SERVE.PUBLIC.PORT.SOMETHING_ELSE` -# and replacing `.` with `_`: -# `serve.public.port.somethihng_else` -> `SERVE_PUBLIC_PORT_SOMETHING_ELSE` -# -# Environment variables always override values from the configuration file. Here are some more examples: -# -# Configuration key | Environment variable | -# ------------------|----------------------| -# dsn | DSN | -# serve.admin.host | SERVE_ADMIN_HOST | -# ------------------|----------------------| -# -# -# List items such as -# -# secrets: -# system: -# - this-is-the-primary-secret -# - this-is-an-old-secret -# - this-is-another-old-secret -# -# must be separated using `,` when using environment variables. The environment variable equivalent to the code section# -# above is: -# -# Linux/macOS: $ export SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret -# Windows: > set SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret - -# log configures the logger -log: - # Sets the log level, supports "panic", "fatal", "error", "warn", "info" and "debug". Defaults to "info". - level: info - # Sets the log format. Leave it undefined for text based log format, or set to "json" for JSON formatting. - format: json - -# serve controls the configuration for the http(s) daemon(s). -serve: - # public controls the public daemon serving public API endpoints like /oauth2/auth, /oauth2/token, /.well-known/jwks.json - public: - # The port to listen on. Defaults to 4444 - port: 4444 - # The interface or unix socket ORY Hydra should listen and handle public API requests on. - # Use the prefix "unix:" to specify a path to a unix socket. - # Leave empty to listen on all interfaces. - host: localhost # leave this out or empty to listen on all devices which is the default - # host: unix:/path/to/socket - # socket: - # owner: hydra - # group: hydra - # mode: 0775 - - # cors configures Cross Origin Resource Sharing for public endpoints. - cors: - # set enabled to true to enable CORS. Defaults to false. - enabled: true - # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. - # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) - # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. - # - # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). - allowed_origins: - - https://example.com - - https://*.example.com - # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain - # requests. Defaults to the methods listed. - allowed_methods: - - POST - - GET - - PUT - - PATCH - - DELETE - - # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. - allowed_headers: - - Authorization - - Content-Type - - # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. - exposed_headers: - - Content-Type - - # Sets whether the request can include user credentials like cookies, HTTP authentication - # or client side SSL certificates. Defaults to true. - allow_credentials: true - - # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request - # is preceded by a preflight request. Defaults to 0. - max_age: 10 - - # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. - debug: true - - # Access Log configuration for public server. - access_log: - # Disable access log for health endpoints. - disable_for_health: false - - # admin controls the admin daemon serving admin API endpoints like /jwk, /client, ... - admin: - # The port to listen on. Defaults to 4445 - port: 4445 - # The interface or unix socket ORY Hydra should listen and handle administrative API requests on. - # Use the prefix "unix:" to specify a path to a unix socket. - # Leave empty to listen on all interfaces. - host: localhost # leave this out or empty to listen on all devices which is the default - # host: unix:/path/to/socket - # socket: - # owner: hydra - # group: hydra - # mode: 0775 - - # cors configures Cross Origin Resource Sharing for admin endpoints. - cors: - # set enabled to true to enable CORS. Defaults to false. - enabled: true - # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. - # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) - # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. - # - # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). - allowed_origins: - - https://example.com - - https://*.example.com - # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain - # requests. Defaults to GET and POST. - allowed_methods: - - POST - - GET - - PUT - - PATCH - - DELETE - - # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. - allowed_headers: - - Authorization - - Content-Type - - # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. - exposed_headers: - - Content-Type - - # Sets whether the request can include user credentials like cookies, HTTP authentication - # or client side SSL certificates. - allow_credentials: true - - # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request - # is preceded by a preflight request. Defaults to 0. - max_age: 10 - - # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. - debug: true - - # Access Log configuration for admin server. - access_log: - # Disable access log for health endpoints. - disable_for_health: false - - # tls configures HTTPS (HTTP over TLS). If configured, the server automatically supports HTTP/2. - tls: - # key configures the private key (pem encoded) - key: - # The key can either be loaded from a file: - path: /path/to/key.pem - # Or from a base64 encoded (without padding) string: - base64: LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3... - - # cert configures the TLS certificate (PEM encoded) - cert: - # The cert can either be loaded from a file: - path: /path/to/cert.pem - # Or from a base64 encoded (without padding) string: - base64: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr... - - # Whitelist one or multiple CIDR address ranges and allow them to terminate TLS connections. - # Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but - # your proxy / gateway / load balancer. Supports ipv4 and ipv6. - # - # Hydra serves http instead of https when this option is set. - # - # For more information head over to: https://www.ory.sh/docs/hydra/production#tls-termination - allow_termination_from: - - 127.0.0.1/32 - cookies: - # specify the SameSite mode that cookies should be sent with - same_site_mode: Lax - - # Some older browser versions don't work with SameSite=None. This option enables the workaround - # defined in https://web.dev/samesite-cookie-recipes/ which essentially stores a second cookie - # without SameSite as a fallback. - same_site_legacy_workaround: false - -# dsn sets the data source name. This configures the backend where ORY Hydra persists data. -# -## In-memory database -# -# If dsn is "memory", data will be written to memory and is lost when you restart this instance. -# You can set this value using the DSN environment variable: -# -## SQL databases -# -# ORY Hydra supports popular SQL databases. For more detailed configuration information go to: -# https://www.ory.sh/docs/hydra/dependencies-environment#sql -# -### PostgreSQL (recommended) -# -# If dsn is starting with postgres:// PostgreSQL will be used as storage backend: -# dsn: dsn=postgres://user:password@host:123/database -# -### MySQL database -# -# If dsn is starting with mysql:// MySQL will be used as storage backend: -# dsn: mysql://user:password@tcp(host:123)/database -# -### CockroachDB -# -# If dsn is starting with cockroach:// CockroachDB will be used as storage backend: -# dsn: cockroach://user:password@host:123/database -# -dsn: memory -# dsn: postgres://user:password@host:123/database -# dsn: mysql://user:password@tcp(host:123)/database - -# webfinger configures ./well-known/ settings -webfinger: - # jwks configures the /.well-known/jwks.json endpoint. - jwks: - # broadcast_keys is a list of JSON Web Keys that should be exposed at that endpoint. This is usually - # the public key for verifying OpenID Connect ID Tokens. However, you might want to add additional keys here as well. - broadcast_keys: - - hydra.openid.id-token # This key is always exposed by default - # - hydra.jwt.access-token # This key will be exposed when the OAuth2 Access Token strategy is set to JWT. - - # oidc_discovery configures OpenID Connect Discovery (/.well-known/openid-configuration) - oidc_discovery: - client_registration_url: https://my-service.com/clients - # A list of supported claims to be broadcasted. Claim `sub` is always included: - supported_claims: - - email - - username - # The scope OAuth 2.0 Clients may request. Scope `offline`, `offline_access`, and `openid` are always included. - supported_scope: - - email - - whatever - - read.photos - - # A URL of the userinfo endpoint to be advertised at the OpenID Connect - # Discovery endpoint /.well-known/openid-configuration. Defaults to ORY Hydra's userinfo endpoint at /userinfo. - # Set this value if you want to handle this endpoint yourself. - userinfo_url: https://example.org/my-custom-userinfo-endpoint - -# oidc configures OpenID Connect features. -oidc: - # subject_identifiers configures the Subject Identifier algorithm. - # - # For more information please head over to the documentation: - # -> https://www.ory.sh/docs/hydra/advanced#subject-identifier-algorithms - subject_identifiers: - # which algorithms to enable. Defaults to "public" - supported_types: - - pairwise - - public - # configures the pairwise algorithm - pairwise: - # if "pairwise" is enabled, the salt must be defined. - salt: some-random-salt - - # dynamic_client_registration configures OpenID Connect Dynamic Client Registration (exposed as admin endpoints /clients/...) - dynamic_client_registration: - - # The OpenID Connect Dynamic Client Registration specification has no concept of whitelisting OAuth 2.0 Scope. If you - # want to expose Dynamic Client Registration, you should set the default scope enabled for newly registered clients. - # Keep in mind that users can overwrite this default by setting the "scope" key in the registration payload, - # effectively disabling the concept of whitelisted scopes. - default_scope: - - openid - - offline - - offline_access - -urls: - self: - - # This value will be used as the "issuer" in access and ID tokens. It must be - # specified and using HTTPS protocol, unless --dangerous-force-http is set. This should typically be equal - # to the public value. - issuer: https://localhost:4444/ - - # This is the base location of the public endpoints of your ORY Hydra installation. This should typically be equal - # to the issuer value. If left unspecified, it falls back to the issuer value. - public: https://localhost:4444/ - - # Sets the login endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. - login: https://my-login.app/login - # Sets the consent endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. - consent: https://my-consent.app/consent - # Sets the logout endpoint. Defaults to an internal fallback URL. - logout: https://my-logout.app/logout - # Sets the error endpoint. The error ui will be shown when an OAuth2 error occurs that which can not be sent back - # to the client. Defaults to an internal fallback URL. - error: https://my-error.app/error - # When a user agent requests to logout, it will be redirected to this url afterwards per default. - post_logout_redirect: https://my-example.app/logout-successful - -strategies: - scope: DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY - # You may use JSON Web Tokens as access tokens. - # - # But seriously. Don't do that. It's not a great idea and has a ton of caveats and subtle security implications. Read more: - # -> https://www.ory.sh/docs/hydra/advanced#json-web-tokens - # - # access_token: jwt - -# configures time to live -ttl: - # configures how long a user login and consent flow may take. Defaults to 1h. - login_consent_request: 1h - # configures how long access tokens are valid. Defaults to 1h. - access_token: 1h - # configures how long refresh tokens are valid. Defaults to 720h. Set to -1 for refresh tokens to never expire. - refresh_token: 720h - # configures how long id tokens are valid. Defaults to 1h. - id_token: 1h - # configures how long auth codes are valid. Defaults to 10m. - auth_code: 10m - -oauth2: - # Set this to true if you want to share error debugging information with your OAuth 2.0 clients. - # Keep in mind that debug information is very valuable when dealing with errors, but might also expose database error - # codes and similar errors. Defaults to false. - expose_internal_errors: true - # Configures hashing algorithms. Supports only BCrypt at the moment. - hashers: - # Configures the BCrypt hashing algorithm used for hashing Client Secrets. - bcrypt: - # Sets the BCrypt cost. Minimum value is 4 and default value is 10. The higher the value, the more CPU time is being - # used to generate hashes. - cost: 10 - pkce: - # Set this to true if you want PKCE to be enforced for all clients. - enforced: false - # Set this to true if you want PKCE to be enforced for public clients. - enforced_for_public_clients: false - session: - # store encrypted data in database, default true - encrypt_at_rest: true - -# The secrets section configures secrets used for encryption and signing of several systems. All secrets can be rotated, -# for more information on this topic navigate to: -# -> https://www.ory.sh/docs/hydra/advanced#rotation-of-hmac-token-signing-and-database-and-cookie-encryption-keys -secrets: - # The system secret must be at least 16 characters long. If none is provided, one will be generated. They key - # is used to encrypt sensitive data using AES-GCM (256 bit) and validate HMAC signatures. - # - # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures - # and decryption. - system: - - this-is-the-primary-secret - - this-is-an-old-secret - - this-is-another-old-secret - # A secret that is used to encrypt cookie sessions. Defaults to secrets.system. It is recommended to use - # a separate secret in production. - # - # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures - # and decryption. - cookie: - - this-is-the-primary-secret - - this-is-an-old-secret - - this-is-another-old-secret - - ## refresh_token_rotation - # - # By default Refresh Tokens are rotated and invalidated with each use. See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2 for more details - # - refresh_token_rotation: - # - ## grace_period - # - # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after its first use. New refresh tokens will continue - # to be issued. - # - # Examples: - # - 5s - # - 1m - grace_period: 0s - -# Enables profiling if set. Use "cpu" to enable cpu profiling and "mem" to enable memory profiling. For more details -# on profiling, head over to: https://blog.golang.org/profiling-go-programs -profiling: cpu -# profiling: mem - -# ORY Hydra supports distributed tracing. -tracing: - # Set this to the tracing backend you wish to use. Currently supports jaeger. If omitted or empty, tracing will - # be disabled. - provider: jaeger - # Specifies the service name to use on the tracer. - service_name: ORY Hydra - providers: - # Configures the jaeger tracing backend. - jaeger: - # The address of the jaeger-agent where spans should be sent to - local_agent_address: 127.0.0.1:6831 - # The tracing header format - propagation: jaeger - # The maximum length of jaeger tag value - max_tag_value_length: 1024 - sampling: - # The type of the sampler you want to use. Supports: - # - const - # - probabilistic - # - ratelimiting - type: const - # The value passed to the sampler type that has been configured. - # Supported values: This is dependant on the sampling strategy used: - # - const: 0 or 1 (all or nothing) - # - rateLimiting: a constant rate (e.g. setting this to 3 will sample requests with the rate of 3 traces per second) - # - probabilistic: a value between 0..1 - value: 1.0 - # The address of jaeger-agent's HTTP sampling server - server_url: http://localhost:5778/sampling +# ORY Hydra Configuration +# +# +# !!WARNING!! +# This configuration file is for documentation purposes only. Do not use it in production. As all configuration items +# are enabled, it will not work out of the box either. +# +# +# ORY Hydra can be configured using a configuration file and passing the file location using `--config path/to/config.yaml`. +# Per default, ORY Hydra will look up and load file ~/.hydra.yaml. All configuration keys can be set using environment +# variables as well. +# +# Setting environment variables is easy: +# +## Linux / OSX +# +# $ export MY_ENV_VAR=foo +# $ hydra ... +# +# alternatively: +# +# $ MY_ENV_VAR=foo hydra ... +# +## Windows +# +### Command Prompt +# +# > set MY_ENV_VAR=foo +# > hydra ... +# +### Powershell +# +# > $env:MY_ENV_VAR="foo" +# > hydra ... +# +## Docker +# +# $ docker run -e MY_ENV_VAR=foo oryd/hydra:... +# +# +# Assuming the following configuration layout: +# +# serve: +# public: +# port: 4444 +# something_else: foobar +# +# Key `something_else` can be set as an environment variable by uppercasing it's path: +# `serve.public.port.somethihng_else` -> `SERVE.PUBLIC.PORT.SOMETHING_ELSE` +# and replacing `.` with `_`: +# `serve.public.port.somethihng_else` -> `SERVE_PUBLIC_PORT_SOMETHING_ELSE` +# +# Environment variables always override values from the configuration file. Here are some more examples: +# +# Configuration key | Environment variable | +# ------------------|----------------------| +# dsn | DSN | +# serve.admin.host | SERVE_ADMIN_HOST | +# ------------------|----------------------| +# +# +# List items such as +# +# secrets: +# system: +# - this-is-the-primary-secret +# - this-is-an-old-secret +# - this-is-another-old-secret +# +# must be separated using `,` when using environment variables. The environment variable equivalent to the code section# +# above is: +# +# Linux/macOS: $ export SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret +# Windows: > set SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret + +# log configures the logger +log: + # Sets the log level, supports "panic", "fatal", "error", "warn", "info" and "debug". Defaults to "info". + level: info + # Sets the log format. Leave it undefined for text based log format, or set to "json" for JSON formatting. + format: json + +# serve controls the configuration for the http(s) daemon(s). +serve: + # public controls the public daemon serving public API endpoints like /oauth2/auth, /oauth2/token, /.well-known/jwks.json + public: + # The port to listen on. Defaults to 4444 + port: 4444 + # The interface or unix socket ORY Hydra should listen and handle public API requests on. + # Use the prefix "unix:" to specify a path to a unix socket. + # Leave empty to listen on all interfaces. + host: localhost # leave this out or empty to listen on all devices which is the default + # host: unix:/path/to/socket + # socket: + # owner: hydra + # group: hydra + # mode: 0775 + + # cors configures Cross Origin Resource Sharing for public endpoints. + cors: + # set enabled to true to enable CORS. Defaults to false. + enabled: true + # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. + # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) + # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. + # + # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). + allowed_origins: + - https://example.com + - https://*.example.com + # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain + # requests. Defaults to the methods listed. + allowed_methods: + - POST + - GET + - PUT + - PATCH + - DELETE + + # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. + allowed_headers: + - Authorization + - Content-Type + + # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. + exposed_headers: + - Content-Type + + # Sets whether the request can include user credentials like cookies, HTTP authentication + # or client side SSL certificates. Defaults to true. + allow_credentials: true + + # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request + # is preceded by a preflight request. Defaults to 0. + max_age: 10 + + # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. + debug: true + + # Access Log configuration for public server. + access_log: + # Disable access log for health endpoints. + disable_for_health: false + + # admin controls the admin daemon serving admin API endpoints like /jwk, /client, ... + admin: + # The port to listen on. Defaults to 4445 + port: 4445 + # The interface or unix socket ORY Hydra should listen and handle administrative API requests on. + # Use the prefix "unix:" to specify a path to a unix socket. + # Leave empty to listen on all interfaces. + host: localhost # leave this out or empty to listen on all devices which is the default + # host: unix:/path/to/socket + # socket: + # owner: hydra + # group: hydra + # mode: 0775 + + # cors configures Cross Origin Resource Sharing for admin endpoints. + cors: + # set enabled to true to enable CORS. Defaults to false. + enabled: true + # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. + # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) + # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. + # + # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). + allowed_origins: + - https://example.com + - https://*.example.com + # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain + # requests. Defaults to GET and POST. + allowed_methods: + - POST + - GET + - PUT + - PATCH + - DELETE + + # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. + allowed_headers: + - Authorization + - Content-Type + + # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. + exposed_headers: + - Content-Type + + # Sets whether the request can include user credentials like cookies, HTTP authentication + # or client side SSL certificates. + allow_credentials: true + + # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request + # is preceded by a preflight request. Defaults to 0. + max_age: 10 + + # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. + debug: true + + # Access Log configuration for admin server. + access_log: + # Disable access log for health endpoints. + disable_for_health: false + + # tls configures HTTPS (HTTP over TLS). If configured, the server automatically supports HTTP/2. + tls: + # key configures the private key (pem encoded) + key: + # The key can either be loaded from a file: + path: /path/to/key.pem + # Or from a base64 encoded (without padding) string: + base64: LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3... + + # cert configures the TLS certificate (PEM encoded) + cert: + # The cert can either be loaded from a file: + path: /path/to/cert.pem + # Or from a base64 encoded (without padding) string: + base64: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr... + + # Whitelist one or multiple CIDR address ranges and allow them to terminate TLS connections. + # Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but + # your proxy / gateway / load balancer. Supports ipv4 and ipv6. + # + # Hydra serves http instead of https when this option is set. + # + # For more information head over to: https://www.ory.sh/docs/hydra/production#tls-termination + allow_termination_from: + - 127.0.0.1/32 + cookies: + # specify the SameSite mode that cookies should be sent with + same_site_mode: Lax + + # Some older browser versions don't work with SameSite=None. This option enables the workaround + # defined in https://web.dev/samesite-cookie-recipes/ which essentially stores a second cookie + # without SameSite as a fallback. + same_site_legacy_workaround: false + +# dsn sets the data source name. This configures the backend where ORY Hydra persists data. +# +## In-memory database +# +# If dsn is "memory", data will be written to memory and is lost when you restart this instance. +# You can set this value using the DSN environment variable: +# +## SQL databases +# +# ORY Hydra supports popular SQL databases. For more detailed configuration information go to: +# https://www.ory.sh/docs/hydra/dependencies-environment#sql +# +### PostgreSQL (recommended) +# +# If dsn is starting with postgres:// PostgreSQL will be used as storage backend: +# dsn: dsn=postgres://user:password@host:123/database +# +### MySQL database +# +# If dsn is starting with mysql:// MySQL will be used as storage backend: +# dsn: mysql://user:password@tcp(host:123)/database +# +### CockroachDB +# +# If dsn is starting with cockroach:// CockroachDB will be used as storage backend: +# dsn: cockroach://user:password@host:123/database +# +dsn: memory +# dsn: postgres://user:password@host:123/database +# dsn: mysql://user:password@tcp(host:123)/database + +# webfinger configures ./well-known/ settings +webfinger: + # jwks configures the /.well-known/jwks.json endpoint. + jwks: + # broadcast_keys is a list of JSON Web Keys that should be exposed at that endpoint. This is usually + # the public key for verifying OpenID Connect ID Tokens. However, you might want to add additional keys here as well. + broadcast_keys: + - hydra.openid.id-token # This key is always exposed by default + # - hydra.jwt.access-token # This key will be exposed when the OAuth2 Access Token strategy is set to JWT. + + # oidc_discovery configures OpenID Connect Discovery (/.well-known/openid-configuration) + oidc_discovery: + client_registration_url: https://my-service.com/clients + # A list of supported claims to be broadcasted. Claim `sub` is always included: + supported_claims: + - email + - username + # The scope OAuth 2.0 Clients may request. Scope `offline`, `offline_access`, and `openid` are always included. + supported_scope: + - email + - whatever + - read.photos + + # A URL of the userinfo endpoint to be advertised at the OpenID Connect + # Discovery endpoint /.well-known/openid-configuration. Defaults to ORY Hydra's userinfo endpoint at /userinfo. + # Set this value if you want to handle this endpoint yourself. + userinfo_url: https://example.org/my-custom-userinfo-endpoint + +# oidc configures OpenID Connect features. +oidc: + # subject_identifiers configures the Subject Identifier algorithm. + # + # For more information please head over to the documentation: + # -> https://www.ory.sh/docs/hydra/advanced#subject-identifier-algorithms + subject_identifiers: + # which algorithms to enable. Defaults to "public" + supported_types: + - pairwise + - public + # configures the pairwise algorithm + pairwise: + # if "pairwise" is enabled, the salt must be defined. + salt: some-random-salt + + # dynamic_client_registration configures OpenID Connect Dynamic Client Registration (exposed as admin endpoints /clients/...) + dynamic_client_registration: + + # The OpenID Connect Dynamic Client Registration specification has no concept of whitelisting OAuth 2.0 Scope. If you + # want to expose Dynamic Client Registration, you should set the default scope enabled for newly registered clients. + # Keep in mind that users can overwrite this default by setting the "scope" key in the registration payload, + # effectively disabling the concept of whitelisted scopes. + default_scope: + - openid + - offline + - offline_access + +urls: + self: + + # This value will be used as the "issuer" in access and ID tokens. It must be + # specified and using HTTPS protocol, unless --dangerous-force-http is set. This should typically be equal + # to the public value. + issuer: https://localhost:4444/ + + # This is the base location of the public endpoints of your ORY Hydra installation. This should typically be equal + # to the issuer value. If left unspecified, it falls back to the issuer value. + public: https://localhost:4444/ + + # Sets the login endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. + login: https://my-login.app/login + # Sets the consent endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. + consent: https://my-consent.app/consent + # Sets the logout endpoint. Defaults to an internal fallback URL. + logout: https://my-logout.app/logout + # Sets the error endpoint. The error ui will be shown when an OAuth2 error occurs that which can not be sent back + # to the client. Defaults to an internal fallback URL. + error: https://my-error.app/error + # When a user agent requests to logout, it will be redirected to this url afterwards per default. + post_logout_redirect: https://my-example.app/logout-successful + +strategies: + scope: DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY + # You may use JSON Web Tokens as access tokens. + # + # But seriously. Don't do that. It's not a great idea and has a ton of caveats and subtle security implications. Read more: + # -> https://www.ory.sh/docs/hydra/advanced#json-web-tokens + # + # access_token: jwt + +# configures time to live +ttl: + # configures how long a user login and consent flow may take. Defaults to 1h. + login_consent_request: 1h + # configures how long access tokens are valid. Defaults to 1h. + access_token: 1h + # configures how long refresh tokens are valid. Defaults to 720h. Set to -1 for refresh tokens to never expire. + refresh_token: 720h + # configures how long id tokens are valid. Defaults to 1h. + id_token: 1h + # configures how long auth codes are valid. Defaults to 10m. + auth_code: 10m + +oauth2: + # Set this to true if you want to share error debugging information with your OAuth 2.0 clients. + # Keep in mind that debug information is very valuable when dealing with errors, but might also expose database error + # codes and similar errors. Defaults to false. + expose_internal_errors: true + # Configures hashing algorithms. Supports only BCrypt at the moment. + hashers: + # Configures the BCrypt hashing algorithm used for hashing Client Secrets. + bcrypt: + # Sets the BCrypt cost. Minimum value is 4 and default value is 10. The higher the value, the more CPU time is being + # used to generate hashes. + cost: 10 + pkce: + # Set this to true if you want PKCE to be enforced for all clients. + enforced: false + # Set this to true if you want PKCE to be enforced for public clients. + enforced_for_public_clients: false + session: + # store encrypted data in database, default true + encrypt_at_rest: true + + ## refresh_token_rotation + # + # By default Refresh Tokens are rotated and invalidated with each use. + # See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2 for more details + # + refresh_token_rotation: + # + ## grace_period + # + # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after its first use. New refresh tokens will continue + # to be issued. + # + # Examples: + # - 5s + # - 1m + # - 0m (default; grace period disabled) + grace_period: 0s + +# The secrets section configures secrets used for encryption and signing of several systems. All secrets can be rotated, +# for more information on this topic navigate to: +# -> https://www.ory.sh/docs/hydra/advanced#rotation-of-hmac-token-signing-and-database-and-cookie-encryption-keys +secrets: + # The system secret must be at least 16 characters long. If none is provided, one will be generated. They key + # is used to encrypt sensitive data using AES-GCM (256 bit) and validate HMAC signatures. + # + # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures + # and decryption. + system: + - this-is-the-primary-secret + - this-is-an-old-secret + - this-is-another-old-secret + # A secret that is used to encrypt cookie sessions. Defaults to secrets.system. It is recommended to use + # a separate secret in production. + # + # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures + # and decryption. + cookie: + - this-is-the-primary-secret + - this-is-an-old-secret + - this-is-another-old-secret + +# Enables profiling if set. Use "cpu" to enable cpu profiling and "mem" to enable memory profiling. For more details +# on profiling, head over to: https://blog.golang.org/profiling-go-programs +profiling: cpu +# profiling: mem + +# ORY Hydra supports distributed tracing. +tracing: + # Set this to the tracing backend you wish to use. Currently supports jaeger. If omitted or empty, tracing will + # be disabled. + provider: jaeger + # Specifies the service name to use on the tracer. + service_name: ORY Hydra + providers: + # Configures the jaeger tracing backend. + jaeger: + # The address of the jaeger-agent where spans should be sent to + local_agent_address: 127.0.0.1:6831 + # The tracing header format + propagation: jaeger + # The maximum length of jaeger tag value + max_tag_value_length: 1024 + sampling: + # The type of the sampler you want to use. Supports: + # - const + # - probabilistic + # - ratelimiting + type: const + # The value passed to the sampler type that has been configured. + # Supported values: This is dependant on the sampling strategy used: + # - const: 0 or 1 (all or nothing) + # - rateLimiting: a constant rate (e.g. setting this to 3 will sample requests with the rate of 3 traces per second) + # - probabilistic: a value between 0..1 + value: 1.0 + # The address of jaeger-agent's HTTP sampling server + server_url: http://localhost:5778/sampling From 89527fde851dc981c6dabdf85b2e6496ead95d56 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:47:38 -0700 Subject: [PATCH 17/29] update documentation --- docs/docs/guides/token-expiration.mdx | 1 + internal/config/config.yaml | 936 +++++++++++++------------- 2 files changed, 469 insertions(+), 468 deletions(-) diff --git a/docs/docs/guides/token-expiration.mdx b/docs/docs/guides/token-expiration.mdx index 159edfe351..ec62b44093 100644 --- a/docs/docs/guides/token-expiration.mdx +++ b/docs/docs/guides/token-expiration.mdx @@ -112,6 +112,7 @@ oauth2: # Examples: # - 5s # - 1m + # - 0s (default; grace period disabled) grace_period: 0s ``` diff --git a/internal/config/config.yaml b/internal/config/config.yaml index a1182586a0..bf04b4d5a1 100644 --- a/internal/config/config.yaml +++ b/internal/config/config.yaml @@ -1,468 +1,468 @@ -# ORY Hydra Configuration -# -# -# !!WARNING!! -# This configuration file is for documentation purposes only. Do not use it in production. As all configuration items -# are enabled, it will not work out of the box either. -# -# -# ORY Hydra can be configured using a configuration file and passing the file location using `--config path/to/config.yaml`. -# Per default, ORY Hydra will look up and load file ~/.hydra.yaml. All configuration keys can be set using environment -# variables as well. -# -# Setting environment variables is easy: -# -## Linux / OSX -# -# $ export MY_ENV_VAR=foo -# $ hydra ... -# -# alternatively: -# -# $ MY_ENV_VAR=foo hydra ... -# -## Windows -# -### Command Prompt -# -# > set MY_ENV_VAR=foo -# > hydra ... -# -### Powershell -# -# > $env:MY_ENV_VAR="foo" -# > hydra ... -# -## Docker -# -# $ docker run -e MY_ENV_VAR=foo oryd/hydra:... -# -# -# Assuming the following configuration layout: -# -# serve: -# public: -# port: 4444 -# something_else: foobar -# -# Key `something_else` can be set as an environment variable by uppercasing it's path: -# `serve.public.port.somethihng_else` -> `SERVE.PUBLIC.PORT.SOMETHING_ELSE` -# and replacing `.` with `_`: -# `serve.public.port.somethihng_else` -> `SERVE_PUBLIC_PORT_SOMETHING_ELSE` -# -# Environment variables always override values from the configuration file. Here are some more examples: -# -# Configuration key | Environment variable | -# ------------------|----------------------| -# dsn | DSN | -# serve.admin.host | SERVE_ADMIN_HOST | -# ------------------|----------------------| -# -# -# List items such as -# -# secrets: -# system: -# - this-is-the-primary-secret -# - this-is-an-old-secret -# - this-is-another-old-secret -# -# must be separated using `,` when using environment variables. The environment variable equivalent to the code section# -# above is: -# -# Linux/macOS: $ export SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret -# Windows: > set SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret - -# log configures the logger -log: - # Sets the log level, supports "panic", "fatal", "error", "warn", "info" and "debug". Defaults to "info". - level: info - # Sets the log format. Leave it undefined for text based log format, or set to "json" for JSON formatting. - format: json - -# serve controls the configuration for the http(s) daemon(s). -serve: - # public controls the public daemon serving public API endpoints like /oauth2/auth, /oauth2/token, /.well-known/jwks.json - public: - # The port to listen on. Defaults to 4444 - port: 4444 - # The interface or unix socket ORY Hydra should listen and handle public API requests on. - # Use the prefix "unix:" to specify a path to a unix socket. - # Leave empty to listen on all interfaces. - host: localhost # leave this out or empty to listen on all devices which is the default - # host: unix:/path/to/socket - # socket: - # owner: hydra - # group: hydra - # mode: 0775 - - # cors configures Cross Origin Resource Sharing for public endpoints. - cors: - # set enabled to true to enable CORS. Defaults to false. - enabled: true - # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. - # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) - # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. - # - # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). - allowed_origins: - - https://example.com - - https://*.example.com - # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain - # requests. Defaults to the methods listed. - allowed_methods: - - POST - - GET - - PUT - - PATCH - - DELETE - - # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. - allowed_headers: - - Authorization - - Content-Type - - # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. - exposed_headers: - - Content-Type - - # Sets whether the request can include user credentials like cookies, HTTP authentication - # or client side SSL certificates. Defaults to true. - allow_credentials: true - - # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request - # is preceded by a preflight request. Defaults to 0. - max_age: 10 - - # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. - debug: true - - # Access Log configuration for public server. - access_log: - # Disable access log for health endpoints. - disable_for_health: false - - # admin controls the admin daemon serving admin API endpoints like /jwk, /client, ... - admin: - # The port to listen on. Defaults to 4445 - port: 4445 - # The interface or unix socket ORY Hydra should listen and handle administrative API requests on. - # Use the prefix "unix:" to specify a path to a unix socket. - # Leave empty to listen on all interfaces. - host: localhost # leave this out or empty to listen on all devices which is the default - # host: unix:/path/to/socket - # socket: - # owner: hydra - # group: hydra - # mode: 0775 - - # cors configures Cross Origin Resource Sharing for admin endpoints. - cors: - # set enabled to true to enable CORS. Defaults to false. - enabled: true - # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. - # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) - # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. - # - # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). - allowed_origins: - - https://example.com - - https://*.example.com - # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain - # requests. Defaults to GET and POST. - allowed_methods: - - POST - - GET - - PUT - - PATCH - - DELETE - - # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. - allowed_headers: - - Authorization - - Content-Type - - # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. - exposed_headers: - - Content-Type - - # Sets whether the request can include user credentials like cookies, HTTP authentication - # or client side SSL certificates. - allow_credentials: true - - # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request - # is preceded by a preflight request. Defaults to 0. - max_age: 10 - - # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. - debug: true - - # Access Log configuration for admin server. - access_log: - # Disable access log for health endpoints. - disable_for_health: false - - # tls configures HTTPS (HTTP over TLS). If configured, the server automatically supports HTTP/2. - tls: - # key configures the private key (pem encoded) - key: - # The key can either be loaded from a file: - path: /path/to/key.pem - # Or from a base64 encoded (without padding) string: - base64: LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3... - - # cert configures the TLS certificate (PEM encoded) - cert: - # The cert can either be loaded from a file: - path: /path/to/cert.pem - # Or from a base64 encoded (without padding) string: - base64: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr... - - # Whitelist one or multiple CIDR address ranges and allow them to terminate TLS connections. - # Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but - # your proxy / gateway / load balancer. Supports ipv4 and ipv6. - # - # Hydra serves http instead of https when this option is set. - # - # For more information head over to: https://www.ory.sh/docs/hydra/production#tls-termination - allow_termination_from: - - 127.0.0.1/32 - cookies: - # specify the SameSite mode that cookies should be sent with - same_site_mode: Lax - - # Some older browser versions don't work with SameSite=None. This option enables the workaround - # defined in https://web.dev/samesite-cookie-recipes/ which essentially stores a second cookie - # without SameSite as a fallback. - same_site_legacy_workaround: false - -# dsn sets the data source name. This configures the backend where ORY Hydra persists data. -# -## In-memory database -# -# If dsn is "memory", data will be written to memory and is lost when you restart this instance. -# You can set this value using the DSN environment variable: -# -## SQL databases -# -# ORY Hydra supports popular SQL databases. For more detailed configuration information go to: -# https://www.ory.sh/docs/hydra/dependencies-environment#sql -# -### PostgreSQL (recommended) -# -# If dsn is starting with postgres:// PostgreSQL will be used as storage backend: -# dsn: dsn=postgres://user:password@host:123/database -# -### MySQL database -# -# If dsn is starting with mysql:// MySQL will be used as storage backend: -# dsn: mysql://user:password@tcp(host:123)/database -# -### CockroachDB -# -# If dsn is starting with cockroach:// CockroachDB will be used as storage backend: -# dsn: cockroach://user:password@host:123/database -# -dsn: memory -# dsn: postgres://user:password@host:123/database -# dsn: mysql://user:password@tcp(host:123)/database - -# webfinger configures ./well-known/ settings -webfinger: - # jwks configures the /.well-known/jwks.json endpoint. - jwks: - # broadcast_keys is a list of JSON Web Keys that should be exposed at that endpoint. This is usually - # the public key for verifying OpenID Connect ID Tokens. However, you might want to add additional keys here as well. - broadcast_keys: - - hydra.openid.id-token # This key is always exposed by default - # - hydra.jwt.access-token # This key will be exposed when the OAuth2 Access Token strategy is set to JWT. - - # oidc_discovery configures OpenID Connect Discovery (/.well-known/openid-configuration) - oidc_discovery: - client_registration_url: https://my-service.com/clients - # A list of supported claims to be broadcasted. Claim `sub` is always included: - supported_claims: - - email - - username - # The scope OAuth 2.0 Clients may request. Scope `offline`, `offline_access`, and `openid` are always included. - supported_scope: - - email - - whatever - - read.photos - - # A URL of the userinfo endpoint to be advertised at the OpenID Connect - # Discovery endpoint /.well-known/openid-configuration. Defaults to ORY Hydra's userinfo endpoint at /userinfo. - # Set this value if you want to handle this endpoint yourself. - userinfo_url: https://example.org/my-custom-userinfo-endpoint - -# oidc configures OpenID Connect features. -oidc: - # subject_identifiers configures the Subject Identifier algorithm. - # - # For more information please head over to the documentation: - # -> https://www.ory.sh/docs/hydra/advanced#subject-identifier-algorithms - subject_identifiers: - # which algorithms to enable. Defaults to "public" - supported_types: - - pairwise - - public - # configures the pairwise algorithm - pairwise: - # if "pairwise" is enabled, the salt must be defined. - salt: some-random-salt - - # dynamic_client_registration configures OpenID Connect Dynamic Client Registration (exposed as admin endpoints /clients/...) - dynamic_client_registration: - - # The OpenID Connect Dynamic Client Registration specification has no concept of whitelisting OAuth 2.0 Scope. If you - # want to expose Dynamic Client Registration, you should set the default scope enabled for newly registered clients. - # Keep in mind that users can overwrite this default by setting the "scope" key in the registration payload, - # effectively disabling the concept of whitelisted scopes. - default_scope: - - openid - - offline - - offline_access - -urls: - self: - - # This value will be used as the "issuer" in access and ID tokens. It must be - # specified and using HTTPS protocol, unless --dangerous-force-http is set. This should typically be equal - # to the public value. - issuer: https://localhost:4444/ - - # This is the base location of the public endpoints of your ORY Hydra installation. This should typically be equal - # to the issuer value. If left unspecified, it falls back to the issuer value. - public: https://localhost:4444/ - - # Sets the login endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. - login: https://my-login.app/login - # Sets the consent endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. - consent: https://my-consent.app/consent - # Sets the logout endpoint. Defaults to an internal fallback URL. - logout: https://my-logout.app/logout - # Sets the error endpoint. The error ui will be shown when an OAuth2 error occurs that which can not be sent back - # to the client. Defaults to an internal fallback URL. - error: https://my-error.app/error - # When a user agent requests to logout, it will be redirected to this url afterwards per default. - post_logout_redirect: https://my-example.app/logout-successful - -strategies: - scope: DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY - # You may use JSON Web Tokens as access tokens. - # - # But seriously. Don't do that. It's not a great idea and has a ton of caveats and subtle security implications. Read more: - # -> https://www.ory.sh/docs/hydra/advanced#json-web-tokens - # - # access_token: jwt - -# configures time to live -ttl: - # configures how long a user login and consent flow may take. Defaults to 1h. - login_consent_request: 1h - # configures how long access tokens are valid. Defaults to 1h. - access_token: 1h - # configures how long refresh tokens are valid. Defaults to 720h. Set to -1 for refresh tokens to never expire. - refresh_token: 720h - # configures how long id tokens are valid. Defaults to 1h. - id_token: 1h - # configures how long auth codes are valid. Defaults to 10m. - auth_code: 10m - -oauth2: - # Set this to true if you want to share error debugging information with your OAuth 2.0 clients. - # Keep in mind that debug information is very valuable when dealing with errors, but might also expose database error - # codes and similar errors. Defaults to false. - expose_internal_errors: true - # Configures hashing algorithms. Supports only BCrypt at the moment. - hashers: - # Configures the BCrypt hashing algorithm used for hashing Client Secrets. - bcrypt: - # Sets the BCrypt cost. Minimum value is 4 and default value is 10. The higher the value, the more CPU time is being - # used to generate hashes. - cost: 10 - pkce: - # Set this to true if you want PKCE to be enforced for all clients. - enforced: false - # Set this to true if you want PKCE to be enforced for public clients. - enforced_for_public_clients: false - session: - # store encrypted data in database, default true - encrypt_at_rest: true - - ## refresh_token_rotation - # - # By default Refresh Tokens are rotated and invalidated with each use. - # See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2 for more details - # - refresh_token_rotation: - # - ## grace_period - # - # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after its first use. New refresh tokens will continue - # to be issued. - # - # Examples: - # - 5s - # - 1m - # - 0m (default; grace period disabled) - grace_period: 0s - -# The secrets section configures secrets used for encryption and signing of several systems. All secrets can be rotated, -# for more information on this topic navigate to: -# -> https://www.ory.sh/docs/hydra/advanced#rotation-of-hmac-token-signing-and-database-and-cookie-encryption-keys -secrets: - # The system secret must be at least 16 characters long. If none is provided, one will be generated. They key - # is used to encrypt sensitive data using AES-GCM (256 bit) and validate HMAC signatures. - # - # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures - # and decryption. - system: - - this-is-the-primary-secret - - this-is-an-old-secret - - this-is-another-old-secret - # A secret that is used to encrypt cookie sessions. Defaults to secrets.system. It is recommended to use - # a separate secret in production. - # - # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures - # and decryption. - cookie: - - this-is-the-primary-secret - - this-is-an-old-secret - - this-is-another-old-secret - -# Enables profiling if set. Use "cpu" to enable cpu profiling and "mem" to enable memory profiling. For more details -# on profiling, head over to: https://blog.golang.org/profiling-go-programs -profiling: cpu -# profiling: mem - -# ORY Hydra supports distributed tracing. -tracing: - # Set this to the tracing backend you wish to use. Currently supports jaeger. If omitted or empty, tracing will - # be disabled. - provider: jaeger - # Specifies the service name to use on the tracer. - service_name: ORY Hydra - providers: - # Configures the jaeger tracing backend. - jaeger: - # The address of the jaeger-agent where spans should be sent to - local_agent_address: 127.0.0.1:6831 - # The tracing header format - propagation: jaeger - # The maximum length of jaeger tag value - max_tag_value_length: 1024 - sampling: - # The type of the sampler you want to use. Supports: - # - const - # - probabilistic - # - ratelimiting - type: const - # The value passed to the sampler type that has been configured. - # Supported values: This is dependant on the sampling strategy used: - # - const: 0 or 1 (all or nothing) - # - rateLimiting: a constant rate (e.g. setting this to 3 will sample requests with the rate of 3 traces per second) - # - probabilistic: a value between 0..1 - value: 1.0 - # The address of jaeger-agent's HTTP sampling server - server_url: http://localhost:5778/sampling +# ORY Hydra Configuration +# +# +# !!WARNING!! +# This configuration file is for documentation purposes only. Do not use it in production. As all configuration items +# are enabled, it will not work out of the box either. +# +# +# ORY Hydra can be configured using a configuration file and passing the file location using `--config path/to/config.yaml`. +# Per default, ORY Hydra will look up and load file ~/.hydra.yaml. All configuration keys can be set using environment +# variables as well. +# +# Setting environment variables is easy: +# +## Linux / OSX +# +# $ export MY_ENV_VAR=foo +# $ hydra ... +# +# alternatively: +# +# $ MY_ENV_VAR=foo hydra ... +# +## Windows +# +### Command Prompt +# +# > set MY_ENV_VAR=foo +# > hydra ... +# +### Powershell +# +# > $env:MY_ENV_VAR="foo" +# > hydra ... +# +## Docker +# +# $ docker run -e MY_ENV_VAR=foo oryd/hydra:... +# +# +# Assuming the following configuration layout: +# +# serve: +# public: +# port: 4444 +# something_else: foobar +# +# Key `something_else` can be set as an environment variable by uppercasing it's path: +# `serve.public.port.somethihng_else` -> `SERVE.PUBLIC.PORT.SOMETHING_ELSE` +# and replacing `.` with `_`: +# `serve.public.port.somethihng_else` -> `SERVE_PUBLIC_PORT_SOMETHING_ELSE` +# +# Environment variables always override values from the configuration file. Here are some more examples: +# +# Configuration key | Environment variable | +# ------------------|----------------------| +# dsn | DSN | +# serve.admin.host | SERVE_ADMIN_HOST | +# ------------------|----------------------| +# +# +# List items such as +# +# secrets: +# system: +# - this-is-the-primary-secret +# - this-is-an-old-secret +# - this-is-another-old-secret +# +# must be separated using `,` when using environment variables. The environment variable equivalent to the code section# +# above is: +# +# Linux/macOS: $ export SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret +# Windows: > set SECRETS_SYSTEM=this-is-the-primary-secret,this-is-an-old-secret,this-is-another-old-secret + +# log configures the logger +log: + # Sets the log level, supports "panic", "fatal", "error", "warn", "info" and "debug". Defaults to "info". + level: info + # Sets the log format. Leave it undefined for text based log format, or set to "json" for JSON formatting. + format: json + +# serve controls the configuration for the http(s) daemon(s). +serve: + # public controls the public daemon serving public API endpoints like /oauth2/auth, /oauth2/token, /.well-known/jwks.json + public: + # The port to listen on. Defaults to 4444 + port: 4444 + # The interface or unix socket ORY Hydra should listen and handle public API requests on. + # Use the prefix "unix:" to specify a path to a unix socket. + # Leave empty to listen on all interfaces. + host: localhost # leave this out or empty to listen on all devices which is the default + # host: unix:/path/to/socket + # socket: + # owner: hydra + # group: hydra + # mode: 0775 + + # cors configures Cross Origin Resource Sharing for public endpoints. + cors: + # set enabled to true to enable CORS. Defaults to false. + enabled: true + # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. + # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) + # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. + # + # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). + allowed_origins: + - https://example.com + - https://*.example.com + # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain + # requests. Defaults to the methods listed. + allowed_methods: + - POST + - GET + - PUT + - PATCH + - DELETE + + # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. + allowed_headers: + - Authorization + - Content-Type + + # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. + exposed_headers: + - Content-Type + + # Sets whether the request can include user credentials like cookies, HTTP authentication + # or client side SSL certificates. Defaults to true. + allow_credentials: true + + # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request + # is preceded by a preflight request. Defaults to 0. + max_age: 10 + + # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. + debug: true + + # Access Log configuration for public server. + access_log: + # Disable access log for health endpoints. + disable_for_health: false + + # admin controls the admin daemon serving admin API endpoints like /jwk, /client, ... + admin: + # The port to listen on. Defaults to 4445 + port: 4445 + # The interface or unix socket ORY Hydra should listen and handle administrative API requests on. + # Use the prefix "unix:" to specify a path to a unix socket. + # Leave empty to listen on all interfaces. + host: localhost # leave this out or empty to listen on all devices which is the default + # host: unix:/path/to/socket + # socket: + # owner: hydra + # group: hydra + # mode: 0775 + + # cors configures Cross Origin Resource Sharing for admin endpoints. + cors: + # set enabled to true to enable CORS. Defaults to false. + enabled: true + # allowed_origins is a list of origins (comma separated values) a cross-domain request can be executed from. + # If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) + # to replace 0 or more characters (i.e.: http://*.domain.com). Only one wildcard can be used per origin. + # + # If empty or undefined, this defaults to `*`, allowing CORS from every domain (if cors.enabled: true). + allowed_origins: + - https://example.com + - https://*.example.com + # allowed_methods is list of HTTP methods the user agent is allowed to use with cross-domain + # requests. Defaults to GET and POST. + allowed_methods: + - POST + - GET + - PUT + - PATCH + - DELETE + + # A list of non simple headers the client is allowed to use with cross-domain requests. Defaults to the listed values. + allowed_headers: + - Authorization + - Content-Type + + # Sets which headers (comma separated values) are safe to expose to the API of a CORS API specification. Defaults to the listed values. + exposed_headers: + - Content-Type + + # Sets whether the request can include user credentials like cookies, HTTP authentication + # or client side SSL certificates. + allow_credentials: true + + # Sets how long (in seconds) the results of a preflight request can be cached. If set to 0, every request + # is preceded by a preflight request. Defaults to 0. + max_age: 10 + + # If set to true, adds additional log output to debug server side CORS issues. Defaults to false. + debug: true + + # Access Log configuration for admin server. + access_log: + # Disable access log for health endpoints. + disable_for_health: false + + # tls configures HTTPS (HTTP over TLS). If configured, the server automatically supports HTTP/2. + tls: + # key configures the private key (pem encoded) + key: + # The key can either be loaded from a file: + path: /path/to/key.pem + # Or from a base64 encoded (without padding) string: + base64: LS0tLS1CRUdJTiBFTkNSWVBURUQgUFJJVkFURSBLRVktLS0tLVxuTUlJRkRqQkFCZ2txaGtpRzl3MEJCUTB3... + + # cert configures the TLS certificate (PEM encoded) + cert: + # The cert can either be loaded from a file: + path: /path/to/cert.pem + # Or from a base64 encoded (without padding) string: + base64: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXG5NSUlEWlRDQ0FrMmdBd0lCQWdJRVY1eE90REFOQmdr... + + # Whitelist one or multiple CIDR address ranges and allow them to terminate TLS connections. + # Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but + # your proxy / gateway / load balancer. Supports ipv4 and ipv6. + # + # Hydra serves http instead of https when this option is set. + # + # For more information head over to: https://www.ory.sh/docs/hydra/production#tls-termination + allow_termination_from: + - 127.0.0.1/32 + cookies: + # specify the SameSite mode that cookies should be sent with + same_site_mode: Lax + + # Some older browser versions don't work with SameSite=None. This option enables the workaround + # defined in https://web.dev/samesite-cookie-recipes/ which essentially stores a second cookie + # without SameSite as a fallback. + same_site_legacy_workaround: false + +# dsn sets the data source name. This configures the backend where ORY Hydra persists data. +# +## In-memory database +# +# If dsn is "memory", data will be written to memory and is lost when you restart this instance. +# You can set this value using the DSN environment variable: +# +## SQL databases +# +# ORY Hydra supports popular SQL databases. For more detailed configuration information go to: +# https://www.ory.sh/docs/hydra/dependencies-environment#sql +# +### PostgreSQL (recommended) +# +# If dsn is starting with postgres:// PostgreSQL will be used as storage backend: +# dsn: dsn=postgres://user:password@host:123/database +# +### MySQL database +# +# If dsn is starting with mysql:// MySQL will be used as storage backend: +# dsn: mysql://user:password@tcp(host:123)/database +# +### CockroachDB +# +# If dsn is starting with cockroach:// CockroachDB will be used as storage backend: +# dsn: cockroach://user:password@host:123/database +# +dsn: memory +# dsn: postgres://user:password@host:123/database +# dsn: mysql://user:password@tcp(host:123)/database + +# webfinger configures ./well-known/ settings +webfinger: + # jwks configures the /.well-known/jwks.json endpoint. + jwks: + # broadcast_keys is a list of JSON Web Keys that should be exposed at that endpoint. This is usually + # the public key for verifying OpenID Connect ID Tokens. However, you might want to add additional keys here as well. + broadcast_keys: + - hydra.openid.id-token # This key is always exposed by default + # - hydra.jwt.access-token # This key will be exposed when the OAuth2 Access Token strategy is set to JWT. + + # oidc_discovery configures OpenID Connect Discovery (/.well-known/openid-configuration) + oidc_discovery: + client_registration_url: https://my-service.com/clients + # A list of supported claims to be broadcasted. Claim `sub` is always included: + supported_claims: + - email + - username + # The scope OAuth 2.0 Clients may request. Scope `offline`, `offline_access`, and `openid` are always included. + supported_scope: + - email + - whatever + - read.photos + + # A URL of the userinfo endpoint to be advertised at the OpenID Connect + # Discovery endpoint /.well-known/openid-configuration. Defaults to ORY Hydra's userinfo endpoint at /userinfo. + # Set this value if you want to handle this endpoint yourself. + userinfo_url: https://example.org/my-custom-userinfo-endpoint + +# oidc configures OpenID Connect features. +oidc: + # subject_identifiers configures the Subject Identifier algorithm. + # + # For more information please head over to the documentation: + # -> https://www.ory.sh/docs/hydra/advanced#subject-identifier-algorithms + subject_identifiers: + # which algorithms to enable. Defaults to "public" + supported_types: + - pairwise + - public + # configures the pairwise algorithm + pairwise: + # if "pairwise" is enabled, the salt must be defined. + salt: some-random-salt + + # dynamic_client_registration configures OpenID Connect Dynamic Client Registration (exposed as admin endpoints /clients/...) + dynamic_client_registration: + + # The OpenID Connect Dynamic Client Registration specification has no concept of whitelisting OAuth 2.0 Scope. If you + # want to expose Dynamic Client Registration, you should set the default scope enabled for newly registered clients. + # Keep in mind that users can overwrite this default by setting the "scope" key in the registration payload, + # effectively disabling the concept of whitelisted scopes. + default_scope: + - openid + - offline + - offline_access + +urls: + self: + + # This value will be used as the "issuer" in access and ID tokens. It must be + # specified and using HTTPS protocol, unless --dangerous-force-http is set. This should typically be equal + # to the public value. + issuer: https://localhost:4444/ + + # This is the base location of the public endpoints of your ORY Hydra installation. This should typically be equal + # to the issuer value. If left unspecified, it falls back to the issuer value. + public: https://localhost:4444/ + + # Sets the login endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. + login: https://my-login.app/login + # Sets the consent endpoint of the User Login & Consent flow. Defaults to an internal fallback URL. + consent: https://my-consent.app/consent + # Sets the logout endpoint. Defaults to an internal fallback URL. + logout: https://my-logout.app/logout + # Sets the error endpoint. The error ui will be shown when an OAuth2 error occurs that which can not be sent back + # to the client. Defaults to an internal fallback URL. + error: https://my-error.app/error + # When a user agent requests to logout, it will be redirected to this url afterwards per default. + post_logout_redirect: https://my-example.app/logout-successful + +strategies: + scope: DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY + # You may use JSON Web Tokens as access tokens. + # + # But seriously. Don't do that. It's not a great idea and has a ton of caveats and subtle security implications. Read more: + # -> https://www.ory.sh/docs/hydra/advanced#json-web-tokens + # + # access_token: jwt + +# configures time to live +ttl: + # configures how long a user login and consent flow may take. Defaults to 1h. + login_consent_request: 1h + # configures how long access tokens are valid. Defaults to 1h. + access_token: 1h + # configures how long refresh tokens are valid. Defaults to 720h. Set to -1 for refresh tokens to never expire. + refresh_token: 720h + # configures how long id tokens are valid. Defaults to 1h. + id_token: 1h + # configures how long auth codes are valid. Defaults to 10m. + auth_code: 10m + +oauth2: + # Set this to true if you want to share error debugging information with your OAuth 2.0 clients. + # Keep in mind that debug information is very valuable when dealing with errors, but might also expose database error + # codes and similar errors. Defaults to false. + expose_internal_errors: true + # Configures hashing algorithms. Supports only BCrypt at the moment. + hashers: + # Configures the BCrypt hashing algorithm used for hashing Client Secrets. + bcrypt: + # Sets the BCrypt cost. Minimum value is 4 and default value is 10. The higher the value, the more CPU time is being + # used to generate hashes. + cost: 10 + pkce: + # Set this to true if you want PKCE to be enforced for all clients. + enforced: false + # Set this to true if you want PKCE to be enforced for public clients. + enforced_for_public_clients: false + session: + # store encrypted data in database, default true + encrypt_at_rest: true + ## refresh_token_rotation + # + # By default Refresh Tokens are rotated and invalidated with each use. + # See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2 for more details + # + refresh_token_rotation: + # + ## grace_period + # + # Set the grace period for a refresh token to allow it to be used for the duration of this configuration after its first use. New refresh tokens will continue + # to be issued. + # + # Examples: + # - 5s + # - 1m + # - 0s (default; grace period disabled) + grace_period: 0s + +# The secrets section configures secrets used for encryption and signing of several systems. All secrets can be rotated, +# for more information on this topic navigate to: +# -> https://www.ory.sh/docs/hydra/advanced#rotation-of-hmac-token-signing-and-database-and-cookie-encryption-keys +secrets: + # The system secret must be at least 16 characters long. If none is provided, one will be generated. They key + # is used to encrypt sensitive data using AES-GCM (256 bit) and validate HMAC signatures. + # + # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures + # and decryption. + system: + - this-is-the-primary-secret + - this-is-an-old-secret + - this-is-another-old-secret + # A secret that is used to encrypt cookie sessions. Defaults to secrets.system. It is recommended to use + # a separate secret in production. + # + # The first item in the list is used for signing and encryption. The whole list is used for verifying signatures + # and decryption. + cookie: + - this-is-the-primary-secret + - this-is-an-old-secret + - this-is-another-old-secret + + +# Enables profiling if set. Use "cpu" to enable cpu profiling and "mem" to enable memory profiling. For more details +# on profiling, head over to: https://blog.golang.org/profiling-go-programs +profiling: cpu +# profiling: mem + +# ORY Hydra supports distributed tracing. +tracing: + # Set this to the tracing backend you wish to use. Currently supports jaeger. If omitted or empty, tracing will + # be disabled. + provider: jaeger + # Specifies the service name to use on the tracer. + service_name: ORY Hydra + providers: + # Configures the jaeger tracing backend. + jaeger: + # The address of the jaeger-agent where spans should be sent to + local_agent_address: 127.0.0.1:6831 + # The tracing header format + propagation: jaeger + # The maximum length of jaeger tag value + max_tag_value_length: 1024 + sampling: + # The type of the sampler you want to use. Supports: + # - const + # - probabilistic + # - ratelimiting + type: const + # The value passed to the sampler type that has been configured. + # Supported values: This is dependant on the sampling strategy used: + # - const: 0 or 1 (all or nothing) + # - rateLimiting: a constant rate (e.g. setting this to 3 will sample requests with the rate of 3 traces per second) + # - probabilistic: a value between 0..1 + value: 1.0 + # The address of jaeger-agent's HTTP sampling server + server_url: http://localhost:5778/sampling From 69ca392848b13d334cd76a7fb85763f1749b413e Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:49:18 -0700 Subject: [PATCH 18/29] remove unneeded file --- persistence/sql/models/models.go | 1 - 1 file changed, 1 deletion(-) delete mode 100644 persistence/sql/models/models.go diff --git a/persistence/sql/models/models.go b/persistence/sql/models/models.go deleted file mode 100644 index 2640e7f93e..0000000000 --- a/persistence/sql/models/models.go +++ /dev/null @@ -1 +0,0 @@ -package models From 8fa6c8cee2fb298afb7d18de9026acdfb2b81785 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 13:13:51 -0700 Subject: [PATCH 19/29] make encryption of session more obvious --- persistence/sql/persister_oauth2.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index d548fca1ba..f13143594b 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -110,22 +110,15 @@ func (p *Persister) marshalSession(session fosite.Session) ([]byte, error) { return nil, err } - if sessionBytes, err = p.maybeEncryptSession(sessionBytes); err != nil { - return nil, err - } - return sessionBytes, nil -} - -// MaybeEncryptSession encrypt a session if configuration indicates it should -func (p *Persister) maybeEncryptSession(session []byte) ([]byte, error) { if !p.config.EncryptSessionData() { - return session, nil + return sessionBytes, nil } - ciphertext, err := p.r.KeyCipher().Encrypt(session) + ciphertext, err := p.r.KeyCipher().Encrypt(sessionBytes) if err != nil { return nil, err } + return []byte(ciphertext), nil } From bd8a15e2f3b8d3cabfe801b0ef0caf75bc6aa30e Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:00:35 -0700 Subject: [PATCH 20/29] rename used to in_grace_period --- ...efresh_token_in_grace_period_flag.down.sql | 1 + ..._refresh_token_in_grace_period_flag.up.sql | 1 + ...93405_add_refresh_token_used_flag.down.sql | 1 - ...1093405_add_refresh_token_used_flag.up.sql | 1 - persistence/sql/persister_oauth2.go | 28 +++++++++---------- 5 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.down.sql create mode 100644 persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.up.sql delete mode 100644 persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.down.sql delete mode 100644 persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.up.sql diff --git a/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.down.sql b/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.down.sql new file mode 100644 index 0000000000..163e5f5cc0 --- /dev/null +++ b/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.down.sql @@ -0,0 +1 @@ +ALTER TABLE hydra_oauth2_refresh DROP COLUMN in_grace_period; diff --git a/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.up.sql b/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.up.sql new file mode 100644 index 0000000000..3a94b7dc7c --- /dev/null +++ b/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.up.sql @@ -0,0 +1 @@ +ALTER TABLE hydra_oauth2_refresh ADD in_grace_period bool DEFAULT false; diff --git a/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.down.sql b/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.down.sql deleted file mode 100644 index 94c35e4c72..0000000000 --- a/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE hydra_oauth2_refresh DROP COLUMN used; diff --git a/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.up.sql b/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.up.sql deleted file mode 100644 index fb92c2b17c..0000000000 --- a/persistence/sql/migrations/20211101093405_add_refresh_token_used_flag.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE hydra_oauth2_refresh ADD used bool DEFAULT false; diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index f13143594b..14872fe6e4 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -233,7 +233,7 @@ func (p *Persister) createSession(ctx context.Context, signature string, request return nil } -func (p *Persister) updateRefreshSession(ctx context.Context, requestId string, session fosite.Session, used bool) error { +func (p *Persister) updateRefreshSession(ctx context.Context, requestId string, session fosite.Session, inGracePeriod bool) error { _, ok := session.(*oauth2.Session) if !ok && session != nil { return errors.Errorf("expected session to be of type *oauth2.Session but got: %T", session) @@ -243,11 +243,11 @@ func (p *Persister) updateRefreshSession(ctx context.Context, requestId string, return err } - updateSql := fmt.Sprintf("UPDATE %s SET session_data = ?, used = ? WHERE request_id = ?", + updateSql := fmt.Sprintf("UPDATE %s SET session_data = ?, in_grace_period = ? WHERE request_id = ?", sqlTableRefresh.TableName()) return p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { - err := p.Connection(ctx).RawQuery(updateSql, sessionBytes, used, requestId).Exec() + err := p.Connection(ctx).RawQuery(updateSql, sessionBytes, inGracePeriod, requestId).Exec() if errors.Is(err, sql.ErrNoRows) { return errorsx.WithStack(fosite.ErrNotFound) } else if err != nil { @@ -318,18 +318,18 @@ func (p *Persister) deactivateSessionByRequestID(ctx context.Context, id string, return sqlcon.HandleError( p.Connection(ctx). RawQuery( - fmt.Sprintf("UPDATE %s SET active=false, used=true WHERE request_id=?", OAuth2RequestSQL{Table: table}.TableName()), + fmt.Sprintf("UPDATE %s SET active=false, in_grace_period=true WHERE request_id=?", OAuth2RequestSQL{Table: table}.TableName()), id, ). Exec(), ) } -func (p *Persister) getRefreshTokenUsedStatusBySignature(ctx context.Context, signature string) (bool, error) { - var used bool - return used, p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { - query := fmt.Sprintf("SELECT used FROM %s WHERE signature = ?", sqlTableRefresh.TableName()) - err := p.Connection(ctx).RawQuery(query, signature).First(&used) +func (p *Persister) getRefreshTokenGracePeriodStatusBySignature(ctx context.Context, signature string) (bool, error) { + var inGracePeriod bool + return inGracePeriod, p.transaction(ctx, func(ctx context.Context, c *pop.Connection) error { + query := fmt.Sprintf("SELECT in_grace_period FROM %s WHERE signature = ?", sqlTableRefresh.TableName()) + err := p.Connection(ctx).RawQuery(query, signature).First(&inGracePeriod) if errors.Is(err, sql.ErrNoRows) { return errorsx.WithStack(fosite.ErrNotFound) } else if err != nil { @@ -423,13 +423,13 @@ func (p *Persister) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requ return errors.WithStack(err) } - var used bool - if used,err = p.getRefreshTokenUsedStatusBySignature(ctx, signature); err != nil { - p.l.Errorf("signature: %s used status not found. grace period not applied", signature) + var inGracePeriod bool + if inGracePeriod,err = p.getRefreshTokenGracePeriodStatusBySignature(ctx, signature); err != nil { + p.l.Errorf("signature: %s in_grace_period status not found. grace period not applied", signature) return errors.WithStack(err) } - if ! used { + if ! inGracePeriod { session := requester.GetSession() session.SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(gracePeriod)) if err = p.updateRefreshSession(ctx, requestId, session, true); err != nil { @@ -437,7 +437,7 @@ func (p *Persister) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requ return errors.WithStack(err) } } else { - p.l.Debugf("request_id: %s has already been used and is in the grace period", requestId) + p.l.Tracef("request_id: %s is in the grace period", requestId) } return nil } From 13cdea853e841a437dc1fac997c2fa1719f3536e Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Mon, 22 Nov 2021 15:11:43 -0700 Subject: [PATCH 21/29] when deactivating a refresh token, in_grace_period should be false --- persistence/sql/persister_oauth2.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index 14872fe6e4..7bb1924667 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -318,7 +318,7 @@ func (p *Persister) deactivateSessionByRequestID(ctx context.Context, id string, return sqlcon.HandleError( p.Connection(ctx). RawQuery( - fmt.Sprintf("UPDATE %s SET active=false, in_grace_period=true WHERE request_id=?", OAuth2RequestSQL{Table: table}.TableName()), + fmt.Sprintf("UPDATE %s SET active=false, in_grace_period=false WHERE request_id=?", OAuth2RequestSQL{Table: table}.TableName()), id, ). Exec(), From 1f642d39fd12e82a68ab99294565b8c05e1a7490 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Fri, 3 Dec 2021 12:00:58 -0700 Subject: [PATCH 22/29] add testing the refresh token store when grace period is configured use reflection to control configuring the persister during tests --- driver/registry_sql.go | 5 +++++ oauth2/fosite_store_helpers.go | 30 +++++++++++++++++------------ persistence/sql/persister_oauth2.go | 6 +++--- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/driver/registry_sql.go b/driver/registry_sql.go index 2e5733346f..d75412f953 100644 --- a/driver/registry_sql.go +++ b/driver/registry_sql.go @@ -49,6 +49,11 @@ func NewRegistrySQL() *RegistrySQL { return r } +// SetConfig used for testing only. +func(m *RegistrySQL) SetConfig(key string, value interface{}) { + m.C.Set(key,value) +} + func (m *RegistrySQL) Init(ctx context.Context) error { if m.persister == nil { var opts []instrumentedsql.Opt diff --git a/oauth2/fosite_store_helpers.go b/oauth2/fosite_store_helpers.go index a0fa4658b1..5e91442ef1 100644 --- a/oauth2/fosite_store_helpers.go +++ b/oauth2/fosite_store_helpers.go @@ -25,6 +25,7 @@ import ( "crypto/sha256" "fmt" "net/url" + "reflect" "testing" "time" @@ -412,6 +413,15 @@ func testHelperRevokeAccessToken(x InternalRegistry) func(t *testing.T) { func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *testing.T) { + setConfig := func(registry InternalRegistry, key string, value interface{}){ + r := reflect.ValueOf(registry) + updateConfig := r.MethodByName("SetConfig") + updateConfig.Call([]reflect.Value{ + reflect.ValueOf(key), + reflect.ValueOf(value), + }) + } + return func(t *testing.T) { t.Run("Revokes refresh token when grace period not configured", func(t *testing.T) { // SETUP @@ -424,49 +434,45 @@ func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *te // ACT err = m.RevokeRefreshTokenMaybeGracePeriod(ctx, defaultRequest.GetID(), refreshTokenSession) - - // ASSERT assert.NoError(t, err) tmpSession := new(fosite.Session) _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) + // ASSERT // a revoked refresh token returns an error when getting the token again assert.Error(t, err) assert.True(t, errors.Is(err, fosite.ErrInactiveToken)) }) + t.Run("refresh token enters grace period when configured,", func(t *testing.T) { - /* TODO: figure out how to change config values and get a new/udpated OAuth2Storage instance // SETUP - log := logrusx.New("testGracePeriod", "na") - option := configx.WithValue("oauth2.refresh_token_rotation.grace_period", "1m") - c := config.MustNew(log, option) + setConfig(x, "oauth2.refresh_token_rotation.grace_period", "1m") - //TODO make m := OAuth2Storage + // always reset back to the default + defer setConfig(x, "oauth2.refresh_token_rotation.grace_period", "0m") ctx := context.Background() + m := x.OAuth2Storage() - refreshTokenSession := fmt.Sprintf("refresh_token_%d", time.Now().Unix()) + refreshTokenSession := fmt.Sprintf("refresh_token_%d_with_grace_period", time.Now().Unix()) err := m.CreateRefreshTokenSession(ctx, refreshTokenSession, &defaultRequest) assert.NoError(t, err, "precondition failed: could not create refresh token session") // ACT err = m.RevokeRefreshTokenMaybeGracePeriod(ctx,defaultRequest.GetID(), refreshTokenSession) - // ASSERT assert.NoError(t, err) tmpSession := new(fosite.Session) _, err = m.GetRefreshTokenSession(ctx, refreshTokenSession, *tmpSession) + // ASSERT // when grace period is configured the refresh token can be obtained within // the grace period without error assert.NoError(t, err) - - registry.WithConfig(oldConfig) - */ }) } diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index 7bb1924667..9a67c29614 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -429,10 +429,10 @@ func (p *Persister) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requ return errors.WithStack(err) } + requesterSession := requester.GetSession() if ! inGracePeriod { - session := requester.GetSession() - session.SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(gracePeriod)) - if err = p.updateRefreshSession(ctx, requestId, session, true); err != nil { + requesterSession.SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(gracePeriod)) + if err = p.updateRefreshSession(ctx, requestId, requesterSession, true); err != nil { p.l.Errorf("failed to update session with signature: %s", signature) return errors.WithStack(err) } From f244b61e72e7008370e6f5b75690549ad7bc1463 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Tue, 4 Jan 2022 11:32:50 -0700 Subject: [PATCH 23/29] npx prettier --write {test,cypress}/**/*.js --- docs/docs/guides/token-expiration.mdx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/docs/guides/token-expiration.mdx b/docs/docs/guides/token-expiration.mdx index ec62b44093..fd933deec4 100644 --- a/docs/docs/guides/token-expiration.mdx +++ b/docs/docs/guides/token-expiration.mdx @@ -79,9 +79,10 @@ for refresh tokens to never expire. When a refresh token is used it is deactivated, which is known as Refresh Token Rotation. By default Hydra deactivates the refresh token it receives and issues -a new token. If a deactivated refresh token is used again, all tokens related to that refresh token -will also be deactivated. More information on Refresh Token Rotation can be found in the -Recommendations section of the OAuth 2.0 Security Best Practices document +a new token. If a deactivated refresh token is used again, all tokens related to +that refresh token will also be deactivated. More information on Refresh Token +Rotation can be found in the Recommendations section of the OAuth 2.0 Security +Best Practices document [here](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.13.2). There are some cases when a one time use refresh token may be undesirable, such From a487706a14380496cfb28fa91ca8fc7024bbb1b5 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Tue, 11 Jan 2022 16:46:25 +0100 Subject: [PATCH 24/29] cchore: format --- CONTRIBUTORS | 150 ++++++++++++++-------------- driver/config/provider.go | 2 +- driver/registry_sql.go | 4 +- oauth2/fosite_store_helpers.go | 6 +- persistence/sql/persister_oauth2.go | 4 +- 5 files changed, 84 insertions(+), 82 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index a7aa3d1e25..3ee2120336 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,17 +1,12 @@ # contributors generated by `make contributors` 115100 <115100@users.noreply.github.com> -abusaidm Ackermann Yuriy -Aeneas Aeneas +Aeneas Aeneas -aeneasr <3372410+aeneasr@users.noreply.github.com> -aeneasr -aeneasr -aeneasr -Aeneas Rekkas Aeneas Rekkas (arekkas) +Aeneas Rekkas Ajanthan Akbar Uddin Kashif Alano Terblanche @@ -26,33 +21,25 @@ André Filipe Ankit Singh Ante Mihalj Anton Samoylenko -arapaho -arekkas -arekkas Aritz Berasarte Arkady Bagdasarov <56913719+arkady-bagdasarov@users.noreply.github.com> Artem Yarmoliuk Arthur Knoepflin -arunas-ka -aspeteRakete Atallah khedrane BastianHofmann -Benjamin Tanone Ben Scholzen +Benjamin Tanone Bernat Mut <1116133+monwolf@users.noreply.github.com> -bfrisbie-brex <63267486+bfrisbie-brex@users.noreply.github.com> Bhargav SNV <44526455+Gituser143@users.noreply.github.com> +Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Brandon Philips Brian <20056195+coopbri@users.noreply.github.com> Brian Teller Bruno Bigras Bruno Heridet -catper <60221155+catper@users.noreply.github.com> -cherrymu Chris Mack Christian Dreier Christian Skovholm -clausdenk Corey Burmeister Dallan Quass Damien Bravin @@ -60,30 +47,23 @@ Daniel Jiménez Daniel Schellhorn Daniel Shuy Daniel Sutton -darron froese Dave Kushner <1289314+dkushner@users.noreply.github.com> David +David David Lobe David López -David David Wilkins -debrutal DennisPattmann5012 <44261569+DennisPattmann5012@users.noreply.github.com> Dexter Chua -dharmendraImprowised <72780358+DKImprowised@users.noreply.github.com> Dibyajyoti Behera Dimitrij Drus Dimitrij Drus Divyansh Bansal Dmitry Dmitry Dolbik -ducksecops Edward Wilde -emil Eric Douglas Euan Kemp -fazal -fazal Felix Jung Flavio Leggio Flori <40140792+fl0lli@users.noreply.github.com> @@ -98,14 +78,10 @@ Grant Zvolsky Grant Zvolský Greg Woodcock Grigory -hackerman <3372410+aeneasr@users.noreply.github.com> -hackerman Hans Harsimran Singh Maan Helmuth Bederna <25813283+IonFoXx@users.noreply.github.com> Hendrik Heil -hisamura333 <0aw2794w78t0b4c@ezweb.ne.jp> -İbrahim Esen Igor Zibarev Imran Ismail Iñigo @@ -114,14 +90,10 @@ Jacek Symonowicz Jakub Błaszczyk Jakub Błaszczyk James Elliott -jamesnicolas Jamie Stackhouse -Jan Beckmann Jan +Jan Beckmann Jay Linski -jayme-github -jess -jhuggett <59655877+jhuggett@users.noreply.github.com> JiaLiPassion Jimmy Stridh Joao Carlos @@ -134,92 +106,75 @@ Joshua Obasaju <41480580+obasajujoshua31@users.noreply.github.com> Julian Tescher Justin Clift Kevin Minehart -khevse Kim Neunert Kishan B -kobayashilin Kostya Lepa Kunal Parikh -lauri LemurP Lennart Rosam Louis Laureys Luis Pedrosa <2365589+lpedrosa@users.noreply.github.com> Lukasz Jagiello Luke Stoward +MOZGIII Marco Hutzsch <39520486+marcohutzsch1234@users.noreply.github.com> +Mart Aarma Masoud Tahmasebi Matheus Moraes +Matouš Dzivjak Matt Bonnell <64976795+mbonnell-wish@users.noreply.github.com> Matt Bonnell Matt Drollette -Matteo Suppo -Matthew Fawcett Matt Vinall Matt Vinall +Matteo Suppo +Matthew Fawcett Maurizio -Maxime Song Max Köhler -michaelwagler -Mitar +Maxime Song Mitar -mkontani +Mitar Moritz Lang -MOZGIII Natalia Nathan Mills -naveenpaul1 <79908956+naveenpaul1@users.noreply.github.com> Neeraj Nejcraft -nessita Nestor Nick Otter -NickUfer Nick Ufer +NickUfer Nikita Puzankov -NikolaySl Nikolay Stupak -nishanth2143 -olFi95 -Olivier Deckers -Olivier Tremblay +NikolaySl ORY Continuous Integration ORY Continuous Integration ORY Continuous Integration +Olivier Deckers +Olivier Tremblay Oz Haven Patrick Barker Patrick Tescher Patrik Paul Harman Petr Jediný -phi2039 Philip Nicolcev <33558528+pnicolcev-tulipretail@users.noreply.github.com> Philip Nicolcev -phiremande <16595434+phiremande@users.noreply.github.com> Pierre-David Bélanger -pike1212 -pike1212 -prateek1192 <1192prateek@gmail.com> Prateek Malhotra Quentin Perez +RNBack Ricardo Iván Vieitez Parra <3857362+corrideat@users.noreply.github.com> -Richard Zana Rich Wareham -rickwang7712 +Richard Zana RikiyaFujii -RNBack -robhinds Rob Smith Roman Lytvyn Roman Minkin Saad Tazi -sagarshah1983 SaintMalik <37118134+saintmalik@users.noreply.github.com> Samuele Lilli Sawada Shota -sawadashota Sawada Shota -seremenko-wish <60801091+seremenko-wish@users.noreply.github.com> Shadaï ALI Shane Starcher Shankar Dhanasekaran @@ -227,35 +182,28 @@ Shaurya Dhadwal Shota SAWADA Simon Lipp Simon-Pierre Gingras <892367+spg@users.noreply.github.com> -simpleway Smotrov Dmitriy Stepan Rakitin Stephan Renatus Steve Kaliski Sufijen Bani Sven Neuhaus +T Venu Madhav The Gitter Badger Thibault Doubliez -Thomas Aidan Curran Thomas Aidan Curran +Thomas Aidan Curran Thomas Recloux Thomas Stewart Thor Marius Henrichsen TilmanTheile <50573074+TilmanTheile@users.noreply.github.com> -timothyknight Tim Sazon -tutman96 <11356668+tutman96@users.noreply.github.com> -T Venu Madhav -tyaps Vadim -vancity-amir <62674577+vancity-amir@users.noreply.github.com> Vincent Vinci Xu <277040271@qq.com> -vinckr Vishesh Handa Vitaly Migunov Vladimir Kalugin -wanderer163 <93438190+wanderer163@users.noreply.github.com> Wei Cheng Wojciech Kuźmiński <45849042+woojtek@users.noreply.github.com> Wyatt Anderson @@ -263,6 +211,62 @@ Yannick Heinrich Yorman ����’.͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇͇Ỏ̷͖͈̞̩͎̻̫̫̜͉̠̫͕̭̭̫ ฏ๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎๎ Yuki Hirasawa <48427044+hirasawayuki@users.noreply.github.com> Zbigniew Mandziejewicz +abusaidm +aeneasr <3372410+aeneasr@users.noreply.github.com> +aeneasr +aeneasr +aeneasr +arapaho +arekkas +arekkas +arunas-ka +aspeteRakete +bfrisbie-brex <63267486+bfrisbie-brex@users.noreply.github.com> +bill-robbins-ss +catper <60221155+catper@users.noreply.github.com> +cherrymu +clausdenk +darron froese +debrutal +dharmendraImprowised <72780358+DKImprowised@users.noreply.github.com> +ducksecops +emil +fazal +fazal +hackerman <3372410+aeneasr@users.noreply.github.com> +hackerman +hisamura333 <0aw2794w78t0b4c@ezweb.ne.jp> +jamesnicolas +jayme-github +jess +jhuggett <59655877+jhuggett@users.noreply.github.com> +khevse +kobayashilin +lauri +michaelwagler +mkontani +naveenpaul1 <79908956+naveenpaul1@users.noreply.github.com> +nessita +nishanth2143 +olFi95 +phi2039 +phiremande <16595434+phiremande@users.noreply.github.com> +pike1212 +pike1212 +prateek1192 <1192prateek@gmail.com> +rickwang7712 +robhinds +sagarshah1983 +sawadashota +seremenko-wish <60801091+seremenko-wish@users.noreply.github.com> +simpleway +timothyknight +tutman96 <11356668+tutman96@users.noreply.github.com> +tyaps +vancity-amir <62674577+vancity-amir@users.noreply.github.com> +vinckr +wanderer163 <93438190+wanderer163@users.noreply.github.com> zepatrik zepatrik +İbrahim Esen 巢鹏 diff --git a/driver/config/provider.go b/driver/config/provider.go index 901565652a..5e8ecf1cb4 100644 --- a/driver/config/provider.go +++ b/driver/config/provider.go @@ -481,6 +481,6 @@ func (p *Provider) GrantTypeJWTBearerMaxDuration() time.Duration { return p.p.DurationF(KeyOAuth2GrantJWTMaxDuration, time.Hour*24*30) } -func(p *Provider) RefreshTokenRotationGracePeriod() time.Duration { +func (p *Provider) RefreshTokenRotationGracePeriod() time.Duration { return p.p.DurationF(KeyRefreshTokenRotationGracePeriod, 0) } diff --git a/driver/registry_sql.go b/driver/registry_sql.go index f8ece1bcd9..7e1db28892 100644 --- a/driver/registry_sql.go +++ b/driver/registry_sql.go @@ -55,8 +55,8 @@ func NewRegistrySQL() *RegistrySQL { } // SetConfig used for testing only. -func(m *RegistrySQL) SetConfig(key string, value interface{}) { - m.C.Set(key,value) +func (m *RegistrySQL) SetConfig(key string, value interface{}) { + m.C.Set(key, value) } func (m *RegistrySQL) Init(ctx context.Context) error { diff --git a/oauth2/fosite_store_helpers.go b/oauth2/fosite_store_helpers.go index fd4da890c7..84ed697ba4 100644 --- a/oauth2/fosite_store_helpers.go +++ b/oauth2/fosite_store_helpers.go @@ -190,7 +190,6 @@ func TestHelperRunner(t *testing.T, store InternalRegistry, k string) { t.Run(fmt.Sprintf("case=testHelperRevokeRefreshTokenMaybeGracePeriod/db=%s", k), testHelperRevokeRefreshTokenMaybeGracePeriod(store)) } - func testHelperRequestIDMultiples(m InternalRegistry, _ string) func(t *testing.T) { return func(t *testing.T) { requestId := uuid.New() @@ -419,7 +418,7 @@ func testHelperRevokeAccessToken(x InternalRegistry) func(t *testing.T) { func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *testing.T) { - setConfig := func(registry InternalRegistry, key string, value interface{}){ + setConfig := func(registry InternalRegistry, key string, value interface{}) { r := reflect.ValueOf(registry) updateConfig := r.MethodByName("SetConfig") updateConfig.Call([]reflect.Value{ @@ -451,7 +450,6 @@ func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *te assert.True(t, errors.Is(err, fosite.ErrInactiveToken)) }) - t.Run("refresh token enters grace period when configured,", func(t *testing.T) { // SETUP @@ -468,7 +466,7 @@ func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *te assert.NoError(t, err, "precondition failed: could not create refresh token session") // ACT - err = m.RevokeRefreshTokenMaybeGracePeriod(ctx,defaultRequest.GetID(), refreshTokenSession) + err = m.RevokeRefreshTokenMaybeGracePeriod(ctx, defaultRequest.GetID(), refreshTokenSession) assert.NoError(t, err) diff --git a/persistence/sql/persister_oauth2.go b/persistence/sql/persister_oauth2.go index 58f446efcd..2c0c8dc4c7 100644 --- a/persistence/sql/persister_oauth2.go +++ b/persistence/sql/persister_oauth2.go @@ -425,13 +425,13 @@ func (p *Persister) RevokeRefreshTokenMaybeGracePeriod(ctx context.Context, requ } var inGracePeriod bool - if inGracePeriod,err = p.getRefreshTokenGracePeriodStatusBySignature(ctx, signature); err != nil { + if inGracePeriod, err = p.getRefreshTokenGracePeriodStatusBySignature(ctx, signature); err != nil { p.l.Errorf("signature: %s in_grace_period status not found. grace period not applied", signature) return errors.WithStack(err) } requesterSession := requester.GetSession() - if ! inGracePeriod { + if !inGracePeriod { requesterSession.SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(gracePeriod)) if err = p.updateRefreshSession(ctx, requestId, requesterSession, true); err != nil { p.l.Errorf("failed to update session with signature: %s", signature) From e992907a205ee89898ed9d500d212777a278159f Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Tue, 11 Jan 2022 17:53:33 +0100 Subject: [PATCH 25/29] chore: update fosite --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 62a2962cfb..2d29d5c007 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/oleiade/reflections v1.0.1 github.com/olekukonko/tablewriter v0.0.1 github.com/ory/analytics-go/v4 v4.0.2 - github.com/ory/fosite v0.40.3-0.20211013150831-5027277a8297 + github.com/ory/fosite v0.42.0 github.com/ory/go-acc v0.2.6 github.com/ory/graceful v0.1.1 github.com/ory/herodot v0.9.12 diff --git a/go.sum b/go.sum index 373a0099bb..4779326934 100644 --- a/go.sum +++ b/go.sum @@ -1339,8 +1339,8 @@ github.com/ory/dockertest/v3 v3.6.5/go.mod h1:iYKQSRlYrt/2s5fJWYdB98kCQG6g/LjBMv github.com/ory/dockertest/v3 v3.8.1 h1:vU/8d1We4qIad2YM0kOwRVtnyue7ExvacPiw1yDm17g= github.com/ory/dockertest/v3 v3.8.1/go.mod h1:wSRQ3wmkz+uSARYMk7kVJFDBGm8x5gSxIhI7NDc+BAQ= github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0= -github.com/ory/fosite v0.40.3-0.20211013150831-5027277a8297 h1:r8t/5GYtFx8dY+OuebrxbmCh+sL9B9KW1gc4xCy9hCE= -github.com/ory/fosite v0.40.3-0.20211013150831-5027277a8297/go.mod h1:IIRYBnuhyfgmYpSwk1h56+2CI7p+605KRCiJ7olUcl0= +github.com/ory/fosite v0.42.0 h1:ICAa2d7tR+kS/taYIyMzGKufGViC1bb/QAdOgLxFqlg= +github.com/ory/fosite v0.42.0/go.mod h1:qggrqm3ZWQF9i2f/d3RLH5mHHPtv44hsiltkVKLsCYo= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.6 h1:YfI+L9dxI7QCtWn2RbawqO0vXhiThdXu/RgizJBbaq0= github.com/ory/go-acc v0.2.6/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw= From d1db1351244346caf3b1544fa1cf966724d54cb6 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Thu, 20 Jan 2022 16:37:35 -0700 Subject: [PATCH 26/29] fix linting errors --- driver/config/provider.go | 2 +- driver/registry_sql.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/driver/config/provider.go b/driver/config/provider.go index 5e8ecf1cb4..d8f37c8267 100644 --- a/driver/config/provider.go +++ b/driver/config/provider.go @@ -71,7 +71,7 @@ const ( KeyOAuth2LegacyErrors = "oauth2.include_legacy_error_fields" KeyExcludeNotBeforeClaim = "oauth2.exclude_not_before_claim" KeyAllowedTopLevelClaims = "oauth2.allowed_top_level_claims" - KeyRefreshTokenRotationGracePeriod = "oauth2.refresh_token_rotation.grace_period" + KeyRefreshTokenRotationGracePeriod = "oauth2.refresh_token_rotation.grace_period" // #nosec G101 KeyOAuth2GrantJWTIDOptional = "oauth2.grant.jwt.jti_optional" KeyOAuth2GrantJWTIssuedDateOptional = "oauth2.grant.jwt.iat_optional" KeyOAuth2GrantJWTMaxDuration = "oauth2.grant.jwt.max_ttl" diff --git a/driver/registry_sql.go b/driver/registry_sql.go index 7e1db28892..3d6a500794 100644 --- a/driver/registry_sql.go +++ b/driver/registry_sql.go @@ -56,7 +56,9 @@ func NewRegistrySQL() *RegistrySQL { // SetConfig used for testing only. func (m *RegistrySQL) SetConfig(key string, value interface{}) { - m.C.Set(key, value) + if err := m.C.Set(key, value); err != nil { + m.l.WithError(err).Fatalf("Unable to set \"%s\" to \"%s\".", key, value) + } } func (m *RegistrySQL) Init(ctx context.Context) error { From 2c7b95f7432701c7112b7c4c79075c512692ba52 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:16:42 +0100 Subject: [PATCH 27/29] fix: add max lifetime --- driver/config/provider.go | 5 +++++ driver/config/provider_test.go | 7 +++++++ spec/config.json | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/driver/config/provider.go b/driver/config/provider.go index 991d80abfb..658f3525d7 100644 --- a/driver/config/provider.go +++ b/driver/config/provider.go @@ -483,5 +483,10 @@ func (p *Provider) GrantTypeJWTBearerMaxDuration() time.Duration { } func (p *Provider) RefreshTokenRotationGracePeriod() time.Duration { + var duration = p.p.DurationF(KeyRefreshTokenRotationGracePeriod, 0) + if duration > time.Hour { + return time.Hour + } + return p.p.DurationF(KeyRefreshTokenRotationGracePeriod, 0) } diff --git a/driver/config/provider_test.go b/driver/config/provider_test.go index d6b57d288b..2cc404c903 100644 --- a/driver/config/provider_test.go +++ b/driver/config/provider_test.go @@ -278,6 +278,13 @@ func TestViperProviderValidates(t *testing.T) { assert.Equal(t, "random_salt", c.SubjectIdentifierAlgorithmSalt()) assert.Equal(t, []string{"whatever"}, c.DefaultClientScope()) + // refresh + assert.Equal(t, time.Duration(0), c.RefreshTokenRotationGracePeriod()) + require.NoError(t, c.Set(KeyRefreshTokenRotationGracePeriod, "1s")) + assert.Equal(t, time.Second, c.RefreshTokenRotationGracePeriod()) + require.NoError(t, c.Set(KeyRefreshTokenRotationGracePeriod, "2h")) + assert.Equal(t, time.Hour, c.RefreshTokenRotationGracePeriod()) + // urls assert.Equal(t, urlx.ParseOrPanic("https://issuer/"), c.IssuerURL()) assert.Equal(t, urlx.ParseOrPanic("https://public/"), c.PublicURL()) diff --git a/spec/config.json b/spec/config.json index 59b979665b..ce4b55c887 100644 --- a/spec/config.json +++ b/spec/config.json @@ -946,7 +946,8 @@ "type": "object", "properties": { "grace_period": { - "description": "Configures how long a Refresh Token remains valid after it has been used.", + "title": "Refresh Token Rotation Grace Period", + "description": "Configures how long a Refresh Token remains valid after it has been used. The maximum value is one hour.", "default": "0h", "allOf": [ { From b1d37ca27d2121a857cfea55c8f42f4bc2aeaf8e Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:19:56 +0100 Subject: [PATCH 28/29] fix: move migration to latest --- driver/registry_sql.go | 7 ------- ...000000_add_refresh_token_in_grace_period_flag.down.sql} | 0 ...00000000_add_refresh_token_in_grace_period_flag.up.sql} | 0 3 files changed, 7 deletions(-) rename persistence/sql/migrations/{20211101093405_add_refresh_token_in_grace_period_flag.down.sql => 20220214121000000000_add_refresh_token_in_grace_period_flag.down.sql} (100%) rename persistence/sql/migrations/{20211101093405_add_refresh_token_in_grace_period_flag.up.sql => 20220214121000000000_add_refresh_token_in_grace_period_flag.up.sql} (100%) diff --git a/driver/registry_sql.go b/driver/registry_sql.go index 3d6a500794..96583ba500 100644 --- a/driver/registry_sql.go +++ b/driver/registry_sql.go @@ -54,13 +54,6 @@ func NewRegistrySQL() *RegistrySQL { return r } -// SetConfig used for testing only. -func (m *RegistrySQL) SetConfig(key string, value interface{}) { - if err := m.C.Set(key, value); err != nil { - m.l.WithError(err).Fatalf("Unable to set \"%s\" to \"%s\".", key, value) - } -} - func (m *RegistrySQL) Init(ctx context.Context) error { if m.persister == nil { var opts []instrumentedsql.Opt diff --git a/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.down.sql b/persistence/sql/migrations/20220214121000000000_add_refresh_token_in_grace_period_flag.down.sql similarity index 100% rename from persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.down.sql rename to persistence/sql/migrations/20220214121000000000_add_refresh_token_in_grace_period_flag.down.sql diff --git a/persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.up.sql b/persistence/sql/migrations/20220214121000000000_add_refresh_token_in_grace_period_flag.up.sql similarity index 100% rename from persistence/sql/migrations/20211101093405_add_refresh_token_in_grace_period_flag.up.sql rename to persistence/sql/migrations/20220214121000000000_add_refresh_token_in_grace_period_flag.up.sql From 42de645664163d15d85f52f0f6a7c9da10566376 Mon Sep 17 00:00:00 2001 From: Bill Robbins <23531640+bill-robbins-ss@users.noreply.github.com> Date: Wed, 16 Feb 2022 15:40:36 -0700 Subject: [PATCH 29/29] remove reflection --- oauth2/fosite_store_helpers.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/oauth2/fosite_store_helpers.go b/oauth2/fosite_store_helpers.go index 84ed697ba4..93234e7b06 100644 --- a/oauth2/fosite_store_helpers.go +++ b/oauth2/fosite_store_helpers.go @@ -25,7 +25,6 @@ import ( "crypto/sha256" "fmt" "net/url" - "reflect" "testing" "time" @@ -418,15 +417,6 @@ func testHelperRevokeAccessToken(x InternalRegistry) func(t *testing.T) { func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *testing.T) { - setConfig := func(registry InternalRegistry, key string, value interface{}) { - r := reflect.ValueOf(registry) - updateConfig := r.MethodByName("SetConfig") - updateConfig.Call([]reflect.Value{ - reflect.ValueOf(key), - reflect.ValueOf(value), - }) - } - return func(t *testing.T) { t.Run("Revokes refresh token when grace period not configured", func(t *testing.T) { // SETUP @@ -453,10 +443,10 @@ func testHelperRevokeRefreshTokenMaybeGracePeriod(x InternalRegistry) func(t *te t.Run("refresh token enters grace period when configured,", func(t *testing.T) { // SETUP - setConfig(x, "oauth2.refresh_token_rotation.grace_period", "1m") + x.Config().Set("oauth2.refresh_token_rotation.grace_period", "1m") // always reset back to the default - defer setConfig(x, "oauth2.refresh_token_rotation.grace_period", "0m") + defer x.Config().Set("oauth2.refresh_token_rotation.grace_period", "0m") ctx := context.Background() m := x.OAuth2Storage()