From 77db13e1e7e6312b1479588a9aafc491a114c105 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Tue, 5 Nov 2024 17:34:17 +0100 Subject: [PATCH 1/8] feat: basic loggic for dynamic wellknown --- cmd/vc-rest/startcmd/params.go | 1 + cmd/vc-rest/startcmd/start.go | 20 ++++- .../wallet-cli/pkg/oidc4vci/oidc4vci_flow.go | 3 +- pkg/profile/api.go | 1 + .../gomock_reflect_3569893530/prog.go | 70 ++++++++++++++++ pkg/restapi/v1/common/common.go | 13 +++ .../oidc4ci/gomock_reflect_2238258269/prog.go | 82 ++++++++++++++++++ .../oidc4ci/gomock_reflect_784313643/prog.go | 82 ++++++++++++++++++ pkg/service/oidc4ci/interfaces.go | 10 +++ pkg/service/oidc4ci/oidc4ci_service.go | 10 +++ .../oidc4ci_service_initiate_issuance.go | 39 +++++++-- .../verifycredential_service.go | 1 + pkg/service/wellknown/provider/api.go | 3 + pkg/service/wellknown/provider/interfaces.go | 18 ++++ .../wellknown/provider/wellknown_service.go | 66 ++++++++++++--- .../dynamicwellknown/dynamicwellknown.go | 83 +++++++++++++++++++ .../redis/dynamicwellknown/interfaces.go | 14 ++++ test/bdd/attestation/go.mod | 2 +- test/bdd/features/oidc4vc_api.feature | 5 +- test/bdd/fixtures/profile/profiles.json | 53 ++++++++++++ test/bdd/pkg/v1/oidc4vc/oidc4vci.go | 2 +- 21 files changed, 553 insertions(+), 25 deletions(-) create mode 100644 pkg/restapi/handlers/gomock_reflect_3569893530/prog.go create mode 100644 pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go create mode 100644 pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go create mode 100644 pkg/service/wellknown/provider/api.go create mode 100644 pkg/service/wellknown/provider/interfaces.go create mode 100644 pkg/storage/redis/dynamicwellknown/dynamicwellknown.go create mode 100644 pkg/storage/redis/dynamicwellknown/interfaces.go diff --git a/cmd/vc-rest/startcmd/params.go b/cmd/vc-rest/startcmd/params.go index 664cc8873..da1df87d2 100644 --- a/cmd/vc-rest/startcmd/params.go +++ b/cmd/vc-rest/startcmd/params.go @@ -392,6 +392,7 @@ const ( defaultOIDC4CITransactionDataTTL = 15 * time.Minute defaultOIDC4CIAckDataTTL = 24 * time.Hour defaultOIDC4CIAuthStateTTL = 15 * time.Minute + defaultDynamicWellKnownTTL = 1 * time.Hour defaultDataEncryptionKeyLength = 256 ) diff --git a/cmd/vc-rest/startcmd/start.go b/cmd/vc-rest/startcmd/start.go index 2154ff975..7394a2ab4 100644 --- a/cmd/vc-rest/startcmd/start.go +++ b/cmd/vc-rest/startcmd/start.go @@ -115,6 +115,7 @@ import ( "github.com/trustbloc/vcs/pkg/storage/redis" redisclient "github.com/trustbloc/vcs/pkg/storage/redis" "github.com/trustbloc/vcs/pkg/storage/redis/ackstore" + "github.com/trustbloc/vcs/pkg/storage/redis/dynamicwellknown" oidc4ciclaimdatastoreredis "github.com/trustbloc/vcs/pkg/storage/redis/oidc4ciclaimdatastore" oidc4cinoncestoreredis "github.com/trustbloc/vcs/pkg/storage/redis/oidc4cinoncestore" oidc4cistatestoreredis "github.com/trustbloc/vcs/pkg/storage/redis/oidc4cistatestore" @@ -553,10 +554,16 @@ func buildEchoHandler( return newHTTPClient(tlsConfig, conf.StartupParameters, metrics, id) } + dynamicWellKnownStore, err := getDynamicWellKnownStore(redisClient) + if err != nil { + return nil, fmt.Errorf("failed to get dynamic well-known store: %w", err) + } + openidCredentialIssuerConfigProviderSvc := wellknownprovider.NewService(&wellknownprovider.Config{ - ExternalHostURL: conf.StartupParameters.apiGatewayURL, - KMSRegistry: kmsRegistry, - CryptoJWTSigner: vcCrypto, + ExternalHostURL: conf.StartupParameters.apiGatewayURL, + KMSRegistry: kmsRegistry, + CryptoJWTSigner: vcCrypto, + DynamicWellKnownStore: dynamicWellKnownStore, }) // Issuer Profile Management API @@ -735,6 +742,7 @@ func buildEchoHandler( AckService: ackService, DocumentLoader: documentLoader, PrepareCredential: prepareCredentialSvc, + WellKnownProvider: openidCredentialIssuerConfigProviderSvc, }) if err != nil { return nil, fmt.Errorf("failed to instantiate new oidc4ci service: %w", err) @@ -1213,6 +1221,12 @@ func getOIDC4CITransactionStore( return store, nil } +func getDynamicWellKnownStore( + redisClient *redis.Client, +) (wellknownprovider.DynamicWellKnownStore, error) { + return dynamicwellknown.New(redisClient, defaultDynamicWellKnownTTL), nil +} + func getAckStore( redisClient *redis.Client, oidc4ciAckDataTTL int32, diff --git a/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go b/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go index e4cb8c191..0643053fe 100644 --- a/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go +++ b/component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go @@ -13,7 +13,6 @@ import ( "encoding/json" "errors" "fmt" - "go.uber.org/zap" "io" "log/slog" "net" @@ -24,6 +23,8 @@ import ( "strings" "time" + "go.uber.org/zap" + "github.com/cli/browser" "github.com/google/uuid" "github.com/piprate/json-gold/ld" diff --git a/pkg/profile/api.go b/pkg/profile/api.go index e40ef96da..57a8eff35 100644 --- a/pkg/profile/api.go +++ b/pkg/profile/api.go @@ -179,6 +179,7 @@ type OIDCConfig struct { CredentialResponseAlgValuesSupported []string `json:"credential_response_alg_values_supported"` CredentialResponseEncValuesSupported []string `json:"credential_response_enc_values_supported"` CredentialResponseEncryptionRequired bool `json:"credential_response_encryption_required"` + DynamicWellKnownSupported bool `json:"dynamic_well_known_supported"` ClaimsEndpoint string `json:"claims_endpoint"` } diff --git a/pkg/restapi/handlers/gomock_reflect_3569893530/prog.go b/pkg/restapi/handlers/gomock_reflect_3569893530/prog.go new file mode 100644 index 000000000..02adc13a7 --- /dev/null +++ b/pkg/restapi/handlers/gomock_reflect_3569893530/prog.go @@ -0,0 +1,70 @@ + +package main + +import ( + "encoding/gob" + "flag" + "fmt" + "os" + "path" + "reflect" + + "github.com/golang/mock/mockgen/model" + + pkg_ "github.com/ory/fosite/handler/oauth2" +) + +var output = flag.String("output", "", "The output file name, or empty to use stdout.") + +func main() { + flag.Parse() + + its := []struct{ + sym string + typ reflect.Type + }{ + + { "CoreStorage", reflect.TypeOf((*pkg_.CoreStorage)(nil)).Elem()}, + + { "AccessTokenStrategy", reflect.TypeOf((*pkg_.AccessTokenStrategy)(nil)).Elem()}, + + { "RefreshTokenStrategy", reflect.TypeOf((*pkg_.RefreshTokenStrategy)(nil)).Elem()}, + + } + pkg := &model.Package{ + // NOTE: This behaves contrary to documented behaviour if the + // package name is not the final component of the import path. + // The reflect package doesn't expose the package name, though. + Name: path.Base("github.com/ory/fosite/handler/oauth2"), + } + + for _, it := range its { + intf, err := model.InterfaceFromInterfaceType(it.typ) + if err != nil { + fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) + os.Exit(1) + } + intf.Name = it.sym + pkg.Interfaces = append(pkg.Interfaces, intf) + } + + outfile := os.Stdout + if len(*output) != 0 { + var err error + outfile, err = os.Create(*output) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) + } + defer func() { + if err := outfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) + os.Exit(1) + } + }() + } + + if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { + fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) + os.Exit(1) + } +} diff --git a/pkg/restapi/v1/common/common.go b/pkg/restapi/v1/common/common.go index 872033cb8..bf64c5fe5 100644 --- a/pkg/restapi/v1/common/common.go +++ b/pkg/restapi/v1/common/common.go @@ -53,6 +53,19 @@ func ValidateVPFormat(format VPFormat) (vcsverifiable.Format, error) { return "", fmt.Errorf("unsupported vp format %s, use one of next [%s, %s]", format, JwtVcJsonLd, LdpVc) } +func MapToVCFormat(format vcsverifiable.Format) (vcsverifiable.OIDCFormat, error) { + switch format { + case vcsverifiable.Jwt: + return vcsverifiable.JwtVCJson, nil + case vcsverifiable.Ldp: + return vcsverifiable.JwtVCJsonLD, nil + case vcsverifiable.Cwt: + return vcsverifiable.CwtVcLD, nil + } + + return "", fmt.Errorf("vc format missmatch %s, rest api supports only [%s, %s]", format, JwtVcJsonLd, LdpVc) +} + func MapToVPFormat(format vcsverifiable.Format) (VPFormat, error) { switch format { case vcsverifiable.Jwt: diff --git a/pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go b/pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go new file mode 100644 index 000000000..dca6eea89 --- /dev/null +++ b/pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go @@ -0,0 +1,82 @@ + +package main + +import ( + "encoding/gob" + "flag" + "fmt" + "os" + "path" + "reflect" + + "github.com/golang/mock/mockgen/model" + + pkg_ "github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci" +) + +var output = flag.String("output", "", "The output file name, or empty to use stdout.") + +func main() { + flag.Parse() + + its := []struct{ + sym string + typ reflect.Type + }{ + + { "StateStore", reflect.TypeOf((*pkg_.StateStore)(nil)).Elem()}, + + { "OAuth2Provider", reflect.TypeOf((*pkg_.OAuth2Provider)(nil)).Elem()}, + + { "IssuerInteractionClient", reflect.TypeOf((*pkg_.IssuerInteractionClient)(nil)).Elem()}, + + { "HTTPClient", reflect.TypeOf((*pkg_.HTTPClient)(nil)).Elem()}, + + { "ClientManager", reflect.TypeOf((*pkg_.ClientManager)(nil)).Elem()}, + + { "ProfileService", reflect.TypeOf((*pkg_.ProfileService)(nil)).Elem()}, + + { "AckService", reflect.TypeOf((*pkg_.AckService)(nil)).Elem()}, + + { "CwtProofChecker", reflect.TypeOf((*pkg_.CwtProofChecker)(nil)).Elem()}, + + { "LDPProofParser", reflect.TypeOf((*pkg_.LDPProofParser)(nil)).Elem()}, + + } + pkg := &model.Package{ + // NOTE: This behaves contrary to documented behaviour if the + // package name is not the final component of the import path. + // The reflect package doesn't expose the package name, though. + Name: path.Base("github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci"), + } + + for _, it := range its { + intf, err := model.InterfaceFromInterfaceType(it.typ) + if err != nil { + fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) + os.Exit(1) + } + intf.Name = it.sym + pkg.Interfaces = append(pkg.Interfaces, intf) + } + + outfile := os.Stdout + if len(*output) != 0 { + var err error + outfile, err = os.Create(*output) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) + } + defer func() { + if err := outfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) + os.Exit(1) + } + }() + } + + if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { + fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) + os.Exit(1) + } +} diff --git a/pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go b/pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go new file mode 100644 index 000000000..dca6eea89 --- /dev/null +++ b/pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go @@ -0,0 +1,82 @@ + +package main + +import ( + "encoding/gob" + "flag" + "fmt" + "os" + "path" + "reflect" + + "github.com/golang/mock/mockgen/model" + + pkg_ "github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci" +) + +var output = flag.String("output", "", "The output file name, or empty to use stdout.") + +func main() { + flag.Parse() + + its := []struct{ + sym string + typ reflect.Type + }{ + + { "StateStore", reflect.TypeOf((*pkg_.StateStore)(nil)).Elem()}, + + { "OAuth2Provider", reflect.TypeOf((*pkg_.OAuth2Provider)(nil)).Elem()}, + + { "IssuerInteractionClient", reflect.TypeOf((*pkg_.IssuerInteractionClient)(nil)).Elem()}, + + { "HTTPClient", reflect.TypeOf((*pkg_.HTTPClient)(nil)).Elem()}, + + { "ClientManager", reflect.TypeOf((*pkg_.ClientManager)(nil)).Elem()}, + + { "ProfileService", reflect.TypeOf((*pkg_.ProfileService)(nil)).Elem()}, + + { "AckService", reflect.TypeOf((*pkg_.AckService)(nil)).Elem()}, + + { "CwtProofChecker", reflect.TypeOf((*pkg_.CwtProofChecker)(nil)).Elem()}, + + { "LDPProofParser", reflect.TypeOf((*pkg_.LDPProofParser)(nil)).Elem()}, + + } + pkg := &model.Package{ + // NOTE: This behaves contrary to documented behaviour if the + // package name is not the final component of the import path. + // The reflect package doesn't expose the package name, though. + Name: path.Base("github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci"), + } + + for _, it := range its { + intf, err := model.InterfaceFromInterfaceType(it.typ) + if err != nil { + fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) + os.Exit(1) + } + intf.Name = it.sym + pkg.Interfaces = append(pkg.Interfaces, intf) + } + + outfile := os.Stdout + if len(*output) != 0 { + var err error + outfile, err = os.Create(*output) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) + } + defer func() { + if err := outfile.Close(); err != nil { + fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) + os.Exit(1) + } + }() + } + + if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { + fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) + os.Exit(1) + } +} diff --git a/pkg/service/oidc4ci/interfaces.go b/pkg/service/oidc4ci/interfaces.go index 480fe7c99..b25722543 100644 --- a/pkg/service/oidc4ci/interfaces.go +++ b/pkg/service/oidc4ci/interfaces.go @@ -5,6 +5,7 @@ import ( "github.com/trustbloc/vc-go/verifiable" + profileapi "github.com/trustbloc/vcs/pkg/profile" "github.com/trustbloc/vcs/pkg/service/issuecredential" ) @@ -24,3 +25,12 @@ type composer interface { // nolint:unused req *issuecredential.PrepareCredentialsRequest, ) (*verifiable.Credential, error) } + +type wellKnownProvider interface { + AddDynamicConfiguration( + ctx context.Context, + profileID string, + id string, + credSupported *profileapi.CredentialsConfigurationSupported, + ) error +} diff --git a/pkg/service/oidc4ci/oidc4ci_service.go b/pkg/service/oidc4ci/oidc4ci_service.go index 4973f2b40..ee40812e6 100644 --- a/pkg/service/oidc4ci/oidc4ci_service.go +++ b/pkg/service/oidc4ci/oidc4ci_service.go @@ -175,6 +175,7 @@ type Config struct { AckService ackService DocumentLoader documentLoader PrepareCredential credentialIssuer + WellKnownProvider wellKnownProvider } // Service implements VCS credential interaction API for OIDC credential issuance. @@ -198,6 +199,7 @@ type Service struct { ackService ackService documentLoader documentLoader credentialIssuer credentialIssuer + wellKnownProvider wellKnownProvider } // NewService returns a new Service instance. @@ -222,6 +224,7 @@ func NewService(config *Config) (*Service, error) { ackService: config.AckService, documentLoader: config.DocumentLoader, credentialIssuer: config.PrepareCredential, + wellKnownProvider: config.WellKnownProvider, }, nil } @@ -780,6 +783,13 @@ func (s *Service) PrepareCredential( //nolint:funlen return nil, err } + id := requestedTxCredentialConfigurationIDs + b, _ := json.Marshal(id) + logger.Info(fmt.Sprintf("requestedTxCredentialConfigurationIDs : %v", string(b))) + + b1, _ := json.Marshal(tx) + logger.Info(fmt.Sprintf("tx : %v", string(b1))) + requestedTxCredentialConfigurationIDs[txCredentialConfiguration.ID] = struct{}{} cred, ackID, prepareCredError := s.prepareCredential(ctx, tx, txCredentialConfiguration, requestedCredential) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go index 81412f2c5..afc741e67 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go @@ -26,6 +26,7 @@ import ( "github.com/trustbloc/vcs/pkg/doc/verifiable" profileapi "github.com/trustbloc/vcs/pkg/profile" "github.com/trustbloc/vcs/pkg/restapi/resterr" + "github.com/trustbloc/vcs/pkg/restapi/v1/common" "github.com/trustbloc/vcs/pkg/service/issuecredential" ) @@ -208,15 +209,20 @@ func (s *Service) newTxCredentialConf( } } - credentialConfigurationID, _, err := findCredentialConfigurationID( - targetCredentialTemplate.ID, targetCredentialTemplate.Type, profile) + credentialConfigurationID, metaCredentialConfiguration, err := s.findCredentialConfigurationID( + ctx, + targetCredentialTemplate.ID, + targetCredentialTemplate.Type, + profile, + ) + if err != nil { return nil, err } - profileMeta := profile.CredentialMetaData + //profileMeta := profile.CredentialMetaData - metaCredentialConfiguration := profileMeta.CredentialsConfigurationSupported[credentialConfigurationID] + //metaCredentialConfiguration := profileMeta.CredentialsConfigurationSupported[credentialConfigurationID] txCredentialConfiguration := &issuecredential.TxCredentialConfiguration{ ID: uuid.NewString(), @@ -489,7 +495,8 @@ func findCredentialTemplate( return profile.CredentialTemplates[0], nil } -func findCredentialConfigurationID( +func (s *Service) findCredentialConfigurationID( + ctx context.Context, requestedTemplateID string, credentialType string, profile *profileapi.Issuer, @@ -500,6 +507,28 @@ func findCredentialConfigurationID( } } + if profile.OIDCConfig.DynamicWellKnownSupported { + id := uuid.NewString() + + vcFormat, err := common.MapToVCFormat(profile.VCConfig.Format) + if err != nil { + return "", nil, err + } + + cfg := &profileapi.CredentialsConfigurationSupported{ + Format: vcFormat, + CredentialDefinition: &profileapi.CredentialDefinition{ + Type: []string{credentialType}, + }, + } + + if err = s.wellKnownProvider.AddDynamicConfiguration(ctx, profile.ID, id, cfg); err != nil { + return "", nil, err + } + + return id, cfg, nil + } + return "", nil, resterr.NewValidationError(resterr.InvalidValue, "credential_template_id", fmt.Errorf("credential configuration not found for requested template id %s", requestedTemplateID)) } diff --git a/pkg/service/verifycredential/verifycredential_service.go b/pkg/service/verifycredential/verifycredential_service.go index cb918bb0a..7f2d3fd40 100644 --- a/pkg/service/verifycredential/verifycredential_service.go +++ b/pkg/service/verifycredential/verifycredential_service.go @@ -131,6 +131,7 @@ func (s *Service) verifyVC( return fmt.Errorf("get data integrity verifier: %w", err) } + logger.Info(fmt.Sprintf("verifying VC with challenge[%s] and domain[%s]", challenge, domain)) opts := []verifiable.CredentialOpt{ verifiable.WithProofChecker( defaults.NewDefaultProofChecker(vermethod.NewVDRResolver(s.vdr)), diff --git a/pkg/service/wellknown/provider/api.go b/pkg/service/wellknown/provider/api.go new file mode 100644 index 000000000..7b15bfe86 --- /dev/null +++ b/pkg/service/wellknown/provider/api.go @@ -0,0 +1,3 @@ +package provider + +type DynamicWellKnownStore dynamicWellKnownStore diff --git a/pkg/service/wellknown/provider/interfaces.go b/pkg/service/wellknown/provider/interfaces.go new file mode 100644 index 000000000..4c06d3771 --- /dev/null +++ b/pkg/service/wellknown/provider/interfaces.go @@ -0,0 +1,18 @@ +package provider + +import ( + "context" + + profileapi "github.com/trustbloc/vcs/pkg/profile" +) + +//go:generate mockgen -destination interfaces_mocks_test.go -package provider_test -source=interfaces.go + +type dynamicWellKnownStore interface { + Upsert( + ctx context.Context, + profileID string, + item map[string]*profileapi.CredentialsConfigurationSupported, + ) error + Get(ctx context.Context, profileID string) (map[string]*profileapi.CredentialsConfigurationSupported, error) +} diff --git a/pkg/service/wellknown/provider/wellknown_service.go b/pkg/service/wellknown/provider/wellknown_service.go index ce80d4838..f38f6bb98 100644 --- a/pkg/service/wellknown/provider/wellknown_service.go +++ b/pkg/service/wellknown/provider/wellknown_service.go @@ -9,6 +9,8 @@ SPDX-License-Identifier: Apache-2.0 package provider import ( + "context" + "errors" "fmt" "net/url" "strings" @@ -41,25 +43,39 @@ type JWTWellKnownOpenIDIssuerConfigurationClaims struct { } type Config struct { - ExternalHostURL string - KMSRegistry kmsRegistry - CryptoJWTSigner cryptoJWTSigner + ExternalHostURL string + KMSRegistry kmsRegistry + CryptoJWTSigner cryptoJWTSigner + DynamicWellKnownStore dynamicWellKnownStore } type Service struct { - externalHostURL string - kmsRegistry kmsRegistry - cryptoJWTSigner cryptoJWTSigner + externalHostURL string + kmsRegistry kmsRegistry + cryptoJWTSigner cryptoJWTSigner + dynamicWellKnownStore dynamicWellKnownStore } func NewService(config *Config) *Service { return &Service{ - externalHostURL: config.ExternalHostURL, - kmsRegistry: config.KMSRegistry, - cryptoJWTSigner: config.CryptoJWTSigner, + externalHostURL: config.ExternalHostURL, + kmsRegistry: config.KMSRegistry, + cryptoJWTSigner: config.CryptoJWTSigner, + dynamicWellKnownStore: config.DynamicWellKnownStore, } } +func (s *Service) AddDynamicConfiguration( + ctx context.Context, + profileID string, + id string, + credSupported *profileapi.CredentialsConfigurationSupported, +) error { + return s.dynamicWellKnownStore.Upsert(ctx, profileID, map[string]*profileapi.CredentialsConfigurationSupported{ + id: credSupported, + }) +} + // GetOpenIDCredentialIssuerConfig returns issuer.WellKnownOpenIDIssuerConfiguration object, and // it's JWT signed representation, if this feature is enabled for specific profile. // @@ -79,7 +95,10 @@ func (s *Service) GetOpenIDCredentialIssuerConfig( err error ) - issuerMetadata := s.getOpenIDIssuerConfig(issuerProfile) + issuerMetadata, err := s.getOpenIDIssuerConfig(issuerProfile) + if err != nil { + return nil, "", err + } if issuerProfile.OIDCConfig != nil && issuerProfile.OIDCConfig.SignedIssuerMetadataSupported { jwtSignedIssuerMetadata, err = s.signIssuerMetadata(issuerProfile, issuerMetadata) @@ -91,7 +110,9 @@ func (s *Service) GetOpenIDCredentialIssuerConfig( return issuerMetadata, jwtSignedIssuerMetadata, nil } -func (s *Service) getOpenIDIssuerConfig(issuerProfile *profileapi.Issuer) *issuer.WellKnownOpenIDIssuerConfiguration { +func (s *Service) getOpenIDIssuerConfig( + issuerProfile *profileapi.Issuer, +) (*issuer.WellKnownOpenIDIssuerConfiguration, error) { // TODO: add support of internationalization and Accept-Language Header for this function. // Spec: https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-wg-draft.html#section-11.2.2 // For now, the following option from the spec supported: @@ -101,6 +122,27 @@ func (s *Service) getOpenIDIssuerConfig(issuerProfile *profileapi.Issuer) *issue host += "/" } + if issuerProfile.OIDCConfig != nil && issuerProfile.OIDCConfig.DynamicWellKnownSupported { + if issuerProfile.CredentialMetaData == nil { + issuerProfile.CredentialMetaData = &profileapi.CredentialMetaData{} + } + + dynamic, err := s.dynamicWellKnownStore.Get(context.Background(), issuerProfile.ID) + if err != nil { + return nil, errors.Join(err, errors.New("can not get dynamic well-known")) + } + + if issuerProfile.CredentialMetaData.CredentialsConfigurationSupported == nil { + issuerProfile.CredentialMetaData.CredentialsConfigurationSupported = make( + map[string]*profileapi.CredentialsConfigurationSupported, + ) + } + + for k, v := range dynamic { + issuerProfile.CredentialMetaData.CredentialsConfigurationSupported[k] = v + } + } + credentialsConfigurationSupported := s.buildCredentialConfigurationsSupported(issuerProfile) issuerURL, _ := url.JoinPath(s.externalHostURL, "issuer", issuerProfile.ID, issuerProfile.Version) @@ -142,7 +184,7 @@ func (s *Service) getOpenIDIssuerConfig(issuerProfile *profileapi.Issuer) *issue lo.ToPtr(issuerProfile.OIDCConfig.PreAuthorizedGrantAnonymousAccessSupported) } - return final + return final, nil } func (s *Service) signIssuerMetadata( diff --git a/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go b/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go new file mode 100644 index 000000000..5e9a2278a --- /dev/null +++ b/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go @@ -0,0 +1,83 @@ +package dynamicwellknown + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/redis/go-redis/v9" + + profileapi "github.com/trustbloc/vcs/pkg/profile" +) + +const ( + keyPrefix = "dynamic_well_known" +) + +// Store stores claim data with expiration. +type Store struct { + redisClient redisClient + defaultTTL time.Duration +} + +// New creates presentation claims store. +func New(redisClient redisClient, ttl time.Duration) *Store { + return &Store{ + redisClient: redisClient, + defaultTTL: ttl, + } +} + +func (s *Store) Upsert( + ctx context.Context, + profileID string, + item map[string]*profileapi.CredentialsConfigurationSupported, +) error { + currentValue, err := s.Get(ctx, profileID) + if err != nil { + return err + } + + if currentValue == nil { + currentValue = make(map[string]*profileapi.CredentialsConfigurationSupported) + } + + for k, v := range item { + currentValue[k] = v + } + + b, err := json.Marshal(currentValue) + if err != nil { + return err + } + + if err = s.redisClient.API().Set(ctx, s.resolveRedisKey(profileID), string(b), s.defaultTTL).Err(); err != nil { + return err + } + + return nil +} + +func (s *Store) Get(ctx context.Context, id string) (map[string]*profileapi.CredentialsConfigurationSupported, error) { + b, err := s.redisClient.API().Get(ctx, s.resolveRedisKey(id)).Bytes() + if err != nil { + if errors.Is(err, redis.Nil) { + return nil, nil + } + + return nil, err + } + + var result map[string]*profileapi.CredentialsConfigurationSupported + if err = json.Unmarshal(b, &result); err != nil { + return nil, err + } + + return result, nil +} + +func (s *Store) resolveRedisKey(id string) string { + return fmt.Sprintf("%s-%s", keyPrefix, id) +} diff --git a/pkg/storage/redis/dynamicwellknown/interfaces.go b/pkg/storage/redis/dynamicwellknown/interfaces.go new file mode 100644 index 000000000..1def233ca --- /dev/null +++ b/pkg/storage/redis/dynamicwellknown/interfaces.go @@ -0,0 +1,14 @@ +package dynamicwellknown + +import redisapi "github.com/redis/go-redis/v9" + +//go:generate mockgen -destination interfaces_mocks_test.go -package dynamicwellknown_test -source=interfaces.go + +type redisClient interface { + API() redisapi.UniversalClient +} + +// nolint +type redisApi interface { + redisapi.UniversalClient +} diff --git a/test/bdd/attestation/go.mod b/test/bdd/attestation/go.mod index 42f39a5a6..c9bc590ff 100644 --- a/test/bdd/attestation/go.mod +++ b/test/bdd/attestation/go.mod @@ -11,6 +11,7 @@ toolchain go1.22.4 require ( github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 + github.com/samber/lo v1.47.0 github.com/trustbloc/cmdutil-go v1.0.0 github.com/trustbloc/did-go v1.3.1-0.20241028131019-014935534038 github.com/trustbloc/vc-go v1.2.1-0.20241031140324-d25fb970e6f5 @@ -43,7 +44,6 @@ require ( github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pquerna/cachecontrol v0.1.0 // indirect - github.com/samber/lo v1.47.0 // indirect github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 // indirect github.com/tidwall/gjson v1.14.3 // indirect github.com/tidwall/match v1.1.1 // indirect diff --git a/test/bdd/features/oidc4vc_api.feature b/test/bdd/features/oidc4vc_api.feature index 97a7fded8..d05409d3a 100644 --- a/test/bdd/features/oidc4vc_api.feature +++ b/test/bdd/features/oidc4vc_api.feature @@ -294,8 +294,9 @@ Feature: OIDC4VC REST API Examples: | issuerProfile | credentialType | credentialTemplate | verifierProfile | presentationDefinitionID | fields | proofType | credentialEncoded | - | acme_issuer/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | - | acme_issuer_no_template/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | +# | acme_issuer/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | +# | acme_issuer_no_template/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | + | playground_issuer/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | @oidc4vc_rest_pre_auth_flow_compose_with_attachment Scenario Outline: OIDC credential issuance and verification Pre Auth flow (attachment evidence) diff --git a/test/bdd/fixtures/profile/profiles.json b/test/bdd/fixtures/profile/profiles.json index 560159d9f..f32efc8ef 100644 --- a/test/bdd/fixtures/profile/profiles.json +++ b/test/bdd/fixtures/profile/profiles.json @@ -134,6 +134,59 @@ } }, "createDID": true + },{ + "issuer": { + "id": "playground_issuer", + "version": "v1.0", + "groupID": "playground_issuer", + "name": "Acme Issuer", + "organizationID": "00000000-0000-0000-0000-000000000001", + "url": "http://vc-rest-echo.trustbloc.local:8075", + "webHook": "http://vcs.webhook.example.com:8180", + "active": true, + "vcConfig": { + "refreshServiceEnabled": false, + "signingAlgorithm": "Ed25519Signature2020", + "signatureRepresentation": 0, + "keyType": "ED25519", + "format": "ldp", + "didMethod": "ion", + "status": { + "type": "StatusList2021Entry" + }, + "sdjwt": { + "enable": false + } + }, + "oidcConfig": { + "client_id": "7d4u50e7w6nfq8tfayhzplgjf", + "client_secret_handle": "282ks4fkuqfosus5k0x30abnv", + "issuer_well_known": "http://cognito-mock.trustbloc.local:9229/local_5a9GzRvB/.well-known/openid-configuration", + "scopes_supported": [ + "openid", + "profile" + ], + "grant_types_supported": [ + "authorization_code", + "urn:ietf:params:oauth:grant-type:pre-authorized_code" + ], + "response_types_supported": [ + "code" + ], + "token_endpoint_auth_methods_supported": [ + "none" + ], + "enable_dynamic_client_registration": true, + "enable_discoverable_client_id_scheme": true, + "pre-authorized_grant_anonymous_access_supported": true, + "wallet_initiated_auth_flow_supported": true, + "dynamic_well_known_supported" : true, + "claims_endpoint": "https://mock-login-consent.example.com:8099/claim-data?credentialType=UniversityDegreeCredential" + }, + "credentialTemplates": [], + "credentialMetadata": {} + }, + "createDID": true }, { "issuer": { diff --git a/test/bdd/pkg/v1/oidc4vc/oidc4vci.go b/test/bdd/pkg/v1/oidc4vc/oidc4vci.go index a0b803a30..0d9e88a69 100644 --- a/test/bdd/pkg/v1/oidc4vc/oidc4vci.go +++ b/test/bdd/pkg/v1/oidc4vc/oidc4vci.go @@ -148,7 +148,7 @@ func (s *Steps) runOIDC4VCIPreAuth(initiateOIDC4CIResponseData initiateOIDC4VCIR opts = append(opts, options...) - if !s.useCredentialOfferCredConfigIDForCredentialRequest { + if !s.useCredentialOfferCredConfigIDForCredentialRequest && !s.issuerProfile.OIDCConfig.DynamicWellKnownSupported { credentialTypes := strings.Split(s.issuedCredentialType, ",") // Set option filters From 29541004a19118b8109ba27a92fa6b1744320b78 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Wed, 6 Nov 2024 13:34:50 +0100 Subject: [PATCH 2/8] feat: update vc --- test/bdd/fixtures/profile/profiles.json | 7 +++++++ test/bdd/go.mod | 1 - test/bdd/go.sum | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/test/bdd/fixtures/profile/profiles.json b/test/bdd/fixtures/profile/profiles.json index f32efc8ef..c1db11fdf 100644 --- a/test/bdd/fixtures/profile/profiles.json +++ b/test/bdd/fixtures/profile/profiles.json @@ -156,6 +156,10 @@ }, "sdjwt": { "enable": false + }, + "dataIntegrityProof": { + "enable": true, + "suiteType": "eddsa-rdfc-2022" } }, "oidcConfig": { @@ -183,6 +187,9 @@ "dynamic_well_known_supported" : true, "claims_endpoint": "https://mock-login-consent.example.com:8099/claim-data?credentialType=UniversityDegreeCredential" }, + "kmsConfig" : { + "kmsType" : "local" + }, "credentialTemplates": [], "credentialMetadata": {} }, diff --git a/test/bdd/go.mod b/test/bdd/go.mod index 6e05c333f..35c4ce57f 100644 --- a/test/bdd/go.mod +++ b/test/bdd/go.mod @@ -32,7 +32,6 @@ require ( github.com/trustbloc/vc-go v1.2.1-0.20241031140324-d25fb970e6f5 github.com/trustbloc/vcs v0.1.9-0.20230210204445-f2870a36f0ea github.com/trustbloc/vcs/component/wallet-cli v0.0.0-20240103173902-7fbe030659b2 - github.com/trustbloc/vcs/test/stress v0.0.0-00010101000000-000000000000 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 go.opentelemetry.io/otel/trace v1.29.0 go.uber.org/zap v1.27.0 diff --git a/test/bdd/go.sum b/test/bdd/go.sum index d6ec6320f..22ea32768 100644 --- a/test/bdd/go.sum +++ b/test/bdd/go.sum @@ -792,6 +792,8 @@ github.com/trustbloc/sidetree-go v1.0.1-0.20240219121130-f4260aff7104 h1:0IW4mua github.com/trustbloc/sidetree-go v1.0.1-0.20240219121130-f4260aff7104/go.mod h1:3yChjB5KOT7B9eZe0W1XaIx3MNUuC1Oe9nR/GCtI1W0= github.com/trustbloc/vc-go v1.2.1-0.20241031140324-d25fb970e6f5 h1:p3dmAih8yucca6pLMeRwjzHHcX1k5g4KbreH329doBw= github.com/trustbloc/vc-go v1.2.1-0.20241031140324-d25fb970e6f5/go.mod h1:3/GbrzF7phN+SxBTZaUBS6VxnoxpXGBUjjk3Eg4ImUk= +github.com/trustbloc/vc-go v1.2.1-0.20241106122245-51c805275bfc h1:YbH7BthyCr3hipCPxVkUxMVdo2HK7xKc1fPX+vtbXUE= +github.com/trustbloc/vc-go v1.2.1-0.20241106122245-51c805275bfc/go.mod h1:3/GbrzF7phN+SxBTZaUBS6VxnoxpXGBUjjk3Eg4ImUk= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= From 02e6a0a0bbcf9416188e3c32cc1023148a33f51f Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Wed, 6 Nov 2024 13:39:50 +0100 Subject: [PATCH 3/8] feat: cleanup --- .../gomock_reflect_3569893530/prog.go | 70 ---------------- .../oidc4ci/gomock_reflect_2238258269/prog.go | 82 ------------------- .../oidc4ci/gomock_reflect_784313643/prog.go | 82 ------------------- test/bdd/features/oidc4vc_api.feature | 4 +- 4 files changed, 2 insertions(+), 236 deletions(-) delete mode 100644 pkg/restapi/handlers/gomock_reflect_3569893530/prog.go delete mode 100644 pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go delete mode 100644 pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go diff --git a/pkg/restapi/handlers/gomock_reflect_3569893530/prog.go b/pkg/restapi/handlers/gomock_reflect_3569893530/prog.go deleted file mode 100644 index 02adc13a7..000000000 --- a/pkg/restapi/handlers/gomock_reflect_3569893530/prog.go +++ /dev/null @@ -1,70 +0,0 @@ - -package main - -import ( - "encoding/gob" - "flag" - "fmt" - "os" - "path" - "reflect" - - "github.com/golang/mock/mockgen/model" - - pkg_ "github.com/ory/fosite/handler/oauth2" -) - -var output = flag.String("output", "", "The output file name, or empty to use stdout.") - -func main() { - flag.Parse() - - its := []struct{ - sym string - typ reflect.Type - }{ - - { "CoreStorage", reflect.TypeOf((*pkg_.CoreStorage)(nil)).Elem()}, - - { "AccessTokenStrategy", reflect.TypeOf((*pkg_.AccessTokenStrategy)(nil)).Elem()}, - - { "RefreshTokenStrategy", reflect.TypeOf((*pkg_.RefreshTokenStrategy)(nil)).Elem()}, - - } - pkg := &model.Package{ - // NOTE: This behaves contrary to documented behaviour if the - // package name is not the final component of the import path. - // The reflect package doesn't expose the package name, though. - Name: path.Base("github.com/ory/fosite/handler/oauth2"), - } - - for _, it := range its { - intf, err := model.InterfaceFromInterfaceType(it.typ) - if err != nil { - fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) - os.Exit(1) - } - intf.Name = it.sym - pkg.Interfaces = append(pkg.Interfaces, intf) - } - - outfile := os.Stdout - if len(*output) != 0 { - var err error - outfile, err = os.Create(*output) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) - } - defer func() { - if err := outfile.Close(); err != nil { - fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) - os.Exit(1) - } - }() - } - - if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { - fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) - os.Exit(1) - } -} diff --git a/pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go b/pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go deleted file mode 100644 index dca6eea89..000000000 --- a/pkg/restapi/v1/oidc4ci/gomock_reflect_2238258269/prog.go +++ /dev/null @@ -1,82 +0,0 @@ - -package main - -import ( - "encoding/gob" - "flag" - "fmt" - "os" - "path" - "reflect" - - "github.com/golang/mock/mockgen/model" - - pkg_ "github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci" -) - -var output = flag.String("output", "", "The output file name, or empty to use stdout.") - -func main() { - flag.Parse() - - its := []struct{ - sym string - typ reflect.Type - }{ - - { "StateStore", reflect.TypeOf((*pkg_.StateStore)(nil)).Elem()}, - - { "OAuth2Provider", reflect.TypeOf((*pkg_.OAuth2Provider)(nil)).Elem()}, - - { "IssuerInteractionClient", reflect.TypeOf((*pkg_.IssuerInteractionClient)(nil)).Elem()}, - - { "HTTPClient", reflect.TypeOf((*pkg_.HTTPClient)(nil)).Elem()}, - - { "ClientManager", reflect.TypeOf((*pkg_.ClientManager)(nil)).Elem()}, - - { "ProfileService", reflect.TypeOf((*pkg_.ProfileService)(nil)).Elem()}, - - { "AckService", reflect.TypeOf((*pkg_.AckService)(nil)).Elem()}, - - { "CwtProofChecker", reflect.TypeOf((*pkg_.CwtProofChecker)(nil)).Elem()}, - - { "LDPProofParser", reflect.TypeOf((*pkg_.LDPProofParser)(nil)).Elem()}, - - } - pkg := &model.Package{ - // NOTE: This behaves contrary to documented behaviour if the - // package name is not the final component of the import path. - // The reflect package doesn't expose the package name, though. - Name: path.Base("github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci"), - } - - for _, it := range its { - intf, err := model.InterfaceFromInterfaceType(it.typ) - if err != nil { - fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) - os.Exit(1) - } - intf.Name = it.sym - pkg.Interfaces = append(pkg.Interfaces, intf) - } - - outfile := os.Stdout - if len(*output) != 0 { - var err error - outfile, err = os.Create(*output) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) - } - defer func() { - if err := outfile.Close(); err != nil { - fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) - os.Exit(1) - } - }() - } - - if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { - fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) - os.Exit(1) - } -} diff --git a/pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go b/pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go deleted file mode 100644 index dca6eea89..000000000 --- a/pkg/restapi/v1/oidc4ci/gomock_reflect_784313643/prog.go +++ /dev/null @@ -1,82 +0,0 @@ - -package main - -import ( - "encoding/gob" - "flag" - "fmt" - "os" - "path" - "reflect" - - "github.com/golang/mock/mockgen/model" - - pkg_ "github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci" -) - -var output = flag.String("output", "", "The output file name, or empty to use stdout.") - -func main() { - flag.Parse() - - its := []struct{ - sym string - typ reflect.Type - }{ - - { "StateStore", reflect.TypeOf((*pkg_.StateStore)(nil)).Elem()}, - - { "OAuth2Provider", reflect.TypeOf((*pkg_.OAuth2Provider)(nil)).Elem()}, - - { "IssuerInteractionClient", reflect.TypeOf((*pkg_.IssuerInteractionClient)(nil)).Elem()}, - - { "HTTPClient", reflect.TypeOf((*pkg_.HTTPClient)(nil)).Elem()}, - - { "ClientManager", reflect.TypeOf((*pkg_.ClientManager)(nil)).Elem()}, - - { "ProfileService", reflect.TypeOf((*pkg_.ProfileService)(nil)).Elem()}, - - { "AckService", reflect.TypeOf((*pkg_.AckService)(nil)).Elem()}, - - { "CwtProofChecker", reflect.TypeOf((*pkg_.CwtProofChecker)(nil)).Elem()}, - - { "LDPProofParser", reflect.TypeOf((*pkg_.LDPProofParser)(nil)).Elem()}, - - } - pkg := &model.Package{ - // NOTE: This behaves contrary to documented behaviour if the - // package name is not the final component of the import path. - // The reflect package doesn't expose the package name, though. - Name: path.Base("github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci"), - } - - for _, it := range its { - intf, err := model.InterfaceFromInterfaceType(it.typ) - if err != nil { - fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) - os.Exit(1) - } - intf.Name = it.sym - pkg.Interfaces = append(pkg.Interfaces, intf) - } - - outfile := os.Stdout - if len(*output) != 0 { - var err error - outfile, err = os.Create(*output) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to open output file %q", *output) - } - defer func() { - if err := outfile.Close(); err != nil { - fmt.Fprintf(os.Stderr, "failed to close output file %q", *output) - os.Exit(1) - } - }() - } - - if err := gob.NewEncoder(outfile).Encode(pkg); err != nil { - fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) - os.Exit(1) - } -} diff --git a/test/bdd/features/oidc4vc_api.feature b/test/bdd/features/oidc4vc_api.feature index d05409d3a..74b6cb43a 100644 --- a/test/bdd/features/oidc4vc_api.feature +++ b/test/bdd/features/oidc4vc_api.feature @@ -294,8 +294,8 @@ Feature: OIDC4VC REST API Examples: | issuerProfile | credentialType | credentialTemplate | verifierProfile | presentationDefinitionID | fields | proofType | credentialEncoded | -# | acme_issuer/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | -# | acme_issuer_no_template/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | + | acme_issuer/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | + | acme_issuer_no_template/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | | playground_issuer/v1.0 | UniversityDegreeCredential | | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id | jwt | ewogICJAY29udGV4dCI6IFsKICAgICJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsCiAgICAiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjEiLAogICAgImh0dHBzOi8vdzNpZC5vcmcvdmMvc3RhdHVzLWxpc3QvMjAyMS92MSIKICBdLAogICJjcmVkZW50aWFsU3RhdHVzIjogewogICAgImlkIjogInVybjp1dWlkOjViYjIwNTU4LTI1Y2UtNDBiNS04MGZjLThhZjY5MWVlZGNjZSIsCiAgICAic3RhdHVzTGlzdENyZWRlbnRpYWwiOiAiaHR0cDovL3ZjLXJlc3QtZWNoby50cnVzdGJsb2MubG9jYWw6ODA3NS9pc3N1ZXIvZ3JvdXBzL2dyb3VwX2FjbWVfaXNzdWVyL2NyZWRlbnRpYWxzL3N0YXR1cy8wNmIxM2U5Mi0yN2E2LTRiNzYtOTk3Ny02ZWNlMjA3NzgzZGQiLAogICAgInN0YXR1c0xpc3RJbmRleCI6ICI2NTQ4IiwKICAgICJzdGF0dXNQdXJwb3NlIjogInJldm9jYXRpb24iLAogICAgInR5cGUiOiAiU3RhdHVzTGlzdDIwMjFFbnRyeSIKICB9LAogICJjcmVkZW50aWFsU3ViamVjdCI6IHsKICAgICJkZWdyZWUiOiB7CiAgICAgICJkZWdyZWUiOiAiTUlUIiwKICAgICAgInR5cGUiOiAiQmFjaGVsb3JEZWdyZWUiCiAgICB9LAogICAgImlkIjogImRpZDppb246RWlCeVFBeFhtT0FFTTdUbEVVRzl1NlJyMnJzNVFDTHh1T2ZWbE5ONXpUM1JtUTpleUprWld4MFlTSTZleUp3WVhSamFHVnpJanBiZXlKaFkzUnBiMjRpT2lKaFpHUXRjSFZpYkdsakxXdGxlWE1pTENKd2RXSnNhV05MWlhseklqcGJleUpwWkNJNklsOXVWVGx5YmtGcGNIZzNNRmhYU0RSS2RVcDNUMVZwV2paNFNVWk5ZVEZuUm1oS1VWVXdWSFoyTTFFaUxDSndkV0pzYVdOTFpYbEtkMnNpT25zaVkzSjJJam9pVUMwek9EUWlMQ0pyYVdRaU9pSmZibFU1Y201QmFYQjROekJZVjBnMFNuVktkMDlWYVZvMmVFbEdUV0V4WjBab1NsRlZNRlIyZGpOUklpd2lhM1I1SWpvaVJVTWlMQ0o0SWpvaU5sYzNkSEZhTVhabFRUZFpTRTU1ZEROME9YaE1PVGRNVDBoelp6a3libTV3WDJSc2FXTlFjVlJsY2pSdFNYaFplVFZ4TTA1MmVVa3djekkxV2tsRk9DSXNJbmtpT2lKRGExUlhaaTFZVlZwNkxWZDJOekl0TlhCdk9XZHBjblo2U25kNE1uRlRRMGt5Wm1GNWJIZFhjbXR0YW5wWE16RkVjV0ZIY2paRldrUXRkVmxEVTJkT0luMHNJbkIxY25CdmMyVnpJanBiSW1GMWRHaGxiblJwWTJGMGFXOXVJaXdpWVhOelpYSjBhVzl1VFdWMGFHOWtJbDBzSW5SNWNHVWlPaUpLYzI5dVYyVmlTMlY1TWpBeU1DSjlYWDFkTENKMWNHUmhkR1ZEYjIxdGFYUnRaVzUwSWpvaVJXbERNbWh0YzJSRk5IRk9kMjFUUTFCbU1qRjFNRXg2UWtodmMzQldVRkZzT1ZKdFRHczNORloxWnpWcmR5SjlMQ0p6ZFdabWFYaEVZWFJoSWpwN0ltUmxiSFJoU0dGemFDSTZJa1ZwUVMxWk9YVjBUVTVoYVVoVlIxcHROVEJtUVZKamNWTmFjVWRCTXpkNlFsWlVOVVk0UzBsdFh6TlRka0VpTENKeVpXTnZkbVZ5ZVVOdmJXMXBkRzFsYm5RaU9pSkZhVUpSYlVOaWRsOVZRelZSUmtOMk9VTndiMUpQYVZNNWJIWkNkbmR1WTJsWkxWOTRjbEU0U1RKdlIwSm5JbjBzSW5SNWNHVWlPaUpqY21WaGRHVWlmUSIsCiAgICAibmFtZSI6ICJKYXlkZW4gRG9lIiwKICAgICJzcG91c2UiOiAiZGlkOmV4YW1wbGU6YzI3NmUxMmVjMjFlYmZlYjFmNzEyZWJjNmYxIgogIH0sCiAgImV4cGlyYXRpb25EYXRlIjogIjIwMjUtMDMtMThUMjI6MDY6MzEuMzQyNTAxMDZaIiwKICAiaWQiOiAidXJuOnV1aWQ6N2NiYjliYTItMzgwMS00Nzg2LWI3YzEtYWQyZDQyOTI0M2E4IiwKICAiaXNzdWFuY2VEYXRlIjogIjIwMjQtMDMtMThUMjI6MDY6MzEuMzc1NDM0MTMxWiIsCiAgImlzc3VlciI6IHsKICAgICJpZCI6ICJkaWQ6aW9uOkVpQW9hSEpZRjJRNms5eml5aDRQRHJ1c0ladE5VS20xLWFGckF2clBEQ0VNU1E6ZXlKa1pXeDBZU0k2ZXlKd1lYUmphR1Z6SWpwYmV5SmhZM1JwYjI0aU9pSmhaR1F0Y0hWaWJHbGpMV3RsZVhNaUxDSndkV0pzYVdOTFpYbHpJanBiZXlKcFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW5CMVlteHBZMHRsZVVwM2F5STZleUpsSWpvaVFWRkJRaUlzSW10cFpDSTZJbU0xTkdRME1qZzJMV0l5TmpNdE5ESTNNQzFoTURNeExUVXlPVEppWXpSak5qUmpaQ0lzSW10MGVTSTZJbEpUUVNJc0ltNGlPaUp1V1RCaU5tRkdURTVWYW1RM2VFRnBiVnA0ZFVnd1prNVNUVWN6WldOSFdHOVpTM1JSV0RSMVMxRmpjMWxaVTI1R2FFRkRlRGhtYW5Cbk5qSkplVnBDZFZSUVlUWnVaM04yWDA1UlFraFVXVWxNUmpKUldWVlFhVFJRVkVzdGVVRTNUbVJQU2xad05VRkZWelZ5YzNsc1pUZ3dTeTFQVUdsVVVXMUtSMng1VWpoSlVrWklOazl0TTNNMllYTnRXVmw1WmtrdExXdFJaVGhJYTBkMGJYYzNUR2xpVldWWlduZDNSemxJU1d4NlNEUlFObWhJVDNOVVpsaGhSRmwyYkRVM1dXcDFibWd5Y1ZVeVlXbFhSRms1UVdsVVVrSnBiVXhDYUVsdVpuWTBOVTVZZVRsMWQzSkdPWE5aYkZodWJHZDViVlZUTVZGV2NUYzVWUzFsYmt0bVJYTXpSMnMyUlVSaVYzSkhiWHBIY0hSUFpHcHpkRWt3T1dWNGFWUklPRXRyZG5oT1JXaFFla3hrZW5rd1VGTmxSV1o0WDJsVFdXeE1aRmxKUmtWVU4yb3hNVTV2TFRGeU1rVjZRa05wVVRWdWRGSXhNMUVpZlN3aWNIVnljRzl6WlhNaU9sc2lZWFYwYUdWdWRHbGpZWFJwYjI0aUxDSmhjM05sY25ScGIyNU5aWFJvYjJRaVhTd2lkSGx3WlNJNklrcHpiMjVYWldKTFpYa3lNREl3SW4xZGZWMHNJblZ3WkdGMFpVTnZiVzFwZEcxbGJuUWlPaUpGYVVORVJsRlFabmhTUm5Oc2NrRkliVm80YVhOUWJFNDRRelpLVW1SemVuUTBabkkwYURsSU5HUm1NVlpuSW4wc0luTjFabVpwZUVSaGRHRWlPbnNpWkdWc2RHRklZWE5vSWpvaVJXbEJTRE14TkZkcVJUTkJhVFJoTFhNeVNtOUhjWFpqUzFWcFRFUmZNVEpWWjNWeFkwWTFiVWR6Um5aMFp5SXNJbkpsWTI5MlpYSjVRMjl0YldsMGJXVnVkQ0k2SWtWcFFTMXBka1pxVFRaTGRteDJZVXRVT0dkQ2EzQkphelpMWmt4dk9VbDVNR28wWVdobWFpMXJUMGwyV2xFaWZTd2lkSGx3WlNJNkltTnlaV0YwWlNKOSIsCiAgICAibmFtZSI6ICJBY21lIElzc3VlciIKICB9LAogICJ0eXBlIjogWwogICAgIlZlcmlmaWFibGVDcmVkZW50aWFsIiwKICAgICJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCIKICBdCn0= | @oidc4vc_rest_pre_auth_flow_compose_with_attachment From a06875ae5b49046a1c12ad8ad3b06b5d20031ddb Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Wed, 6 Nov 2024 14:00:33 +0100 Subject: [PATCH 4/8] fix: lint --- pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go | 4 ---- pkg/storage/redis/dynamicwellknown/dynamicwellknown.go | 8 ++------ test/bdd/go.mod | 1 + test/bdd/go.sum | 2 -- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go index afc741e67..0ddde1ae9 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go @@ -220,10 +220,6 @@ func (s *Service) newTxCredentialConf( return nil, err } - //profileMeta := profile.CredentialMetaData - - //metaCredentialConfiguration := profileMeta.CredentialsConfigurationSupported[credentialConfigurationID] - txCredentialConfiguration := &issuecredential.TxCredentialConfiguration{ ID: uuid.NewString(), CredentialTemplate: targetCredentialTemplate, diff --git a/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go b/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go index 5e9a2278a..e623e8b06 100644 --- a/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go +++ b/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go @@ -53,18 +53,14 @@ func (s *Store) Upsert( return err } - if err = s.redisClient.API().Set(ctx, s.resolveRedisKey(profileID), string(b), s.defaultTTL).Err(); err != nil { - return err - } - - return nil + return s.redisClient.API().Set(ctx, s.resolveRedisKey(profileID), string(b), s.defaultTTL).Err() } func (s *Store) Get(ctx context.Context, id string) (map[string]*profileapi.CredentialsConfigurationSupported, error) { b, err := s.redisClient.API().Get(ctx, s.resolveRedisKey(id)).Bytes() if err != nil { if errors.Is(err, redis.Nil) { - return nil, nil + return map[string]*profileapi.CredentialsConfigurationSupported{}, nil } return nil, err diff --git a/test/bdd/go.mod b/test/bdd/go.mod index 35c4ce57f..6e05c333f 100644 --- a/test/bdd/go.mod +++ b/test/bdd/go.mod @@ -32,6 +32,7 @@ require ( github.com/trustbloc/vc-go v1.2.1-0.20241031140324-d25fb970e6f5 github.com/trustbloc/vcs v0.1.9-0.20230210204445-f2870a36f0ea github.com/trustbloc/vcs/component/wallet-cli v0.0.0-20240103173902-7fbe030659b2 + github.com/trustbloc/vcs/test/stress v0.0.0-00010101000000-000000000000 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 go.opentelemetry.io/otel/trace v1.29.0 go.uber.org/zap v1.27.0 diff --git a/test/bdd/go.sum b/test/bdd/go.sum index 22ea32768..d6ec6320f 100644 --- a/test/bdd/go.sum +++ b/test/bdd/go.sum @@ -792,8 +792,6 @@ github.com/trustbloc/sidetree-go v1.0.1-0.20240219121130-f4260aff7104 h1:0IW4mua github.com/trustbloc/sidetree-go v1.0.1-0.20240219121130-f4260aff7104/go.mod h1:3yChjB5KOT7B9eZe0W1XaIx3MNUuC1Oe9nR/GCtI1W0= github.com/trustbloc/vc-go v1.2.1-0.20241031140324-d25fb970e6f5 h1:p3dmAih8yucca6pLMeRwjzHHcX1k5g4KbreH329doBw= github.com/trustbloc/vc-go v1.2.1-0.20241031140324-d25fb970e6f5/go.mod h1:3/GbrzF7phN+SxBTZaUBS6VxnoxpXGBUjjk3Eg4ImUk= -github.com/trustbloc/vc-go v1.2.1-0.20241106122245-51c805275bfc h1:YbH7BthyCr3hipCPxVkUxMVdo2HK7xKc1fPX+vtbXUE= -github.com/trustbloc/vc-go v1.2.1-0.20241106122245-51c805275bfc/go.mod h1:3/GbrzF7phN+SxBTZaUBS6VxnoxpXGBUjjk3Eg4ImUk= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= From af43bfed7ccf5e5379fbb6977f8f291f934c8c94 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Wed, 6 Nov 2024 16:26:00 +0100 Subject: [PATCH 5/8] feat: add wellknown --- pkg/service/oidc4ci/oidc4ci_service.go | 7 -- pkg/service/wellknown/provider/api.go | 6 ++ pkg/service/wellknown/provider/interfaces.go | 6 ++ .../dynamicwellknown/dynamicwellknown.go | 12 ++- .../dynamicwellknown/dynamicwellknown_test.go | 85 +++++++++++++++++++ .../redis/dynamicwellknown/interfaces.go | 6 ++ 6 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 pkg/storage/redis/dynamicwellknown/dynamicwellknown_test.go diff --git a/pkg/service/oidc4ci/oidc4ci_service.go b/pkg/service/oidc4ci/oidc4ci_service.go index ee40812e6..6834271b8 100644 --- a/pkg/service/oidc4ci/oidc4ci_service.go +++ b/pkg/service/oidc4ci/oidc4ci_service.go @@ -783,13 +783,6 @@ func (s *Service) PrepareCredential( //nolint:funlen return nil, err } - id := requestedTxCredentialConfigurationIDs - b, _ := json.Marshal(id) - logger.Info(fmt.Sprintf("requestedTxCredentialConfigurationIDs : %v", string(b))) - - b1, _ := json.Marshal(tx) - logger.Info(fmt.Sprintf("tx : %v", string(b1))) - requestedTxCredentialConfigurationIDs[txCredentialConfiguration.ID] = struct{}{} cred, ackID, prepareCredError := s.prepareCredential(ctx, tx, txCredentialConfiguration, requestedCredential) diff --git a/pkg/service/wellknown/provider/api.go b/pkg/service/wellknown/provider/api.go index 7b15bfe86..fc3a5c4d6 100644 --- a/pkg/service/wellknown/provider/api.go +++ b/pkg/service/wellknown/provider/api.go @@ -1,3 +1,9 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + package provider type DynamicWellKnownStore dynamicWellKnownStore diff --git a/pkg/service/wellknown/provider/interfaces.go b/pkg/service/wellknown/provider/interfaces.go index 4c06d3771..3922007ef 100644 --- a/pkg/service/wellknown/provider/interfaces.go +++ b/pkg/service/wellknown/provider/interfaces.go @@ -1,3 +1,9 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + package provider import ( diff --git a/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go b/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go index e623e8b06..40fc7b3a5 100644 --- a/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go +++ b/pkg/storage/redis/dynamicwellknown/dynamicwellknown.go @@ -1,3 +1,9 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + package dynamicwellknown import ( @@ -66,6 +72,10 @@ func (s *Store) Get(ctx context.Context, id string) (map[string]*profileapi.Cred return nil, err } + if len(b) == 0 { + return map[string]*profileapi.CredentialsConfigurationSupported{}, nil + } + var result map[string]*profileapi.CredentialsConfigurationSupported if err = json.Unmarshal(b, &result); err != nil { return nil, err @@ -75,5 +85,5 @@ func (s *Store) Get(ctx context.Context, id string) (map[string]*profileapi.Cred } func (s *Store) resolveRedisKey(id string) string { - return fmt.Sprintf("%s-%s", keyPrefix, id) + return fmt.Sprintf("%s:%s", keyPrefix, id) } diff --git a/pkg/storage/redis/dynamicwellknown/dynamicwellknown_test.go b/pkg/storage/redis/dynamicwellknown/dynamicwellknown_test.go new file mode 100644 index 000000000..7fbb017b3 --- /dev/null +++ b/pkg/storage/redis/dynamicwellknown/dynamicwellknown_test.go @@ -0,0 +1,85 @@ +package dynamicwellknown_test + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/golang/mock/gomock" + redisapi "github.com/redis/go-redis/v9" + "github.com/stretchr/testify/assert" + + profileapi "github.com/trustbloc/vcs/pkg/profile" + "github.com/trustbloc/vcs/pkg/storage/redis/dynamicwellknown" +) + +func TestUpsert(t *testing.T) { + t.Run("no existing", func(t *testing.T) { + cl := NewMockredisClient(gomock.NewController(t)) + api := NewMockredisApi(gomock.NewController(t)) + + cl.EXPECT().API().Return(api).AnyTimes() + + store := dynamicwellknown.New(cl, time.Hour) + + api.EXPECT().Get(context.TODO(), "dynamic_well_known:1234").Return( + redisapi.NewStringResult("", nil)) + + api.EXPECT().Set(gomock.Any(), "dynamic_well_known:1234", gomock.Any(), time.Hour). + Return(redisapi.NewStatusResult("", nil)) + + assert.NoError(t, store.Upsert(context.TODO(), "1234", + map[string]*profileapi.CredentialsConfigurationSupported{ + "key1": {}, + }), + ) + }) + + t.Run("err get", func(t *testing.T) { + cl := NewMockredisClient(gomock.NewController(t)) + api := NewMockredisApi(gomock.NewController(t)) + + cl.EXPECT().API().Return(api).AnyTimes() + + store := dynamicwellknown.New(cl, time.Hour) + + api.EXPECT().Get(context.TODO(), "dynamic_well_known:1234").Return( + redisapi.NewStringResult("", errors.New("unexpected err"))) + + assert.ErrorContains(t, store.Upsert(context.TODO(), "1234", + map[string]*profileapi.CredentialsConfigurationSupported{ + "key1": {}, + }), "unexpected err") + }) + + t.Run("not found err", func(t *testing.T) { + cl := NewMockredisClient(gomock.NewController(t)) + api := NewMockredisApi(gomock.NewController(t)) + + cl.EXPECT().API().Return(api).AnyTimes() + + store := dynamicwellknown.New(cl, time.Hour) + api.EXPECT().Get(context.TODO(), "dynamic_well_known:1234").Return( + redisapi.NewStringResult("", redisapi.Nil)) + + resp, err := store.Get(context.TODO(), "1234") + assert.NoError(t, err) + assert.NotNil(t, resp) + }) + + t.Run("err", func(t *testing.T) { + cl := NewMockredisClient(gomock.NewController(t)) + api := NewMockredisApi(gomock.NewController(t)) + + cl.EXPECT().API().Return(api).AnyTimes() + + store := dynamicwellknown.New(cl, time.Hour) + api.EXPECT().Get(context.TODO(), "dynamic_well_known:1234").Return( + redisapi.NewStringResult("", errors.New("unexpected err"))) + + resp, err := store.Get(context.TODO(), "1234") + assert.ErrorContains(t, err, "unexpected err") + assert.Nil(t, resp) + }) +} diff --git a/pkg/storage/redis/dynamicwellknown/interfaces.go b/pkg/storage/redis/dynamicwellknown/interfaces.go index 1def233ca..72065e77e 100644 --- a/pkg/storage/redis/dynamicwellknown/interfaces.go +++ b/pkg/storage/redis/dynamicwellknown/interfaces.go @@ -1,3 +1,9 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + package dynamicwellknown import redisapi "github.com/redis/go-redis/v9" From bcbe39f9e2d891618e3d06601738c7fc501acae2 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Wed, 6 Nov 2024 20:32:47 +0100 Subject: [PATCH 6/8] feat: add more unit tests --- pkg/restapi/v1/common/common_test.go | 17 ++++ .../oidc4ci_service_initiate_issuance_test.go | 39 ++++++++ pkg/service/wellknown/provider/interfaces.go | 2 +- .../provider/wellknown_service_test.go | 88 +++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) diff --git a/pkg/restapi/v1/common/common_test.go b/pkg/restapi/v1/common/common_test.go index c8969d09c..c534db86b 100644 --- a/pkg/restapi/v1/common/common_test.go +++ b/pkg/restapi/v1/common/common_test.go @@ -221,6 +221,23 @@ func TestValidateVPFormat(t *testing.T) { require.Error(t, err) } +func TestMapVcFormat(t *testing.T) { + got, err := MapToVCFormat(vcsverifiable.Jwt) + require.NoError(t, err) + require.EqualValues(t, JwtVcJson, got) + + got, err = MapToVCFormat(vcsverifiable.Ldp) + require.NoError(t, err) + require.EqualValues(t, JwtVcJsonLd, got) + + got, err = MapToVCFormat(vcsverifiable.Cwt) + require.NoError(t, err) + require.EqualValues(t, CwtVcLd, got) + + _, err = MapToVCFormat("invalid") + require.Error(t, err) +} + func TestValidateDIDMethod(t *testing.T) { got, err := ValidateDIDMethod(DIDMethodKey) require.NoError(t, err) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go index a91b267bd..1a9334cdf 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go @@ -56,6 +56,7 @@ type mocks struct { ackService *MockAckService documentLoader *jsonld.DefaultDocumentLoader composer *Mockcomposer + wellKnown *MockwellKnownProvider } func TestService_InitiateIssuance(t *testing.T) { @@ -1335,6 +1336,42 @@ func TestService_InitiateIssuance(t *testing.T) { require.Nil(t, resp) }, }, + { + name: "Success with Dynamic WellKnown", + setup: func(mocks *mocks) { + initialOpState := "" + claimData := degreeClaims + + profile = &testProfile + delete(profile.CredentialMetaData.CredentialsConfigurationSupported, "UniversityDegreeCredentialIdentifier") + profile.OIDCConfig.DynamicWellKnownSupported = true + + mocks.wellKnown.EXPECT(). + AddDynamicConfiguration(gomock.Any(), profile.ID, gomock.Any(), gomock.Any()). + Return(nil) + + mocks.jsonSchemaValidator.EXPECT().Validate(gomock.Any(), gomock.Any(), gomock.Any()). + Return(errors.New("schema validation err")) + + issuanceReq = &oidc4ci.InitiateIssuanceRequest{ + ClientWellKnownURL: walletWellKnownURL, + OpState: initialOpState, + UserPinRequired: false, + GrantType: oidc4ci.GrantTypePreAuthorizedCode, + Scope: []string{"openid", "profile"}, + CredentialConfiguration: []oidc4ci.InitiateIssuanceCredentialConfiguration{ + { + CredentialTemplateID: "templateID2", + ClaimData: claimData, + }, + }, + } + }, + check: func(t *testing.T, resp *oidc4ci.InitiateIssuanceResponse, err error) { + require.ErrorContains(t, err, "schema validation err") + require.Nil(t, resp) + }, + }, { name: "Error because of event publishing", setup: func(mocks *mocks) { @@ -1866,6 +1903,7 @@ func TestService_InitiateIssuance(t *testing.T) { crypto: NewMockDataProtector(gomock.NewController(t)), jsonSchemaValidator: NewMockJSONSchemaValidator(gomock.NewController(t)), documentLoader: jsonld.NewDefaultDocumentLoader(http.DefaultClient), + wellKnown: NewMockwellKnownProvider(gomock.NewController(t)), } tt.setup(m) @@ -1881,6 +1919,7 @@ func TestService_InitiateIssuance(t *testing.T) { DataProtector: m.crypto, JSONSchemaValidator: m.jsonSchemaValidator, DocumentLoader: m.documentLoader, + WellKnownProvider: m.wellKnown, }) require.NoError(t, err) diff --git a/pkg/service/wellknown/provider/interfaces.go b/pkg/service/wellknown/provider/interfaces.go index 3922007ef..291b7cbaa 100644 --- a/pkg/service/wellknown/provider/interfaces.go +++ b/pkg/service/wellknown/provider/interfaces.go @@ -12,7 +12,7 @@ import ( profileapi "github.com/trustbloc/vcs/pkg/profile" ) -//go:generate mockgen -destination interfaces_mocks_test.go -package provider_test -source=interfaces.go +//go:generate mockgen -destination interfaces_mocks_test.go -package provider -source=interfaces.go type dynamicWellKnownStore interface { Upsert( diff --git a/pkg/service/wellknown/provider/wellknown_service_test.go b/pkg/service/wellknown/provider/wellknown_service_test.go index d01b8cf53..44552a074 100644 --- a/pkg/service/wellknown/provider/wellknown_service_test.go +++ b/pkg/service/wellknown/provider/wellknown_service_test.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0 package provider import ( + "context" _ "embed" "encoding/json" "errors" @@ -286,6 +287,93 @@ func checkWellKnownOpenIDIssuerConfiguration( } } +func TestBuildWithDynamic(t *testing.T) { + t.Run("dynamic err", func(t *testing.T) { + store := NewMockdynamicWellKnownStore(gomock.NewController(t)) + + srv := NewService(&Config{ + DynamicWellKnownStore: store, + }) + + store.EXPECT().Get(gomock.Any(), "12345"). + Return(nil, errors.New("unexpected err")) + + resp, err := srv.getOpenIDIssuerConfig(&profileapi.Issuer{ + ID: "12345", + CredentialMetaData: &profileapi.CredentialMetaData{}, + OIDCConfig: &profileapi.OIDCConfig{ + DynamicWellKnownSupported: true, + }, + VCConfig: &profileapi.VCConfig{ + KeyType: "someKey", + }, + }) + + assert.ErrorContains(t, err, "unexpected err") + assert.Nil(t, resp) + }) + t.Run("success", func(t *testing.T) { + store := NewMockdynamicWellKnownStore(gomock.NewController(t)) + + srv := NewService(&Config{ + DynamicWellKnownStore: store, + }) + + store.EXPECT().Get(gomock.Any(), "12345"). + Return(map[string]*profileapi.CredentialsConfigurationSupported{ + "a": { + CredentialDefinition: &profileapi.CredentialDefinition{ + Type: []string{"SomeType"}, + }, + }, + }, nil) + + resp, err := srv.getOpenIDIssuerConfig(&profileapi.Issuer{ + ID: "12345", + CredentialMetaData: &profileapi.CredentialMetaData{}, + OIDCConfig: &profileapi.OIDCConfig{ + DynamicWellKnownSupported: true, + }, + VCConfig: &profileapi.VCConfig{ + KeyType: "someKey", + }, + }) + + assert.NoError(t, err) + assert.NotNil(t, resp) + + assert.Len(t, resp.CredentialConfigurationsSupported.AdditionalProperties, 1) + assert.EqualValues( + t, + []string{"SomeType"}, + resp.CredentialConfigurationsSupported.AdditionalProperties["a"].CredentialDefinition.Type, + ) + }) +} + +func TestUpsert(t *testing.T) { + store := NewMockdynamicWellKnownStore(gomock.NewController(t)) + + srv := NewService(&Config{ + DynamicWellKnownStore: store, + }) + + val := &profileapi.CredentialsConfigurationSupported{} + + store.EXPECT().Upsert(gomock.Any(), "profileID", gomock.Any()). + DoAndReturn(func( + ctx context.Context, + s string, + m map[string]*profileapi.CredentialsConfigurationSupported, + ) error { + assert.Len(t, m, 1) + assert.Equal(t, val, m["key1"]) + return nil + }) + assert.NoError(t, srv.AddDynamicConfiguration(context.TODO(), "profileID", "key1", + val)) +} + func checkWellKnownOpenIDIssuerConfigurationDisplayPropertyExist(t *testing.T, display []issuer.CredentialDisplay) { t.Helper() From 1d79de7d04fb5ffef1fbe191f48f782b9de8e803 Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Wed, 6 Nov 2024 20:41:33 +0100 Subject: [PATCH 7/8] fix: profile --- .../oidc4ci/oidc4ci_service_initiate_issuance_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go index 1a9334cdf..67ee2fb58 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go @@ -1342,7 +1342,10 @@ func TestService_InitiateIssuance(t *testing.T) { initialOpState := "" claimData := degreeClaims - profile = &testProfile + b, err := json.Marshal(testProfile) + require.NoError(t, err) + + assert.NoError(t, json.Unmarshal(b, &profile)) delete(profile.CredentialMetaData.CredentialsConfigurationSupported, "UniversityDegreeCredentialIdentifier") profile.OIDCConfig.DynamicWellKnownSupported = true @@ -1570,6 +1573,7 @@ func TestService_InitiateIssuance(t *testing.T) { } profile = &testProfile + profile.OIDCConfig.DynamicWellKnownSupported = false }, check: func(t *testing.T, resp *oidc4ci.InitiateIssuanceResponse, err error) { require.Nil(t, resp) From 58bb02accaac6498e8c0cd9ed2e11016ad3a1bee Mon Sep 17 00:00:00 2001 From: Stas Dm Date: Wed, 6 Nov 2024 20:52:10 +0100 Subject: [PATCH 8/8] fix: lint --- pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go index 67ee2fb58..169698867 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go @@ -1342,8 +1342,8 @@ func TestService_InitiateIssuance(t *testing.T) { initialOpState := "" claimData := degreeClaims - b, err := json.Marshal(testProfile) - require.NoError(t, err) + b, err := json.Marshal(testProfile) //nolint + assert.NoError(t, err) assert.NoError(t, json.Unmarshal(b, &profile)) delete(profile.CredentialMetaData.CredentialsConfigurationSupported, "UniversityDegreeCredentialIdentifier")