Files
zitadel/internal/idp/providers/jwt/jwt_test.go
Livio Spring 64a03fba28 fix(api): return typed saml form post data in idp intent (#10136)
<!--
Please inform yourself about the contribution guidelines on submitting a
PR here:
https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr.
Take note of how PR/commit titles should be written and replace the
template texts in the sections below. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->

# Which Problems Are Solved

The current user V2 API returns a `[]byte` containing a whole HTML
document including the form on `StartIdentifyProviderIntent` for intents
based on form post (e.g. SAML POST bindings). This is not usable for
most clients as they cannot handle that and render a whole page inside
their app.
For redirect based intents, the url to which the client needs to
redirect is returned.

# How the Problems Are Solved

- Changed the returned type to a new `FormData` message containing the
url and a `fields` map.
- internal changes:
- Session.GetAuth now returns an `Auth` interfacce and error instead of
(content string, redirect bool)
- Auth interface has two implementations: `RedirectAuth` and `FormAuth`
- All use of the GetAuth function now type switch on the returned auth
object
- A template has been added to the login UI to execute the form post
automatically (as is).

# Additional Changes

- Some intent integration test did not check the redirect url and were
wrongly configured.

# Additional Context

- relates to zitadel/typescript#410
2025-06-30 15:07:33 +00:00

227 lines
5.4 KiB
Go

package jwt
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/idp"
)
func TestProvider_BeginAuth(t *testing.T) {
type fields struct {
name string
issuer string
jwtEndpoint string
keysEndpoint string
headerName string
encryptionAlg func(t *testing.T) crypto.EncryptionAlgorithm
}
type args struct {
state string
params []idp.Parameter
}
type want struct {
session idp.Session
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
want want
}{
{
name: "missing state, error",
fields: fields{
issuer: "https://jwt.com",
jwtEndpoint: "https://auth.com/jwt",
keysEndpoint: "https://jwt.com/keys",
headerName: "jwt-header",
encryptionAlg: func(t *testing.T) crypto.EncryptionAlgorithm {
return crypto.CreateMockEncryptionAlg(gomock.NewController(t))
},
},
args: args{
state: "",
params: nil,
},
want: want{
err: func(err error) bool {
return errors.Is(err, ErrMissingState)
},
},
},
{
name: "missing userAgentID, fallback to state",
fields: fields{
issuer: "https://jwt.com",
jwtEndpoint: "https://auth.com/jwt",
keysEndpoint: "https://jwt.com/keys",
headerName: "jwt-header",
encryptionAlg: func(t *testing.T) crypto.EncryptionAlgorithm {
return crypto.CreateMockEncryptionAlg(gomock.NewController(t))
},
},
args: args{
state: "testState",
params: nil,
},
want: want{
session: &Session{AuthURL: "https://auth.com/jwt?authRequestID=testState&userAgentID=dGVzdFN0YXRl"},
},
},
{
name: "successful auth",
fields: fields{
issuer: "https://jwt.com",
jwtEndpoint: "https://auth.com/jwt",
keysEndpoint: "https://jwt.com/keys",
headerName: "jwt-header",
encryptionAlg: func(t *testing.T) crypto.EncryptionAlgorithm {
return crypto.CreateMockEncryptionAlg(gomock.NewController(t))
},
},
args: args{
state: "testState",
params: []idp.Parameter{
idp.UserAgentID("agent"),
},
},
want: want{
session: &Session{AuthURL: "https://auth.com/jwt?authRequestID=testState&userAgentID=YWdlbnQ"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := assert.New(t)
provider, err := New(
tt.fields.name,
tt.fields.issuer,
tt.fields.jwtEndpoint,
tt.fields.keysEndpoint,
tt.fields.headerName,
tt.fields.encryptionAlg(t),
)
require.NoError(t, err)
ctx := context.Background()
session, err := provider.BeginAuth(ctx, tt.args.state, tt.args.params...)
if tt.want.err != nil && !tt.want.err(err) {
a.Fail("invalid error", err)
}
if tt.want.err == nil {
a.NoError(err)
wantAuth, wantErr := tt.want.session.GetAuth(ctx)
gotAuth, gotErr := session.GetAuth(ctx)
a.Equal(wantAuth, gotAuth)
a.ErrorIs(gotErr, wantErr)
}
})
}
}
func TestProvider_Options(t *testing.T) {
type fields struct {
name string
issuer string
jwtEndpoint string
keysEndpoint string
headerName string
encryptionAlg func(t *testing.T) crypto.EncryptionAlgorithm
opts []ProviderOpts
}
type want struct {
name string
linkingAllowed bool
creationAllowed bool
autoCreation bool
autoUpdate bool
pkce bool
}
tests := []struct {
name string
fields fields
want want
}{
{
name: "default",
fields: fields{
name: "jwt",
issuer: "https://jwt.com",
jwtEndpoint: "https://auth.com/jwt",
keysEndpoint: "https://jwt.com/keys",
headerName: "jwt-header",
encryptionAlg: func(t *testing.T) crypto.EncryptionAlgorithm {
return crypto.CreateMockEncryptionAlg(gomock.NewController(t))
},
opts: nil,
},
want: want{
name: "jwt",
linkingAllowed: false,
creationAllowed: false,
autoCreation: false,
autoUpdate: false,
pkce: false,
},
},
{
name: "all true",
fields: fields{
name: "jwt",
issuer: "https://jwt.com",
jwtEndpoint: "https://auth.com/jwt",
keysEndpoint: "https://jwt.com/keys",
headerName: "jwt-header",
encryptionAlg: func(t *testing.T) crypto.EncryptionAlgorithm {
return crypto.CreateMockEncryptionAlg(gomock.NewController(t))
},
opts: []ProviderOpts{
WithLinkingAllowed(),
WithCreationAllowed(),
WithAutoCreation(),
WithAutoUpdate(),
},
},
want: want{
name: "jwt",
linkingAllowed: true,
creationAllowed: true,
autoCreation: true,
autoUpdate: true,
pkce: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := assert.New(t)
provider, err := New(
tt.fields.name,
tt.fields.issuer,
tt.fields.jwtEndpoint,
tt.fields.keysEndpoint,
tt.fields.headerName,
tt.fields.encryptionAlg(t),
tt.fields.opts...,
)
require.NoError(t, err)
a.Equal(tt.want.name, provider.Name())
a.Equal(tt.want.linkingAllowed, provider.IsLinkingAllowed())
a.Equal(tt.want.creationAllowed, provider.IsCreationAllowed())
a.Equal(tt.want.autoCreation, provider.IsAutoCreation())
a.Equal(tt.want.autoUpdate, provider.IsAutoUpdate())
})
}
}