Files
zitadel/internal/api/oidc/auth_request_test.go
df75be96ff Merge commit from fork
* fix: Improve host header handling and deployment mode detection

* ZITADEL_CLOUD env

* refactor service config

* service config

* cleanup

* cleanup

* util for original and public host

* instanceHost, publicHost

* cleanup constructUrl

* cleanup middleware

* add x-zitadel-forward-host as fallback for current cloud setup

* fix build

* fix: lint

* fix: update test mocks

* fix: provide basepath to unit tests

* host handling

* unit tests

* imports

* fix: getPublicHost instead of getInstanceHost

* unit tests

* update nextjs, implement jwt verify for logout_token

* pass logout information in jwt to login v2

* handle hosts in jwks url

* point idp callbacks to the api and not the login UI

* logout_hint out of logout token, typecheck

* rm unused imports

* fix oidc integration test

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-12-08 10:01:49 +01:00

141 lines
3.8 KiB
Go

package oidc
import (
"crypto/rand"
"crypto/rsa"
"encoding/json"
"net/url"
"testing"
"github.com/go-jose/go-jose/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/text/language"
)
func TestBuildLoginV2LogoutURL(t *testing.T) {
t.Parallel()
tt := []struct {
testName string
logoutURIStr string
redirectURI string
logoutHint string
uiLocales []language.Tag
signer jose.Signer
expectedParams map[string]string
}{
{
testName: "basic with only redirectURI",
logoutURIStr: "https://example.com/logout",
redirectURI: "https://client/cb",
expectedParams: map[string]string{
"post_logout_redirect": "https://client/cb",
"logout_token": "", // presence checked separately
},
},
{
testName: "with logout hint",
logoutURIStr: "https://example.com/logout",
redirectURI: "https://client/cb",
logoutHint: "user@example.com",
expectedParams: map[string]string{
"post_logout_redirect": "https://client/cb",
"logout_hint": "user@example.com",
"logout_token": "", // presence checked separately
},
},
{
testName: "with ui_locales",
logoutURIStr: "https://example.com/logout",
redirectURI: "https://client/cb",
uiLocales: []language.Tag{language.English, language.Italian},
expectedParams: map[string]string{
"post_logout_redirect": "https://client/cb",
"ui_locales": "en it",
"logout_token": "", // presence checked separately
},
},
{
testName: "with all params",
logoutURIStr: "https://example.com/logout",
redirectURI: "https://client/cb",
logoutHint: "logoutme",
uiLocales: []language.Tag{language.Make("de-CH"), language.Make("fr")},
expectedParams: map[string]string{
"post_logout_redirect": "https://client/cb",
"logout_hint": "logoutme",
"ui_locales": "de-CH fr",
"logout_token": "", // presence checked separately
},
},
{
testName: "base with trailing slash",
logoutURIStr: "https://example.com/logout/",
redirectURI: "https://client/cb",
expectedParams: map[string]string{
"post_logout_redirect": "https://client/cb",
"logout_token": "", // presence checked separately
},
},
}
for _, tc := range tt {
t.Run(tc.testName, func(t *testing.T) {
// t.Parallel()
// Given
logoutURI, err := url.Parse(tc.logoutURIStr)
require.NoError(t, err)
// When
got, err := buildLoginV2LogoutURL(logoutURI, tc.redirectURI, tc.logoutHint, tc.uiLocales, signer)
// Then
require.NoError(t, err)
gotURL, err := url.Parse(got)
require.NoError(t, err)
require.NotContains(t, gotURL.String(), "/logout/")
q := gotURL.Query()
// Ensure no unexpected params
require.Len(t, q, len(tc.expectedParams))
for k, v := range tc.expectedParams {
if k == LoginLogoutTokenParam {
assertLogoutToken(t, q.Get(k), &logoutTokenPayload{
PostLogoutRedirectURI: tc.redirectURI,
LogoutHint: tc.logoutHint,
UILocales: tc.uiLocales,
})
continue
}
assert.Equal(t, v, q.Get(k))
}
})
}
}
func assertLogoutToken(t *testing.T, token string, payload *logoutTokenPayload) {
signature, err := jose.ParseSigned(token, []jose.SignatureAlgorithm{jose.RS256})
require.NoError(t, err)
logoutToken := new(logoutTokenPayload)
err = json.Unmarshal(signature.UnsafePayloadWithoutVerification(), logoutToken)
require.NoError(t, err)
assert.Equal(t, payload, logoutToken)
}
var (
privKey, _ = rsa.GenerateKey(rand.Reader, 2048)
signer = func() jose.Signer {
signer, _ := jose.NewSigner(
jose.SigningKey{
Algorithm: jose.RS256,
Key: privKey,
},
(&jose.SignerOptions{}).WithType("JWT"),
)
return signer
}()
)