mirror of
https://github.com/zitadel/zitadel.git
synced 2025-07-13 15:08:31 +00:00

# Which Problems Are Solved The login v1 allowed to use JWTs as IdP using the JWT IDP. The login V2 uses idp intents for such cases, which were not yet able to handle JWT IdPs. # How the Problems Are Solved - Added handling of JWT IdPs in `StartIdPIntent` and `RetrieveIdPIntent` - The redirect returned by the start, uses the existing `authRequestID` and `userAgentID` parameter names for compatibility reasons. - Added `/idps/jwt` endpoint to handle the proxied (callback) endpoint , which extracts and validates the JWT against the configured endpoint. # Additional Changes None # Additional Context - closes #9758
227 lines
5.4 KiB
Go
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)
|
|
wantHeaders, wantContent := tt.want.session.GetAuth(ctx)
|
|
gotHeaders, gotContent := session.GetAuth(ctx)
|
|
a.Equal(wantHeaders, gotHeaders)
|
|
a.Equal(wantContent, gotContent)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
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())
|
|
})
|
|
}
|
|
}
|