-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: stateless authorization code flow (ory#3515)
This patch optimizes the performance of authorization code grant flows by minimizing the number of database queries. We acheive this by storing the flow in an AEAD-encoded cookie and AEAD-encoded request parameters for the authentication and consent screens. BREAKING CHANGE: * The client that is used as part of the authorization grant flow is stored in the AEAD-encoding. Therefore, running flows will not observe updates to the client after they were started. * Because the login and consent challenge values now include the AEAD-encoded flow, their size increased to around 1kB for a flow without any metadata (and increases linearly with the amount of metadata). Please adjust your ingress / gateway accordingly.
- Loading branch information
Showing
115 changed files
with
3,697 additions
and
1,546 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ | |
docs | ||
node_modules | ||
.circleci | ||
.docker-home | ||
.github | ||
scripts | ||
sdk/js | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,9 +23,9 @@ jobs: | |
# We must fetch at least the immediate parents so that if this is | ||
# a pull request then we can checkout the head. | ||
fetch-depth: 2 | ||
- uses: actions/setup-go@v2 | ||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: "1.19" | ||
go-version: "1.20" | ||
- name: Start service | ||
run: ./test/conformance/start.sh | ||
- name: Run tests | ||
|
@@ -80,22 +80,21 @@ jobs: | |
path: | | ||
internal/httpclient | ||
key: ${{ needs.sdk-generate.outputs.sdk-cache-key }} | ||
- uses: actions/setup-go@v2 | ||
- uses: actions/setup-go@v4 | ||
with: | ||
go-version: "1.19" | ||
go-version: "1.20" | ||
- run: go list -json > go.list | ||
- name: Run nancy | ||
uses: sonatype-nexus-community/[email protected] | ||
with: | ||
nancyVersion: v1.0.42 | ||
- name: Run golangci-lint | ||
uses: golangci/golangci-lint-action@v2 | ||
uses: golangci/golangci-lint-action@v3 | ||
env: | ||
GOGC: 100 | ||
with: | ||
args: --timeout 10m0s | ||
version: v1.47.3 | ||
skip-go-installation: true | ||
version: v1.53.2 | ||
skip-pkg-cache: true | ||
- name: Run go-acc (tests) | ||
run: | | ||
|
@@ -124,9 +123,9 @@ jobs: | |
path: | | ||
internal/httpclient | ||
key: ${{ needs.sdk-generate.outputs.sdk-cache-key }} | ||
- uses: actions/setup-go@v2 | ||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: "1.19" | ||
go-version: "1.20" | ||
- name: Setup HSM libs and packages | ||
run: | | ||
sudo apt install -y softhsm opensc | ||
|
@@ -175,9 +174,9 @@ jobs: | |
docker start cockroach | ||
name: Start CockroachDB | ||
- uses: ory/ci/checkout@master | ||
- uses: actions/setup-go@v2 | ||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: "1.19" | ||
go-version: "1.20" | ||
- uses: actions/cache@v2 | ||
with: | ||
path: ./test/e2e/hydra | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
ignore: | ||
- vulnerability: CVE-2023-2650 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CVE-2023-2650 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright © 2023 Ory Corp | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package aead | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ory/fosite" | ||
) | ||
|
||
// Cipher provides AEAD (authenticated encryption with associated data). The | ||
// ciphertext is returned base64url-encoded. | ||
type Cipher interface { | ||
// Encrypt encrypts and encodes the given plaintext, optionally using | ||
// additiona data. | ||
Encrypt(ctx context.Context, plaintext, additionalData []byte) (ciphertext string, err error) | ||
|
||
// Decrypt decodes, decrypts, and verifies the plaintext and additional data | ||
// from the ciphertext. The ciphertext must be given in the form as returned | ||
// by Encrypt. | ||
Decrypt(ctx context.Context, ciphertext string, additionalData []byte) (plaintext []byte, err error) | ||
} | ||
|
||
type Dependencies interface { | ||
fosite.GlobalSecretProvider | ||
fosite.RotatedGlobalSecretsProvider | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// Copyright © 2022 Ory Corp | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package aead_test | ||
|
||
import ( | ||
"context" | ||
"crypto/rand" | ||
"fmt" | ||
"io" | ||
"testing" | ||
|
||
"github.com/ory/hydra/v2/aead" | ||
"github.com/ory/hydra/v2/driver/config" | ||
"github.com/ory/hydra/v2/internal" | ||
|
||
"github.com/pborman/uuid" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func secret(t *testing.T) string { | ||
bytes := make([]byte, 32) | ||
_, err := io.ReadFull(rand.Reader, bytes) | ||
require.NoError(t, err) | ||
return fmt.Sprintf("%X", bytes) | ||
} | ||
|
||
func TestAEAD(t *testing.T) { | ||
t.Parallel() | ||
for _, tc := range []struct { | ||
name string | ||
new func(aead.Dependencies) aead.Cipher | ||
}{ | ||
{"AES-GCM", func(d aead.Dependencies) aead.Cipher { return aead.NewAESGCM(d) }}, | ||
{"XChaChaPoly", func(d aead.Dependencies) aead.Cipher { return aead.NewXChaCha20Poly1305(d) }}, | ||
} { | ||
tc := tc | ||
|
||
t.Run("cipher="+tc.name, func(t *testing.T) { | ||
NewCipher := tc.new | ||
|
||
t.Run("case=without-rotation", func(t *testing.T) { | ||
t.Parallel() | ||
ctx := context.Background() | ||
c := internal.NewConfigurationWithDefaults() | ||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{secret(t)}) | ||
a := NewCipher(c) | ||
|
||
plain := []byte(uuid.New()) | ||
ct, err := a.Encrypt(ctx, plain, nil) | ||
assert.NoError(t, err) | ||
|
||
ct2, err := a.Encrypt(ctx, plain, nil) | ||
assert.NoError(t, err) | ||
assert.NotEqual(t, ct, ct2, "ciphertexts for the same plaintext must be different each time") | ||
|
||
res, err := a.Decrypt(ctx, ct, nil) | ||
assert.NoError(t, err) | ||
assert.Equal(t, plain, res) | ||
}) | ||
|
||
t.Run("case=wrong-secret", func(t *testing.T) { | ||
t.Parallel() | ||
ctx := context.Background() | ||
c := internal.NewConfigurationWithDefaults() | ||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{secret(t)}) | ||
a := NewCipher(c) | ||
|
||
ct, err := a.Encrypt(ctx, []byte(uuid.New()), nil) | ||
require.NoError(t, err) | ||
|
||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{secret(t)}) | ||
_, err = a.Decrypt(ctx, ct, nil) | ||
require.Error(t, err) | ||
}) | ||
|
||
t.Run("case=with-rotation", func(t *testing.T) { | ||
t.Parallel() | ||
ctx := context.Background() | ||
c := internal.NewConfigurationWithDefaults() | ||
old := secret(t) | ||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{old}) | ||
a := NewCipher(c) | ||
|
||
plain := []byte(uuid.New()) | ||
ct, err := a.Encrypt(ctx, plain, nil) | ||
require.NoError(t, err) | ||
|
||
// Sets the old secret as a rotated secret and creates a new one. | ||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{secret(t), old}) | ||
res, err := a.Decrypt(ctx, ct, nil) | ||
require.NoError(t, err) | ||
assert.Equal(t, plain, res) | ||
|
||
// THis should also work when we re-encrypt the same plain text. | ||
ct2, err := a.Encrypt(ctx, plain, nil) | ||
require.NoError(t, err) | ||
assert.NotEqual(t, ct2, ct) | ||
|
||
res, err = a.Decrypt(ctx, ct, nil) | ||
require.NoError(t, err) | ||
assert.Equal(t, plain, res) | ||
}) | ||
|
||
t.Run("case=with-rotation-wrong-secret", func(t *testing.T) { | ||
t.Parallel() | ||
ctx := context.Background() | ||
c := internal.NewConfigurationWithDefaults() | ||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{secret(t)}) | ||
a := NewCipher(c) | ||
|
||
plain := []byte(uuid.New()) | ||
ct, err := a.Encrypt(ctx, plain, nil) | ||
require.NoError(t, err) | ||
|
||
// When the secrets do not match, an error should be thrown during decryption. | ||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{secret(t), secret(t)}) | ||
_, err = a.Decrypt(ctx, ct, nil) | ||
require.Error(t, err) | ||
}) | ||
|
||
t.Run("suite=with additional data", func(t *testing.T) { | ||
t.Parallel() | ||
ctx := context.Background() | ||
c := internal.NewConfigurationWithDefaults() | ||
c.MustSet(ctx, config.KeyGetSystemSecret, []string{secret(t)}) | ||
a := NewCipher(c) | ||
|
||
plain := []byte(uuid.New()) | ||
ct, err := a.Encrypt(ctx, plain, []byte("additional data")) | ||
assert.NoError(t, err) | ||
|
||
t.Run("case=additional data matches", func(t *testing.T) { | ||
res, err := a.Decrypt(ctx, ct, []byte("additional data")) | ||
assert.NoError(t, err) | ||
assert.Equal(t, plain, res) | ||
}) | ||
|
||
t.Run("case=additional data does not match", func(t *testing.T) { | ||
res, err := a.Decrypt(ctx, ct, []byte("wrong data")) | ||
assert.Error(t, err) | ||
assert.Nil(t, res) | ||
}) | ||
|
||
t.Run("case=missing additional data", func(t *testing.T) { | ||
res, err := a.Decrypt(ctx, ct, nil) | ||
assert.Error(t, err) | ||
assert.Nil(t, res) | ||
}) | ||
}) | ||
}) | ||
} | ||
} |
Oops, something went wrong.