2023-07-14 11:16:16 +00:00
|
|
|
//go:build integration
|
|
|
|
|
|
|
|
package oidc_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2023-12-05 17:01:03 +00:00
|
|
|
"github.com/zitadel/oidc/v3/pkg/client"
|
2023-10-17 15:19:51 +00:00
|
|
|
"github.com/zitadel/oidc/v3/pkg/client/rp"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/client/rs"
|
|
|
|
"github.com/zitadel/oidc/v3/pkg/oidc"
|
2023-11-23 14:17:50 +00:00
|
|
|
"golang.org/x/text/language"
|
2023-07-14 11:16:16 +00:00
|
|
|
|
2023-11-23 14:17:50 +00:00
|
|
|
oidc_api "github.com/zitadel/zitadel/internal/api/oidc"
|
2023-12-05 17:01:03 +00:00
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
|
|
"github.com/zitadel/zitadel/internal/integration"
|
2023-07-14 11:16:16 +00:00
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/authn"
|
|
|
|
"github.com/zitadel/zitadel/pkg/grpc/management"
|
2023-09-13 12:43:01 +00:00
|
|
|
oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2beta"
|
2023-07-14 11:16:16 +00:00
|
|
|
)
|
|
|
|
|
2023-11-21 12:11:38 +00:00
|
|
|
func TestServer_Introspect(t *testing.T) {
|
2023-07-14 11:16:16 +00:00
|
|
|
project, err := Tester.CreateProject(CTX)
|
|
|
|
require.NoError(t, err)
|
2023-12-28 09:25:18 +00:00
|
|
|
app, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false)
|
2023-07-14 11:16:16 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-12-28 13:31:41 +00:00
|
|
|
wantAudience := []string{app.GetClientId(), project.GetId()}
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
api func(*testing.T) (apiID string, resourceServer rs.ResourceServer)
|
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "client assertion",
|
|
|
|
api: func(t *testing.T) (string, rs.ResourceServer) {
|
|
|
|
api, err := Tester.CreateAPIClientJWT(CTX, project.GetId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
keyResp, err := Tester.Client.Mgmt.AddAppKey(CTX, &management.AddAppKeyRequest{
|
|
|
|
ProjectId: project.GetId(),
|
|
|
|
AppId: api.GetAppId(),
|
|
|
|
Type: authn.KeyType_KEY_TYPE_JSON,
|
|
|
|
ExpirationDate: nil,
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
resourceServer, err := Tester.CreateResourceServerJWTProfile(CTX, keyResp.GetKeyDetails())
|
|
|
|
require.NoError(t, err)
|
|
|
|
return api.GetClientId(), resourceServer
|
2023-07-14 11:16:16 +00:00
|
|
|
},
|
|
|
|
},
|
2023-12-28 13:31:41 +00:00
|
|
|
{
|
|
|
|
name: "client credentials",
|
|
|
|
api: func(t *testing.T) (string, rs.ResourceServer) {
|
|
|
|
api, err := Tester.CreateAPIClientBasic(CTX, project.GetId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
resourceServer, err := Tester.CreateResourceServerClientCredentials(CTX, api.GetClientId(), api.GetClientSecret())
|
|
|
|
require.NoError(t, err)
|
|
|
|
return api.GetClientId(), resourceServer
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "client invalid id, error",
|
|
|
|
api: func(t *testing.T) (string, rs.ResourceServer) {
|
|
|
|
api, err := Tester.CreateAPIClientBasic(CTX, project.GetId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
resourceServer, err := Tester.CreateResourceServerClientCredentials(CTX, "xxxxx", api.GetClientSecret())
|
|
|
|
require.NoError(t, err)
|
|
|
|
return api.GetClientId(), resourceServer
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "client invalid secret, error",
|
|
|
|
api: func(t *testing.T) (string, rs.ResourceServer) {
|
|
|
|
api, err := Tester.CreateAPIClientBasic(CTX, project.GetId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
resourceServer, err := Tester.CreateResourceServerClientCredentials(CTX, api.GetClientId(), "xxxxx")
|
|
|
|
require.NoError(t, err)
|
|
|
|
return api.GetClientId(), resourceServer
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "client credentials on jwt client, error",
|
|
|
|
api: func(t *testing.T) (string, rs.ResourceServer) {
|
|
|
|
api, err := Tester.CreateAPIClientJWT(CTX, project.GetId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
resourceServer, err := Tester.CreateResourceServerClientCredentials(CTX, api.GetClientId(), "xxxxx")
|
|
|
|
require.NoError(t, err)
|
|
|
|
return api.GetClientId(), resourceServer
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
apiID, resourceServer := tt.api(t)
|
|
|
|
// wantAudience grows for every API we add to the project.
|
|
|
|
wantAudience = append(wantAudience, apiID)
|
2023-07-14 11:16:16 +00:00
|
|
|
|
2023-12-28 13:31:41 +00:00
|
|
|
scope := []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess, oidc_api.ScopeResourceOwner}
|
|
|
|
authRequestID := createAuthRequest(t, app.GetClientId(), redirectURI, scope...)
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
|
|
|
AuthRequestId: authRequestID,
|
|
|
|
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
|
|
|
Session: &oidc_pb.Session{
|
|
|
|
SessionId: sessionID,
|
|
|
|
SessionToken: sessionToken,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
2023-07-14 11:16:16 +00:00
|
|
|
|
2023-12-28 13:31:41 +00:00
|
|
|
// code exchange
|
|
|
|
code := assertCodeResponse(t, linkResp.GetCallbackUrl())
|
2024-01-18 06:10:49 +00:00
|
|
|
tokens, err := exchangeTokens(t, app.GetClientId(), code, redirectURI)
|
2023-12-28 13:31:41 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, true)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
2023-12-28 13:31:41 +00:00
|
|
|
|
|
|
|
// test actual introspection
|
|
|
|
introspection, err := rs.Introspect[*oidc.IntrospectionResponse](context.Background(), resourceServer, tokens.AccessToken)
|
|
|
|
if tt.wantErr {
|
|
|
|
require.Error(t, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
assertIntrospection(t, introspection,
|
|
|
|
Tester.OIDCIssuer(), app.GetClientId(),
|
|
|
|
scope, wantAudience,
|
|
|
|
tokens.Expiry, tokens.Expiry.Add(-12*time.Hour))
|
|
|
|
})
|
|
|
|
}
|
2023-07-14 11:16:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func assertIntrospection(
|
|
|
|
t *testing.T,
|
|
|
|
introspection *oidc.IntrospectionResponse,
|
|
|
|
issuer, clientID string,
|
|
|
|
scope, audience []string,
|
|
|
|
expiration, creation time.Time,
|
|
|
|
) {
|
|
|
|
assert.True(t, introspection.Active)
|
|
|
|
assert.Equal(t, scope, []string(introspection.Scope))
|
|
|
|
assert.Equal(t, clientID, introspection.ClientID)
|
|
|
|
assert.Equal(t, oidc.BearerToken, introspection.TokenType)
|
|
|
|
assertOIDCTime(t, introspection.Expiration, expiration)
|
|
|
|
assertOIDCTime(t, introspection.IssuedAt, creation)
|
|
|
|
assertOIDCTime(t, introspection.NotBefore, creation)
|
|
|
|
assert.Equal(t, User.GetUserId(), introspection.Subject)
|
|
|
|
assert.ElementsMatch(t, audience, introspection.Audience)
|
|
|
|
assert.Equal(t, issuer, introspection.Issuer)
|
|
|
|
assert.NotEmpty(t, introspection.JWTID)
|
|
|
|
assert.NotEmpty(t, introspection.Username)
|
|
|
|
assert.Equal(t, introspection.Username, introspection.PreferredUsername)
|
|
|
|
assert.Equal(t, "Mickey", introspection.GivenName)
|
|
|
|
assert.Equal(t, "Mouse", introspection.FamilyName)
|
|
|
|
assert.Equal(t, "Mickey Mouse", introspection.Name)
|
2023-11-23 14:17:50 +00:00
|
|
|
assert.Equal(t, oidc.Gender("male"), introspection.Gender)
|
|
|
|
assert.Equal(t, oidc.NewLocale(language.Dutch), introspection.Locale)
|
2023-07-14 11:16:16 +00:00
|
|
|
assert.Equal(t, introspection.Username, introspection.Email)
|
|
|
|
assert.False(t, bool(introspection.EmailVerified))
|
|
|
|
assertOIDCTime(t, introspection.UpdatedAt, User.GetDetails().GetChangeDate().AsTime())
|
2023-11-23 14:17:50 +00:00
|
|
|
|
|
|
|
require.NotNil(t, introspection.Claims)
|
2024-03-20 10:18:46 +00:00
|
|
|
assert.Equal(t, User.Details.ResourceOwner, introspection.Claims[oidc_api.ClaimResourceOwnerID])
|
|
|
|
assert.NotEmpty(t, introspection.Claims[oidc_api.ClaimResourceOwnerName])
|
|
|
|
assert.NotEmpty(t, introspection.Claims[oidc_api.ClaimResourceOwnerPrimaryDomain])
|
2023-07-14 11:16:16 +00:00
|
|
|
}
|
2023-12-05 17:01:03 +00:00
|
|
|
|
|
|
|
// TestServer_VerifyClient tests verification by running code flow tests
|
|
|
|
// with clients that have different authentication methods.
|
|
|
|
func TestServer_VerifyClient(t *testing.T) {
|
|
|
|
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
|
|
|
project, err := Tester.CreateProject(CTX)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
inactiveClient, err := Tester.CreateOIDCInactivateClient(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
|
|
|
require.NoError(t, err)
|
2023-12-28 09:25:18 +00:00
|
|
|
nativeClient, err := Tester.CreateOIDCNativeClient(CTX, redirectURI, logoutRedirectURI, project.GetId(), false)
|
2023-12-05 17:01:03 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
basicWebClient, err := Tester.CreateOIDCWebClientBasic(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
jwtWebClient, keyData, err := Tester.CreateOIDCWebClientJWT(CTX, redirectURI, logoutRedirectURI, project.GetId())
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
type clientDetails struct {
|
|
|
|
authReqClientID string
|
|
|
|
clientID string
|
|
|
|
clientSecret string
|
|
|
|
keyData []byte
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
client clientDetails
|
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "empty client ID error",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: nativeClient.GetClientId(),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "client not found error",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: nativeClient.GetClientId(),
|
|
|
|
clientID: "foo",
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "client inactive error",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: nativeClient.GetClientId(),
|
|
|
|
clientID: inactiveClient.GetClientId(),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "native client success",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: nativeClient.GetClientId(),
|
|
|
|
clientID: nativeClient.GetClientId(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "web client basic secret empty error",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: basicWebClient.GetClientId(),
|
|
|
|
clientID: basicWebClient.GetClientId(),
|
|
|
|
clientSecret: "",
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "web client basic secret invalid error",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: basicWebClient.GetClientId(),
|
|
|
|
clientID: basicWebClient.GetClientId(),
|
|
|
|
clientSecret: "wrong",
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "web client basic secret success",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: basicWebClient.GetClientId(),
|
|
|
|
clientID: basicWebClient.GetClientId(),
|
|
|
|
clientSecret: basicWebClient.GetClientSecret(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "web client JWT profile empty assertion error",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: jwtWebClient.GetClientId(),
|
|
|
|
clientID: jwtWebClient.GetClientId(),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "web client JWT profile invalid assertion error",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: jwtWebClient.GetClientId(),
|
|
|
|
clientID: jwtWebClient.GetClientId(),
|
|
|
|
keyData: createInvalidKeyData(t, jwtWebClient),
|
|
|
|
},
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "web client JWT profile success",
|
|
|
|
client: clientDetails{
|
|
|
|
authReqClientID: jwtWebClient.GetClientId(),
|
|
|
|
clientID: jwtWebClient.GetClientId(),
|
|
|
|
keyData: keyData,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
authRequestID, err := Tester.CreateOIDCAuthRequest(CTX, tt.client.authReqClientID, Tester.Users[integration.FirstInstanceUsersKey][integration.Login].ID, redirectURI, oidc.ScopeOpenID)
|
|
|
|
require.NoError(t, err)
|
|
|
|
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
|
|
|
AuthRequestId: authRequestID,
|
|
|
|
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
|
|
|
Session: &oidc_pb.Session{
|
|
|
|
SessionId: sessionID,
|
|
|
|
SessionToken: sessionToken,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// use a new RP so we can inject different credentials
|
|
|
|
var options []rp.Option
|
|
|
|
if tt.client.keyData != nil {
|
|
|
|
options = append(options, rp.WithJWTProfile(rp.SignerFromKeyFile(tt.client.keyData)))
|
|
|
|
}
|
|
|
|
provider, err := rp.NewRelyingPartyOIDC(CTX, Tester.OIDCIssuer(), tt.client.clientID, tt.client.clientSecret, redirectURI, []string{oidc.ScopeOpenID}, options...)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// test code exchange
|
|
|
|
code := assertCodeResponse(t, linkResp.GetCallbackUrl())
|
|
|
|
codeOpts := codeExchangeOptions(t, provider)
|
|
|
|
tokens, err := rp.CodeExchange[*oidc.IDTokenClaims](context.Background(), code, provider, codeOpts...)
|
|
|
|
if tt.wantErr {
|
|
|
|
require.Error(t, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
|
|
assertTokens(t, tokens, false)
|
2024-05-31 10:10:18 +00:00
|
|
|
assertIDTokenClaims(t, tokens.IDTokenClaims, User.GetUserId(), armPasskey, startTime, changeTime, sessionID)
|
2023-12-05 17:01:03 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func codeExchangeOptions(t testing.TB, provider rp.RelyingParty) []rp.CodeExchangeOpt {
|
|
|
|
codeOpts := []rp.CodeExchangeOpt{rp.WithCodeVerifier(integration.CodeVerifier)}
|
|
|
|
if signer := provider.Signer(); signer != nil {
|
|
|
|
assertion, err := client.SignedJWTProfileAssertion(provider.OAuthConfig().ClientID, []string{provider.Issuer()}, time.Hour, provider.Signer())
|
|
|
|
require.NoError(t, err)
|
|
|
|
codeOpts = append(codeOpts, rp.WithClientAssertionJWT(assertion))
|
|
|
|
}
|
|
|
|
return codeOpts
|
|
|
|
}
|
|
|
|
|
|
|
|
func createInvalidKeyData(t testing.TB, client *management.AddOIDCAppResponse) []byte {
|
|
|
|
key := domain.ApplicationKey{
|
|
|
|
Type: domain.AuthNKeyTypeJSON,
|
|
|
|
KeyID: "1",
|
|
|
|
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----\nMIIEogIBAAKCAQEAxHd087RoEm9ywVWZ/H+tDWxQsmVvhfRz4jAq/RfU+OWXNH4J\njMMSHdFs0Q+WP98nNXRyc7fgbMb8NdmlB2yD4qLYapN5SDaBc5dh/3EnyFt53oSs\njTlKnQUPAeJr2qh/NY046CfyUyQMM4JR5OiQFo4TssfWnqdcgamGt0AEnk2lvbMZ\nKQdAqNS9lDzYbjMGavEQPTZE35mFXFQXjaooZXq+TIa7hbaq7/idH7cHNbLcPLgj\nfPQA8q+DYvnvhXlmq0LPQZH3Oiixf+SF2vRwrBzT2mqGD2OiOkUmhuPwyqEiiBHt\nfxklRtRU6WfLa1Gcb1PsV0uoBGpV3KybIl/GlwIDAQABAoIBAEQjDduLgOCL6Gem\n0X3hpdnW6/HC/jed/Sa//9jBECq2LYeWAqff64ON40hqOHi0YvvGA/+gEOSI6mWe\nsv5tIxxRz+6+cLybsq+tG96kluCE4TJMHy/nY7orS/YiWbd+4odnEApr+D3fbZ/b\nnZ1fDsHTyn8hkYx6jLmnWsJpIHDp7zxD76y7k2Bbg6DZrCGiVxngiLJk23dvz79W\np03lHLM7XE92aFwXQmhfxHGxrbuoB/9eY4ai5IHp36H4fw0vL6NXdNQAo/bhe0p9\nAYB7y0ZumF8Hg0Z/BmMeEzLy6HrYB+VE8cO93pNjhSyH+p2yDB/BlUyTiRLQAoM0\nVTmOZXECgYEA7NGlzpKNhyQEJihVqt0MW0LhKIO/xbBn+XgYfX6GpqPa/ucnMx5/\nVezpl3gK8IU4wPUhAyXXAHJiqNBcEeyxrw0MXLujDVMJgYaLysCLJdvMVgoY08mS\nK5IQivpbozpf4+0y3mOnA+Sy1kbfxv2X8xiWLODRQW3f3q/xoklwOR8CgYEA1GEe\nfaibOFTQAYcIVj77KXtBfYZsX3EGAyfAN9O7cKHq5oaxVstwnF47WxpuVtoKZxCZ\nbNm9D5WvQ9b+Ztpioe42tzwE7Bff/Osj868GcDdRPK7nFlh9N2yVn/D514dOYVwR\n4MBr1KrJzgRWt4QqS4H+to1GzudDTSNlG7gnK4kCgYBUi6AbOHzoYzZL/RhgcJwp\ntJ23nhmH1Su5h2OO4e3mbhcP66w19sxU+8iFN+kH5zfUw26utgKk+TE5vXExQQRK\nT2k7bg2PAzcgk80ybD0BHhA8I0yrx4m0nmfjhe/TPVLgh10iwgbtP+eM0i6v1vc5\nZWyvxu9N4ZEL6lpkqr0y1wKBgG/NAIQd8jhhTW7Aav8cAJQBsqQl038avJOEpYe+\nCnpsgoAAf/K0/f8TDCQVceh+t+MxtdK7fO9rWOxZjWsPo8Si5mLnUaAHoX4/OpnZ\nlYYVWMqdOEFnK+O1Yb7k2GFBdV2DXlX2dc1qavntBsls5ecB89id3pyk2aUN8Pf6\npYQhAoGAMGtrHFely9wyaxI0RTCyfmJbWZHGVGkv6ELK8wneJjdjl82XOBUGCg5q\naRCrTZ3dPitKwrUa6ibJCIFCIziiriBmjDvTHzkMvoJEap2TVxYNDR6IfINVsQ57\nlOsiC4A2uGq4Lbfld+gjoplJ5GX6qXtTgZ6m7eo0y7U6zm2tkN0=\n-----END RSA PRIVATE KEY-----\n"),
|
|
|
|
ApplicationID: client.GetAppId(),
|
|
|
|
ClientID: client.GetClientId(),
|
|
|
|
}
|
|
|
|
data, err := key.Detail()
|
|
|
|
require.NoError(t, err)
|
|
|
|
return data
|
|
|
|
}
|