Merge branch 'next' into next-main
This commit is contained in:
commit
d9487ef77d
118 changed files with 6091 additions and 981 deletions
|
@ -2,33 +2,64 @@ package client_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jeremija/gosubmit"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/exp/slog"
|
||||
|
||||
"github.com/zitadel/oidc/v2/example/server/exampleop"
|
||||
"github.com/zitadel/oidc/v2/example/server/storage"
|
||||
"github.com/zitadel/oidc/v2/pkg/client/rp"
|
||||
"github.com/zitadel/oidc/v2/pkg/client/rs"
|
||||
"github.com/zitadel/oidc/v2/pkg/client/tokenexchange"
|
||||
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v2/pkg/op"
|
||||
"github.com/zitadel/oidc/v3/example/server/exampleop"
|
||||
"github.com/zitadel/oidc/v3/example/server/storage"
|
||||
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
||||
"github.com/zitadel/oidc/v3/pkg/client/rs"
|
||||
"github.com/zitadel/oidc/v3/pkg/client/tokenexchange"
|
||||
httphelper "github.com/zitadel/oidc/v3/pkg/http"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
)
|
||||
|
||||
var Logger = slog.New(
|
||||
slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelDebug,
|
||||
}),
|
||||
)
|
||||
|
||||
var CTX context.Context
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT)
|
||||
defer cancel()
|
||||
CTX, cancel = context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
|
||||
func TestRelyingPartySession(t *testing.T) {
|
||||
for _, wrapServer := range []bool{false, true} {
|
||||
t.Run(fmt.Sprint("wrapServer ", wrapServer), func(t *testing.T) {
|
||||
testRelyingPartySession(t, wrapServer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testRelyingPartySession(t *testing.T, wrapServer bool) {
|
||||
t.Log("------- start example OP ------")
|
||||
targetURL := "http://local-site"
|
||||
exampleStorage := storage.NewStorage(storage.NewUserStore(targetURL))
|
||||
|
@ -36,17 +67,17 @@ func TestRelyingPartySession(t *testing.T) {
|
|||
opServer := httptest.NewServer(&dh)
|
||||
defer opServer.Close()
|
||||
t.Logf("auth server at %s", opServer.URL)
|
||||
dh.Handler = exampleop.SetupServer(opServer.URL, exampleStorage)
|
||||
dh.Handler = exampleop.SetupServer(opServer.URL, exampleStorage, Logger, wrapServer)
|
||||
|
||||
seed := rand.New(rand.NewSource(int64(os.Getpid()) + time.Now().UnixNano()))
|
||||
clientID := t.Name() + "-" + strconv.FormatInt(seed.Int63(), 25)
|
||||
|
||||
t.Log("------- run authorization code flow ------")
|
||||
provider, _, refreshToken, idToken := RunAuthorizationCodeFlow(t, opServer, clientID, "secret")
|
||||
provider, tokens := RunAuthorizationCodeFlow(t, opServer, clientID, "secret")
|
||||
|
||||
t.Log("------- refresh tokens ------")
|
||||
|
||||
newTokens, err := rp.RefreshAccessToken(provider, refreshToken, "", "")
|
||||
newTokens, err := rp.RefreshTokens[*oidc.IDTokenClaims](CTX, provider, tokens.RefreshToken, "", "")
|
||||
require.NoError(t, err, "refresh token")
|
||||
assert.NotNil(t, newTokens, "access token")
|
||||
t.Logf("new access token %s", newTokens.AccessToken)
|
||||
|
@ -54,11 +85,13 @@ func TestRelyingPartySession(t *testing.T) {
|
|||
t.Logf("new token type %s", newTokens.TokenType)
|
||||
t.Logf("new expiry %s", newTokens.Expiry.Format(time.RFC3339))
|
||||
require.NotEmpty(t, newTokens.AccessToken, "new accessToken")
|
||||
assert.NotEmpty(t, newTokens.Extra("id_token"), "new idToken")
|
||||
assert.NotEmpty(t, newTokens.IDToken, "new idToken")
|
||||
assert.NotNil(t, newTokens.IDTokenClaims)
|
||||
assert.Equal(t, newTokens.IDTokenClaims.Subject, tokens.IDTokenClaims.Subject)
|
||||
|
||||
t.Log("------ end session (logout) ------")
|
||||
|
||||
newLoc, err := rp.EndSession(provider, idToken, "", "")
|
||||
newLoc, err := rp.EndSession(CTX, provider, tokens.IDToken, "", "")
|
||||
require.NoError(t, err, "logout")
|
||||
if newLoc != nil {
|
||||
t.Logf("redirect to %s", newLoc)
|
||||
|
@ -67,17 +100,25 @@ func TestRelyingPartySession(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Log("------ attempt refresh again (should fail) ------")
|
||||
t.Log("trying original refresh token", refreshToken)
|
||||
_, err = rp.RefreshAccessToken(provider, refreshToken, "", "")
|
||||
t.Log("trying original refresh token", tokens.RefreshToken)
|
||||
_, err = rp.RefreshTokens[*oidc.IDTokenClaims](CTX, provider, tokens.RefreshToken, "", "")
|
||||
assert.Errorf(t, err, "refresh with original")
|
||||
if newTokens.RefreshToken != "" {
|
||||
t.Log("trying replacement refresh token", newTokens.RefreshToken)
|
||||
_, err = rp.RefreshAccessToken(provider, newTokens.RefreshToken, "", "")
|
||||
_, err = rp.RefreshTokens[*oidc.IDTokenClaims](CTX, provider, newTokens.RefreshToken, "", "")
|
||||
assert.Errorf(t, err, "refresh with replacement")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceServerTokenExchange(t *testing.T) {
|
||||
for _, wrapServer := range []bool{false, true} {
|
||||
t.Run(fmt.Sprint("wrapServer ", wrapServer), func(t *testing.T) {
|
||||
testResourceServerTokenExchange(t, wrapServer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testResourceServerTokenExchange(t *testing.T, wrapServer bool) {
|
||||
t.Log("------- start example OP ------")
|
||||
targetURL := "http://local-site"
|
||||
exampleStorage := storage.NewStorage(storage.NewUserStore(targetURL))
|
||||
|
@ -85,23 +126,24 @@ func TestResourceServerTokenExchange(t *testing.T) {
|
|||
opServer := httptest.NewServer(&dh)
|
||||
defer opServer.Close()
|
||||
t.Logf("auth server at %s", opServer.URL)
|
||||
dh.Handler = exampleop.SetupServer(opServer.URL, exampleStorage)
|
||||
dh.Handler = exampleop.SetupServer(opServer.URL, exampleStorage, Logger, wrapServer)
|
||||
|
||||
seed := rand.New(rand.NewSource(int64(os.Getpid()) + time.Now().UnixNano()))
|
||||
clientID := t.Name() + "-" + strconv.FormatInt(seed.Int63(), 25)
|
||||
clientSecret := "secret"
|
||||
|
||||
t.Log("------- run authorization code flow ------")
|
||||
provider, _, refreshToken, idToken := RunAuthorizationCodeFlow(t, opServer, clientID, clientSecret)
|
||||
provider, tokens := RunAuthorizationCodeFlow(t, opServer, clientID, clientSecret)
|
||||
|
||||
resourceServer, err := rs.NewResourceServerClientCredentials(opServer.URL, clientID, clientSecret)
|
||||
resourceServer, err := rs.NewResourceServerClientCredentials(CTX, opServer.URL, clientID, clientSecret)
|
||||
require.NoError(t, err, "new resource server")
|
||||
|
||||
t.Log("------- exchage refresh tokens (impersonation) ------")
|
||||
|
||||
tokenExchangeResponse, err := tokenexchange.ExchangeToken(
|
||||
CTX,
|
||||
resourceServer,
|
||||
refreshToken,
|
||||
tokens.RefreshToken,
|
||||
oidc.RefreshTokenType,
|
||||
"",
|
||||
"",
|
||||
|
@ -119,7 +161,7 @@ func TestResourceServerTokenExchange(t *testing.T) {
|
|||
|
||||
t.Log("------ end session (logout) ------")
|
||||
|
||||
newLoc, err := rp.EndSession(provider, idToken, "", "")
|
||||
newLoc, err := rp.EndSession(CTX, provider, tokens.IDToken, "", "")
|
||||
require.NoError(t, err, "logout")
|
||||
if newLoc != nil {
|
||||
t.Logf("redirect to %s", newLoc)
|
||||
|
@ -130,8 +172,9 @@ func TestResourceServerTokenExchange(t *testing.T) {
|
|||
t.Log("------- attempt exchage again (should fail) ------")
|
||||
|
||||
tokenExchangeResponse, err = tokenexchange.ExchangeToken(
|
||||
CTX,
|
||||
resourceServer,
|
||||
refreshToken,
|
||||
tokens.RefreshToken,
|
||||
oidc.RefreshTokenType,
|
||||
"",
|
||||
"",
|
||||
|
@ -145,7 +188,7 @@ func TestResourceServerTokenExchange(t *testing.T) {
|
|||
require.Nil(t, tokenExchangeResponse, "token exchange response")
|
||||
}
|
||||
|
||||
func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID, clientSecret string) (provider rp.RelyingParty, accessToken, refreshToken, idToken string) {
|
||||
func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID, clientSecret string) (provider rp.RelyingParty, tokens *oidc.Tokens[*oidc.IDTokenClaims]) {
|
||||
targetURL := "http://local-site"
|
||||
localURL, err := url.Parse(targetURL + "/login?requestID=1234")
|
||||
require.NoError(t, err, "local url")
|
||||
|
@ -167,6 +210,7 @@ func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID,
|
|||
key := []byte("test1234test1234")
|
||||
cookieHandler := httphelper.NewCookieHandler(key, key, httphelper.WithUnsecure())
|
||||
provider, err = rp.NewRelyingPartyOIDC(
|
||||
CTX,
|
||||
opServer.URL,
|
||||
clientID,
|
||||
clientSecret,
|
||||
|
@ -241,7 +285,8 @@ func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID,
|
|||
}
|
||||
|
||||
var email string
|
||||
redirect := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[*oidc.IDTokenClaims], state string, rp rp.RelyingParty, info *oidc.UserInfo) {
|
||||
redirect := func(w http.ResponseWriter, r *http.Request, newTokens *oidc.Tokens[*oidc.IDTokenClaims], state string, rp rp.RelyingParty, info *oidc.UserInfo) {
|
||||
tokens = newTokens
|
||||
require.NotNil(t, tokens, "tokens")
|
||||
require.NotNil(t, info, "info")
|
||||
t.Log("access token", tokens.AccessToken)
|
||||
|
@ -249,9 +294,6 @@ func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID,
|
|||
t.Log("id token", tokens.IDToken)
|
||||
t.Log("email", info.Email)
|
||||
|
||||
accessToken = tokens.AccessToken
|
||||
refreshToken = tokens.RefreshToken
|
||||
idToken = tokens.IDToken
|
||||
email = info.Email
|
||||
http.Redirect(w, r, targetURL, 302)
|
||||
}
|
||||
|
@ -273,12 +315,12 @@ func RunAuthorizationCodeFlow(t *testing.T, opServer *httptest.Server, clientID,
|
|||
require.NoError(t, err, "get fully-authorizied redirect location")
|
||||
require.Equal(t, targetURL, authorizedURL.String(), "fully-authorizied redirect location")
|
||||
|
||||
require.NotEmpty(t, idToken, "id token")
|
||||
assert.NotEmpty(t, refreshToken, "refresh token")
|
||||
assert.NotEmpty(t, accessToken, "access token")
|
||||
require.NotEmpty(t, tokens.IDToken, "id token")
|
||||
assert.NotEmpty(t, tokens.RefreshToken, "refresh token")
|
||||
assert.NotEmpty(t, tokens.AccessToken, "access token")
|
||||
assert.NotEmpty(t, email, "email")
|
||||
|
||||
return provider, accessToken, refreshToken, idToken
|
||||
return provider, tokens
|
||||
}
|
||||
|
||||
func TestErrorFromPromptNone(t *testing.T) {
|
||||
|
@ -299,7 +341,7 @@ func TestErrorFromPromptNone(t *testing.T) {
|
|||
opServer := httptest.NewServer(&dh)
|
||||
defer opServer.Close()
|
||||
t.Logf("auth server at %s", opServer.URL)
|
||||
dh.Handler = exampleop.SetupServer(opServer.URL, exampleStorage, op.WithHttpInterceptors(
|
||||
dh.Handler = exampleop.SetupServer(opServer.URL, exampleStorage, Logger, false, op.WithHttpInterceptors(
|
||||
func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Logf("request to %s", r.URL)
|
||||
|
@ -317,6 +359,7 @@ func TestErrorFromPromptNone(t *testing.T) {
|
|||
key := []byte("test1234test1234")
|
||||
cookieHandler := httphelper.NewCookieHandler(key, key, httphelper.WithUnsecure())
|
||||
provider, err := rp.NewRelyingPartyOIDC(
|
||||
CTX,
|
||||
opServer.URL,
|
||||
clientID,
|
||||
clientSecret,
|
||||
|
@ -412,7 +455,7 @@ func getForm(t *testing.T, desc string, httpClient *http.Client, uri *url.URL) [
|
|||
|
||||
func fillForm(t *testing.T, desc string, httpClient *http.Client, body []byte, uri *url.URL, opts ...gosubmit.Option) *url.URL {
|
||||
// TODO: switch to io.NopCloser when go1.15 support is dropped
|
||||
req := gosubmit.ParseWithURL(ioutil.NopCloser(bytes.NewReader(body)), uri.String()).FirstForm().Testing(t).NewTestRequest(
|
||||
req := gosubmit.ParseWithURL(io.NopCloser(bytes.NewReader(body)), uri.String()).FirstForm().Testing(t).NewTestRequest(
|
||||
append([]gosubmit.Option{gosubmit.AutoFill()}, opts...)...,
|
||||
)
|
||||
if req.URL.Scheme == "" {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue