Files
upstream-mirror c60cc7ca75
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:19:12 +05:00

225 lines
7.0 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package password_test
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
"github.com/gofrs/uuid"
"github.com/phayes/freeport"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
dockertest "github.com/ory/dockertest/v4"
hydraclientgo "github.com/ory/hydra-client-go/v2"
"github.com/ory/kratos/pkg/testhelpers"
"github.com/ory/x/urlx"
)
type clientAppConfig struct {
client *oauth2.Config
expectToken bool
state *clientAppState
}
type clientAppState struct {
visits int64
tokens int64
}
type callTrace string
const (
RegistrationUI callTrace = "registration-ui"
RegistrationWithOAuth2LoginChallenge callTrace = "registration-with-oauth2-login-challenge"
RegistrationWithFlowID callTrace = "registration-with-flow-id"
LoginUI callTrace = "login-ui"
LoginWithOAuth2LoginChallenge callTrace = "login-with-oauth2-login-challenge"
LoginWithFlowID callTrace = "login-with-flow-id"
Consent callTrace = "consent"
ConsentWithChallenge callTrace = "consent-with-challenge"
ConsentAccept callTrace = "consent-accept"
ConsentSkip callTrace = "consent-skip"
ConsentClientSkip callTrace = "consent-client-skip"
CodeExchange callTrace = "code-exchange"
CodeExchangeWithToken callTrace = "code-exchange-with-token"
)
type testContextKey string
const (
TestUIConfig testContextKey = "test-ui-config"
TestOAuthClientState testContextKey = "test-oauth-client-state"
)
type testConfig struct {
identifier string
password string
browserClient *http.Client
kratosPublicTS *httptest.Server
clientAppTS *httptest.Server
hydraAdminClient hydraclientgo.OAuth2API
consentRemember bool
requestedScope []string
callTrace *[]callTrace
}
func createHydraOAuth2ApiClient(url string) hydraclientgo.OAuth2API {
configuration := hydraclientgo.NewConfiguration()
configuration.Host = urlx.ParseOrPanic(url).Host
configuration.Servers = hydraclientgo.ServerConfigurations{{URL: url}}
return hydraclientgo.NewAPIClient(configuration).OAuth2API
}
func createOAuth2Client(t *testing.T, ctx context.Context, hydraAdmin hydraclientgo.OAuth2API, redirectURIs []string, scope string, skipConsent bool) string {
t.Helper()
clientName := "kratos-hydra-integration-test-client-1"
tokenEndpointAuthMethod := "client_secret_post"
clientSecret := "client-secret"
c, r, err := hydraAdmin.CreateOAuth2Client(ctx).OAuth2Client(
hydraclientgo.OAuth2Client{
ClientName: &clientName,
RedirectUris: redirectURIs,
Scope: &scope,
TokenEndpointAuthMethod: &tokenEndpointAuthMethod,
ClientSecret: &clientSecret,
SkipConsent: &skipConsent,
},
).Execute()
require.NoError(t, err)
require.Equal(t, r.StatusCode, http.StatusCreated)
return *c.ClientId
}
func makeAuthCodeURL(t *testing.T, c *oauth2.Config, requestedClaims string, isForced bool) string {
t.Helper()
var options []oauth2.AuthCodeOption
if isForced {
options = append(options, oauth2.SetAuthURLParam("prompt", "login"))
}
if requestedClaims != "" {
options = append(options, oauth2.SetAuthURLParam("claims", requestedClaims))
}
state := fmt.Sprintf("%x", uuid.Must(uuid.NewV4()))
return c.AuthCodeURL(state, options...)
}
func newHydra(t *testing.T, loginUI string, consentUI string) (hydraAdmin string, hydraPublic string) {
pool := dockertest.NewPoolT(t, "")
// The public host port must be known before the container starts because it
// is part of URLS_SELF_ISSUER. freeport releases the port before Docker binds
// it, so another process can grab it in between — retry on that race.
var hydraResource dockertest.ClosableResource
ctx := t.Context()
require.EventuallyWithT(t, func(t *assert.CollectT) {
publicPort, err := freeport.GetFreePort()
require.NoError(t, err)
hydraResource, err = pool.Run(ctx, "oryd/hydra",
// Keep tag in sync with the version in ci.yaml
dockertest.WithTag("v2.2.0"),
dockertest.WithoutReuse(),
dockertest.WithEnv([]string{
"DSN=memory",
fmt.Sprintf("URLS_SELF_ISSUER=http://127.0.0.1:%d/", publicPort),
"URLS_LOGIN=" + loginUI,
"URLS_CONSENT=" + consentUI,
"LOG_LEAK_SENSITIVE_VALUES=true",
"SECRETS_SYSTEM=someverylongsecretthatis32byteslong",
}),
dockertest.WithCmd([]string{"serve", "all", "--dev"}),
dockertest.WithContainerConfig(func(cc *container.Config) {
cc.ExposedPorts = network.PortSet{
network.MustParsePort("4444/tcp"): struct{}{},
network.MustParsePort("4445/tcp"): struct{}{},
}
}),
dockertest.WithHostConfig(func(hc *container.HostConfig) {
hc.PortBindings = network.PortMap{
network.MustParsePort("4444/tcp"): {{HostPort: strconv.Itoa(publicPort)}},
network.MustParsePort("4445/tcp"): {{HostPort: ""}},
}
}),
)
require.NoError(t, err)
}, 2*time.Minute, 100*time.Millisecond)
hydraResource.Cleanup(t)
require.NotEmpty(t, hydraResource.GetPort("4444/tcp"), "%+v", hydraResource.Container().NetworkSettings.Ports)
require.NotEmpty(t, hydraResource.GetPort("4445/tcp"), "%+v", hydraResource.Container)
hydraPublic = "http://127.0.0.1:" + hydraResource.GetPort("4444/tcp")
hydraAdmin = "http://127.0.0.1:" + hydraResource.GetPort("4445/tcp")
go func() {
stdout, stderr, err := hydraResource.Logs(context.Background())
if err == nil {
if stdout != "" {
t.Logf("[hydra stdout]:\n%s\n", stdout)
}
if stderr != "" {
t.Logf("[hydra stderr]:\n%s\n", stderr)
}
}
}()
client := testhelpers.NewTestClient(t)
require.EventuallyWithT(t, func(t *assert.CollectT) {
res, err := client.Get(hydraPublic + "/health/ready")
require.NoError(t, err)
assert.Equal(t, 200, res.StatusCode)
res, err = client.Get(hydraAdmin + "/health/ready")
require.NoError(t, err)
assert.Equal(t, 200, res.StatusCode)
}, 30*time.Second, time.Second)
t.Logf("Ory Hydra running at: %s %s", hydraPublic, hydraAdmin)
return hydraAdmin, hydraPublic
}
type TestLogWriter struct {
streamName string
*testing.T
}
func (t TestLogWriter) Write(p []byte) (int, error) {
t.Logf("[%d bytes @ %s]:\n\n%s\n", len(p), t.streamName, string(p))
return len(p), nil
}
func doOAuthFlow(t *testing.T, ctx context.Context, oauthClient *oauth2.Config, browserClient *http.Client) {
t.Helper()
authCodeURL := makeAuthCodeURL(t, oauthClient, "", false)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, authCodeURL, nil)
require.NoError(t, err)
res, err := browserClient.Do(req)
require.NoError(t, err)
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.NoError(t, res.Body.Close())
require.Equal(t, "", string(body))
require.Equal(t, http.StatusOK, res.StatusCode)
}