276 lines
10 KiB
Go
276 lines
10 KiB
Go
package op_test
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-jose/go-jose/v4"
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
|
"github.com/zitadel/oidc/v3/pkg/op"
|
|
"github.com/zitadel/oidc/v3/pkg/op/mock"
|
|
)
|
|
|
|
func TestGetTokenIDAndSubjectFromToken(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
|
|
// args
|
|
ctx context.Context
|
|
token string
|
|
tokenType oidc.TokenType
|
|
isActor bool
|
|
exchanger op.Exchanger
|
|
|
|
wantReturn []interface{}
|
|
}{
|
|
{
|
|
name: "empty strings, nil and false if fails verify token actor",
|
|
ctx: context.Background(),
|
|
token: func() string {
|
|
tkn, err := op.NewAESCrypto([32]byte{0x01}).Encrypt("test:test:test")
|
|
require.NoError(t, err)
|
|
return tkn
|
|
}(),
|
|
tokenType: oidc.TokenType("unsupported_token_type"),
|
|
isActor: true,
|
|
exchanger: func() op.Exchanger {
|
|
type mStorage struct {
|
|
op.Storage
|
|
op.TokenExchangeTokensVerifierStorage
|
|
}
|
|
|
|
verifier := mock.NewMockTokenExchangeTokensVerifierStorage(gomock.NewController(t))
|
|
verifier.
|
|
EXPECT().
|
|
VerifyExchangeActorToken(gomock.Any(), gomock.Any(), oidc.TokenType("unsupported_token_type")).
|
|
Return("", "", nil, errors.New("actor verify error"))
|
|
ms := mStorage{TokenExchangeTokensVerifierStorage: verifier}
|
|
ex := mock.NewMockExchanger(gomock.NewController(t))
|
|
ex.EXPECT().Storage().Return(ms)
|
|
|
|
return ex
|
|
}(),
|
|
wantReturn: []interface{}{"", "", map[string]interface{}(nil), false},
|
|
},
|
|
{
|
|
name: "empty strings, nil and false if fails verify token exchange subject",
|
|
ctx: context.Background(),
|
|
token: func() string {
|
|
tkn, err := op.NewAESCrypto([32]byte{0x01}).Encrypt("test:test:test")
|
|
require.NoError(t, err)
|
|
return tkn
|
|
}(),
|
|
tokenType: oidc.TokenType("unsupported_token_type"),
|
|
isActor: false,
|
|
exchanger: func() op.Exchanger {
|
|
type mStorage struct {
|
|
op.Storage
|
|
op.TokenExchangeTokensVerifierStorage
|
|
}
|
|
|
|
verifier := mock.NewMockTokenExchangeTokensVerifierStorage(gomock.NewController(t))
|
|
verifier.
|
|
EXPECT().
|
|
VerifyExchangeSubjectToken(gomock.Any(), gomock.Any(), oidc.TokenType("unsupported_token_type")).
|
|
Return("", "", nil, errors.New("actor verify error"))
|
|
ms := mStorage{TokenExchangeTokensVerifierStorage: verifier}
|
|
ex := mock.NewMockExchanger(gomock.NewController(t))
|
|
ex.EXPECT().Storage().Return(ms)
|
|
|
|
return ex
|
|
}(),
|
|
wantReturn: []interface{}{"", "", map[string]interface{}(nil), false},
|
|
},
|
|
{
|
|
name: "empty strings, nil and false if exchanger storage is not TokenExchangeTokenVerifierStorage",
|
|
ctx: context.Background(),
|
|
token: func() string {
|
|
tkn, err := op.NewAESCrypto([32]byte{0x01}).Encrypt("test:test:test")
|
|
require.NoError(t, err)
|
|
return tkn
|
|
}(),
|
|
tokenType: oidc.TokenType("unsupported_token_type"),
|
|
isActor: false,
|
|
exchanger: func() op.Exchanger {
|
|
type mStorage struct {
|
|
op.Storage
|
|
}
|
|
|
|
ms := mStorage{}
|
|
ex := mock.NewMockExchanger(gomock.NewController(t))
|
|
ex.EXPECT().Storage().Return(ms)
|
|
|
|
return ex
|
|
}(),
|
|
wantReturn: []interface{}{"", "", map[string]interface{}(nil), false},
|
|
},
|
|
{
|
|
name: "tokenId subject nil claims and true if success decrypt AccessTokenType",
|
|
ctx: context.Background(),
|
|
token: func() string {
|
|
tkn, err := op.NewAESCrypto([32]byte{0x01}).Encrypt("test:test")
|
|
require.NoError(t, err)
|
|
return tkn
|
|
}(),
|
|
tokenType: oidc.AccessTokenType,
|
|
isActor: true,
|
|
exchanger: func() op.Exchanger {
|
|
ex := mock.NewMockExchanger(gomock.NewController(t))
|
|
ex.EXPECT().Crypto().Return(op.NewAESCrypto([32]byte{0x001}))
|
|
return ex
|
|
}(),
|
|
wantReturn: []interface{}{"test", "test", map[string]interface{}(nil), true},
|
|
},
|
|
{
|
|
name: "tokenId subject claims and true if success verify AccessTokenType claims",
|
|
ctx: context.Background(),
|
|
// jwt.io sample token for RS256 with some extra claims
|
|
token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiY2RlZiJ9.eyJqdGkiOiJ0ZXN0Iiwic3ViIjoiMTIzNDU2Nzg5MCIsIm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlLCJpYXQiOjE3NDI4OTA4NjksImV4cCI6Mjc0Mjg5MDg2OSwiaXNzIjoiaXNzdWVyIn0.MpR3s7zrvwHUnis7Sc9C1-dZnDsXAsssJcdDWJY5cfhR7bXpwJs87GsUkdP-kVc1S3MDZg0Dl0ITLbAeu70Ix5E65o9wlUQM2QB2Nc3O16zWIWkHH9xhKf5mW-s1JRtNDqsy5hypl6S2l9zNSXSLhj96lMxyi4-4qfX2wsI8XN9sQk7oEgZt_Lcl-xiJtqZOKKyfA0zRqOCOPruaAIUJc5bo3dxmDRs9dILP1F7LYkarIH_DXDzOmqRT4UfHVdg7ZH2-yluXQvk24dFwtC2Vm9jut1Ecdihm4vBjvyokuPNw_5RC3nxlk14BPBcgsBNR9NVVo5SV6ATGI-9Uq2TR-Q",
|
|
tokenType: oidc.AccessTokenType,
|
|
isActor: true,
|
|
exchanger: func() op.Exchanger {
|
|
c := mock.NewMockCrypto(gomock.NewController(t))
|
|
c.EXPECT().
|
|
Decrypt(gomock.Any()).
|
|
Return("", errors.New("decrypt_error"))
|
|
|
|
ex := mock.NewMockExchanger(gomock.NewController(t))
|
|
ex.EXPECT().Crypto().Return(c)
|
|
|
|
mockKey := mock.NewMockKey(gomock.NewController(t))
|
|
mockKey.EXPECT().ID().Return("abcdef")
|
|
mockKey.EXPECT().Algorithm().Return(jose.RS256)
|
|
mockKey.EXPECT().Use().Return("sig")
|
|
mockKey.EXPECT().Key().Return(getPublicKey())
|
|
|
|
kStorage := mock.NewMockStorage(gomock.NewController(t))
|
|
kStorage.EXPECT().KeySet(gomock.Any()).Return([]op.Key{mockKey}, nil)
|
|
v := op.AccessTokenVerifier(oidc.Verifier{
|
|
Issuer: "issuer",
|
|
SupportedSignAlgs: []string{string(jose.RS256)},
|
|
KeySet: &op.OpenIDKeySet{
|
|
Storage: kStorage,
|
|
},
|
|
Offset: 5000 * time.Minute,
|
|
})
|
|
ex.EXPECT().AccessTokenVerifier(gomock.Any()).Return(&v)
|
|
return ex
|
|
}(),
|
|
wantReturn: []interface{}{"test", "1234567890", map[string]interface{}{
|
|
"admin": true,
|
|
"exp": float64(2742890869),
|
|
"iat": float64(1742890869),
|
|
"iss": "issuer",
|
|
"name": "John Doe",
|
|
"jti": "test",
|
|
"sub": "1234567890",
|
|
}, true},
|
|
},
|
|
{
|
|
name: "token subject and nil claims if success handling refresh token type",
|
|
ctx: context.Background(),
|
|
// jwt.io sample token for RS256 with some extra claims
|
|
token: "test",
|
|
tokenType: oidc.RefreshTokenType,
|
|
isActor: true,
|
|
exchanger: func() op.Exchanger {
|
|
rt := mock.NewMockRefreshTokenRequest(gomock.NewController(t))
|
|
rt.EXPECT().GetSubject().Return("1234567890")
|
|
st := mock.NewMockStorage(gomock.NewController(t))
|
|
st.EXPECT().
|
|
TokenRequestByRefreshToken(gomock.Any(), "test").
|
|
Return(rt, nil)
|
|
ex := mock.NewMockExchanger(gomock.NewController(t))
|
|
ex.EXPECT().Storage().Return(st)
|
|
return ex
|
|
}(),
|
|
wantReturn: []interface{}{"test", "1234567890", map[string]interface{}(nil), true},
|
|
},
|
|
{
|
|
name: "token subject and tokenclaims if success handling id token type",
|
|
ctx: context.Background(),
|
|
// jwt.io sample token for RS256 with some extra claims
|
|
token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiY2RlZiJ9.eyJqdGkiOiJ0ZXN0Iiwic3ViIjoiMTIzNDU2Nzg5MCIsIm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlLCJpYXQiOjE3NDI4OTA4NjksImV4cCI6Mjc0Mjg5MDg2OSwiaXNzIjoiaXNzdWVyIn0.MpR3s7zrvwHUnis7Sc9C1-dZnDsXAsssJcdDWJY5cfhR7bXpwJs87GsUkdP-kVc1S3MDZg0Dl0ITLbAeu70Ix5E65o9wlUQM2QB2Nc3O16zWIWkHH9xhKf5mW-s1JRtNDqsy5hypl6S2l9zNSXSLhj96lMxyi4-4qfX2wsI8XN9sQk7oEgZt_Lcl-xiJtqZOKKyfA0zRqOCOPruaAIUJc5bo3dxmDRs9dILP1F7LYkarIH_DXDzOmqRT4UfHVdg7ZH2-yluXQvk24dFwtC2Vm9jut1Ecdihm4vBjvyokuPNw_5RC3nxlk14BPBcgsBNR9NVVo5SV6ATGI-9Uq2TR-Q",
|
|
tokenType: oidc.IDTokenType,
|
|
isActor: true,
|
|
exchanger: func() op.Exchanger {
|
|
mockKey := mock.NewMockKey(gomock.NewController(t))
|
|
mockKey.EXPECT().ID().Return("abcdef")
|
|
mockKey.EXPECT().Algorithm().Return(jose.RS256)
|
|
mockKey.EXPECT().Use().Return("sig")
|
|
mockKey.EXPECT().Key().Return(getPublicKey())
|
|
|
|
kStorage := mock.NewMockStorage(gomock.NewController(t))
|
|
kStorage.EXPECT().KeySet(gomock.Any()).Return([]op.Key{mockKey}, nil)
|
|
v := &op.IDTokenHintVerifier{
|
|
Issuer: "issuer",
|
|
SupportedSignAlgs: []string{string(jose.RS256)},
|
|
KeySet: &op.OpenIDKeySet{
|
|
Storage: kStorage,
|
|
},
|
|
Offset: 5000 * time.Minute,
|
|
}
|
|
ex := mock.NewMockExchanger(gomock.NewController(t))
|
|
ex.EXPECT().IDTokenHintVerifier(gomock.Any()).Return(v)
|
|
return ex
|
|
}(),
|
|
wantReturn: []interface{}{
|
|
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImFiY2RlZiJ9.eyJqdGkiOiJ0ZXN0Iiwic3ViIjoiMTIzNDU2Nzg5MCIsIm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlLCJpYXQiOjE3NDI4OTA4NjksImV4cCI6Mjc0Mjg5MDg2OSwiaXNzIjoiaXNzdWVyIn0.MpR3s7zrvwHUnis7Sc9C1-dZnDsXAsssJcdDWJY5cfhR7bXpwJs87GsUkdP-kVc1S3MDZg0Dl0ITLbAeu70Ix5E65o9wlUQM2QB2Nc3O16zWIWkHH9xhKf5mW-s1JRtNDqsy5hypl6S2l9zNSXSLhj96lMxyi4-4qfX2wsI8XN9sQk7oEgZt_Lcl-xiJtqZOKKyfA0zRqOCOPruaAIUJc5bo3dxmDRs9dILP1F7LYkarIH_DXDzOmqRT4UfHVdg7ZH2-yluXQvk24dFwtC2Vm9jut1Ecdihm4vBjvyokuPNw_5RC3nxlk14BPBcgsBNR9NVVo5SV6ATGI-9Uq2TR-Q",
|
|
"1234567890",
|
|
map[string]interface{}{
|
|
"admin": true,
|
|
"exp": float64(2742890869),
|
|
"iat": float64(1742890869),
|
|
"iss": "issuer",
|
|
"name": "John Doe",
|
|
"jti": "test",
|
|
"sub": "1234567890",
|
|
},
|
|
true,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tokenIdOrToken, subject, claims, ok := op.GetTokenIDAndSubjectFromToken(
|
|
tt.ctx,
|
|
tt.exchanger,
|
|
tt.token,
|
|
tt.tokenType,
|
|
tt.isActor,
|
|
)
|
|
|
|
assert.Equal(t, tt.wantReturn[0].(string), tokenIdOrToken)
|
|
assert.Equal(t, tt.wantReturn[1].(string), subject)
|
|
assert.Equal(t, tt.wantReturn[2], claims)
|
|
assert.Equal(t, tt.wantReturn[3].(bool), ok)
|
|
})
|
|
}
|
|
}
|
|
|
|
func getPublicKey() *rsa.PublicKey {
|
|
// jwt.io sample public key for RS256
|
|
spkiPem := `-----BEGIN PUBLIC KEY-----
|
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
|
|
4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
|
|
+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
|
|
kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
|
|
0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
|
|
cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
|
|
mwIDAQAB
|
|
-----END PUBLIC KEY-----`
|
|
spkiBlock, _ := pem.Decode([]byte(spkiPem))
|
|
var spkiKey *rsa.PublicKey
|
|
pubInterface, _ := x509.ParsePKIXPublicKey(spkiBlock.Bytes)
|
|
spkiKey = pubInterface.(*rsa.PublicKey)
|
|
return spkiKey
|
|
}
|