Files
upstream-ory-kratos/selfservice/strategy/oidc/provider_test.go
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

177 lines
4.7 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package oidc
import (
"context"
"encoding/json"
"fmt"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
)
func TestClaimsValidate(t *testing.T) {
require.Error(t, new(Claims).Validate())
require.Error(t, (&Claims{Issuer: "not-empty"}).Validate())
require.Error(t, (&Claims{Issuer: "not-empty"}).Validate())
require.Error(t, (&Claims{Subject: "not-empty"}).Validate())
require.Error(t, (&Claims{Subject: "not-empty"}).Validate())
require.NoError(t, (&Claims{Issuer: "not-empty", Subject: "not-empty"}).Validate())
}
type TestProvider struct {
*ProviderGenericOIDC
}
func NewTestProvider(c *Configuration, reg Dependencies) Provider {
return &TestProvider{
ProviderGenericOIDC: NewProviderGenericOIDC(c, reg).(*ProviderGenericOIDC),
}
}
func RegisterTestProvider(t *testing.T, id string) {
supportedProviders[id] = func(c *Configuration, reg Dependencies) Provider {
return NewTestProvider(c, reg)
}
t.Cleanup(func() {
delete(supportedProviders, id)
})
}
var _ IDTokenVerifier = new(TestProvider)
func (t *TestProvider) Verify(_ context.Context, token string) (*Claims, error) {
if token == "error" {
return nil, fmt.Errorf("stub error")
}
c := Claims{}
if err := json.Unmarshal([]byte(token), &c); err != nil {
return nil, err
}
return &c, nil
}
// SetUserInfoURL allows overriding the userinfo URL for testing purposes.
func (p *ProviderUAEPass) SetUserInfoURL(t *testing.T, url string) {
t.Helper()
p.userinfoURL = url
}
// upstreamParam reports whether the option list produced by
// UpstreamParameters contains a given upstream parameter, and its value.
// The options are applied through a real oauth2.Config.AuthCodeURL so the
// test observes exactly what is sent to the upstream provider.
func upstreamParam(t *testing.T, opts []oauth2.AuthCodeOption, key string) (string, bool) {
t.Helper()
conf := &oauth2.Config{
ClientID: "client",
Endpoint: oauth2.Endpoint{AuthURL: "https://idp.example.com/auth"},
}
authURL, err := url.Parse(conf.AuthCodeURL("state", opts...))
require.NoError(t, err)
q := authURL.Query()
if !q.Has(key) {
return "", false
}
return q.Get(key), true
}
func TestUpstreamParameters(t *testing.T) {
t.Parallel()
upstream := map[string]string{
"login_hint": "victim@example.com",
"hd": "example.com",
"prompt": "select_account",
"auth_type": "reauthenticate",
"acr_values": "urn:example:acr",
"unknown": "dropped",
}
opts := UpstreamParameters(upstream)
t.Run("case=login_hint is never forwarded", func(t *testing.T) {
// A caller-supplied login_hint cannot be trusted and can steer
// account/tenant/client selection on some IdPs, enabling a client-swap
// attack (HackerOne #3239672, ory-corp/cloud#8955).
_, ok := upstreamParam(t, opts, "login_hint")
assert.False(t, ok, "login_hint must not be forwarded")
})
t.Run("case=other allowed parameters are forwarded", func(t *testing.T) {
for key, want := range map[string]string{
"hd": "example.com",
"prompt": "select_account",
"auth_type": "reauthenticate",
"acr_values": "urn:example:acr",
} {
got, ok := upstreamParam(t, opts, key)
assert.True(t, ok, "%s must be forwarded", key)
assert.Equal(t, want, got)
}
})
t.Run("case=unknown parameters are dropped", func(t *testing.T) {
_, ok := upstreamParam(t, opts, "unknown")
assert.False(t, ok, "unknown parameters must not be forwarded")
})
}
func TestLocale(t *testing.T) {
// test json unmarshal
for _, tc := range []struct {
name string
json string
expected string
assertErr assert.ErrorAssertionFunc
}{{
name: "empty",
json: `{}`,
expected: "",
}, {
name: "empty string locale",
json: `{"locale":""}`,
expected: "",
}, {
name: "invalid string locale",
json: `{"locale":"""}`,
assertErr: assert.Error,
}, {
name: "string locale",
json: `{"locale":"en-US"}`,
expected: "en-US",
}, {
name: "linkedin locale",
json: `{"locale":{"country":"US","language":"en","ignore":"me"}}`,
expected: "en-US",
}, {
name: "missing country linkedin locale",
json: `{"locale":{"language":"en"}}`,
expected: "en",
}, {
name: "missing language linkedin locale",
json: `{"locale":{"country":"US"}}`,
expected: "US",
}, {
name: "invalid linkedin locale",
json: `{"locale":{"invalid":"me"}}`,
expected: "",
}} {
t.Run(tc.name, func(t *testing.T) {
var c Claims
err := json.Unmarshal([]byte(tc.json), &c)
if tc.assertErr != nil {
tc.assertErr(t, err)
return
}
require.NoError(t, err)
assert.EqualValues(t, tc.expected, c.Locale)
})
}
}