From 79599becdc39852b46ffe8ab162ec541cadac99c Mon Sep 17 00:00:00 2001 From: Mykhailo Sizov Date: Tue, 12 Sep 2023 16:58:45 +0300 Subject: [PATCH] feat: Signed Credential Issuer's Metadata Signed-off-by: Mykhailo Sizov --- cmd/vc-rest/startcmd/start.go | 38 +-- component/profile/reader/file/go.mod | 10 + component/profile/reader/file/go.sum | 94 +++++++ component/profile/reader/file/reader.go | 14 +- component/wallet-cli/cmd/oidc4ci.go | 59 ++--- .../pkg/credentialoffer/credentialoffer.go | 18 +- .../pkg/walletrunner/wallet_runner_oidc4ci.go | 75 ++++-- .../wallet_runner_oidc4ci_pre_auth.go | 7 +- pkg/profile/api.go | 1 + pkg/restapi/v1/issuer/controller.go | 141 +++------- pkg/restapi/v1/issuer/controller_test.go | 214 ++++++--------- pkg/restapi/v1/util/bind.go | 10 + .../{ => fetcher}/wellknown_service.go | 4 +- .../{ => fetcher}/wellknown_service_test.go | 14 +- .../wellknown/provider/testdata/profile.json | 52 ++++ .../wellknown/provider/wellknown_service.go | 178 +++++++++++++ .../provider/wellknown_service_test.go | 248 ++++++++++++++++++ test/bdd/fixtures/profile/profiles.json | 1 + test/bdd/pkg/v1/oidc4vc/oidc4ci.go | 58 ++-- 19 files changed, 883 insertions(+), 353 deletions(-) rename pkg/service/wellknown/{ => fetcher}/wellknown_service.go (91%) rename pkg/service/wellknown/{ => fetcher}/wellknown_service_test.go (93%) create mode 100644 pkg/service/wellknown/provider/testdata/profile.json create mode 100644 pkg/service/wellknown/provider/wellknown_service.go create mode 100644 pkg/service/wellknown/provider/wellknown_service_test.go diff --git a/cmd/vc-rest/startcmd/start.go b/cmd/vc-rest/startcmd/start.go index 96fcc1a55..e753aa105 100644 --- a/cmd/vc-rest/startcmd/start.go +++ b/cmd/vc-rest/startcmd/start.go @@ -89,7 +89,8 @@ import ( "github.com/trustbloc/vcs/pkg/service/requestobject" "github.com/trustbloc/vcs/pkg/service/verifycredential" "github.com/trustbloc/vcs/pkg/service/verifypresentation" - "github.com/trustbloc/vcs/pkg/service/wellknown" + wellknownfetcher "github.com/trustbloc/vcs/pkg/service/wellknown/fetcher" + wellknownprovider "github.com/trustbloc/vcs/pkg/service/wellknown/provider" "github.com/trustbloc/vcs/pkg/storage/mongodb" "github.com/trustbloc/vcs/pkg/storage/mongodb/cslindexstore" "github.com/trustbloc/vcs/pkg/storage/mongodb/cslvcstore" @@ -536,12 +537,19 @@ func buildEchoHandler( return newHTTPClient(tlsConfig, conf.StartupParameters, metrics, id) } + openidCredentialIssuerConfigProviderSvc := wellknownprovider.NewService(&wellknownprovider.Config{ + ExternalHostURL: conf.StartupParameters.apiGatewayURL, + KMSRegistry: kmsRegistry, + CryptoJWTSigner: vcCrypto, + }) + // Issuer Profile Management API issuerProfileSvc, err := profilereader.NewIssuerReader(&profilereader.Config{ - TLSConfig: tlsConfig, - KMSRegistry: kmsRegistry, - CMD: cmd, - HTTPClient: getHTTPClient(metricsProvider.ClientIssuerProfile), + OpenidIssuerConfigProvider: openidCredentialIssuerConfigProviderSvc, + TLSConfig: tlsConfig, + KMSRegistry: kmsRegistry, + CMD: cmd, + HTTPClient: getHTTPClient(metricsProvider.ClientIssuerProfile), }) if err != nil { return nil, err @@ -647,7 +655,7 @@ func buildEchoHandler( oidc4ciService, err = oidc4ci.NewService(&oidc4ci.Config{ TransactionStore: oidc4ciTransactionStore, ClaimDataStore: oidc4ciClaimDataStore, - WellKnownService: wellknown.NewService(getHTTPClient(metricsProvider.ClientWellKnown)), + WellKnownService: wellknownfetcher.NewService(getHTTPClient(metricsProvider.ClientWellKnown)), ProfileService: issuerProfileSvc, IssuerVCSPublicHost: conf.StartupParameters.apiGatewayURL, HTTPClient: getHTTPClient(metricsProvider.ClientOIDC4CI), @@ -754,15 +762,15 @@ func buildEchoHandler( })) issuerv1.RegisterHandlers(e, issuerv1.NewController(&issuerv1.Config{ - EventSvc: eventSvc, - ProfileSvc: issuerProfileSvc, - KMSRegistry: kmsRegistry, - DocumentLoader: documentLoader, - IssueCredentialService: issueCredentialSvc, - VcStatusManager: statusListVCSvc, - OIDC4CIService: oidc4ciService, - ExternalHostURL: conf.StartupParameters.apiGatewayURL, - Tracer: conf.Tracer, + EventSvc: eventSvc, + ProfileSvc: issuerProfileSvc, + DocumentLoader: documentLoader, + IssueCredentialService: issueCredentialSvc, + VcStatusManager: statusListVCSvc, + OIDC4CIService: oidc4ciService, + ExternalHostURL: conf.StartupParameters.apiGatewayURL, + Tracer: conf.Tracer, + OpenidIssuerConfigProvider: openidCredentialIssuerConfigProviderSvc, })) // Verifier Profile Management API diff --git a/component/profile/reader/file/go.mod b/component/profile/reader/file/go.mod index f71d050f5..7e63ab1c3 100644 --- a/component/profile/reader/file/go.mod +++ b/component/profile/reader/file/go.mod @@ -45,15 +45,20 @@ require ( github.com/creasty/defaults v1.7.0 // indirect github.com/dave/jennifer v1.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deepmap/oapi-codegen v1.11.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ecordell/optgen v0.0.9 // indirect github.com/evanphx/json-patch v4.11.0+incompatible // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/getkin/kin-openapi v0.94.0 // indirect + github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect github.com/go-jose/go-jose/v3 v3.0.1-0.20221117193127-916db76e8214 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/swag v0.22.3 // indirect github.com/golang/glog v1.1.1 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -67,6 +72,7 @@ require ( github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 // indirect github.com/hyperledger/ursa-wrapper-go v0.3.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e // indirect github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 // indirect github.com/klauspost/compress v1.15.9 // indirect @@ -74,6 +80,7 @@ require ( github.com/labstack/echo/v4 v4.9.0 // indirect github.com/labstack/gommon v0.3.1 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/goveralls v0.0.12 // indirect @@ -136,13 +143,16 @@ require ( golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.14.0 // indirect + golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/component/profile/reader/file/go.sum b/component/profile/reader/file/go.sum index 973485303..c091f0bff 100644 --- a/component/profile/reader/file/go.sum +++ b/component/profile/reader/file/go.sum @@ -130,16 +130,23 @@ github.com/consensys/gnark-crypto v0.9.1/go.mod h1:a2DQL4+5ywF6safEeZFEPGRiiGbjz github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= github.com/cristalhq/jwt/v4 v4.0.2 h1:g/AD3h0VicDamtlM70GWGElp8kssQEv+5wYd7L9WOhU= github.com/cristalhq/jwt/v4 v4.0.2/go.mod h1:HnYraSNKDRag1DZP92rYHyrjyQHnVEHPNqesmzs+miQ= +github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.6.1 h1:T4T/67t6RAA5AIV6+NP8Uk/BIsXgDoqEowgycdQQLuk= github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/deepmap/oapi-codegen v1.11.0 h1:f/X2NdIkaBKsSdpeuwLnY/vDI0AtPUrmB5LMgc7YD+A= +github.com/deepmap/oapi-codegen v1.11.0/go.mod h1:k+ujhoQGxmQYBZBbxhOZNZf4j08qv5mC+OH+fFTnKxM= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= @@ -174,6 +181,14 @@ github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/getkin/kin-openapi v0.94.0 h1:bAxg2vxgnHHHoeefVdmGbR+oxtJlcv5HsJJa3qmAHuo= +github.com/getkin/kin-openapi v0.94.0/go.mod h1:LWZfzOd7PRy8GJ1dJ6mCU6tNdSfOwRac1BUPam4aw6Q= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= +github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= +github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -184,8 +199,23 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -216,11 +246,13 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -236,6 +268,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -262,6 +295,7 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -289,9 +323,15 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= +github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kawamuray/jsonpath v0.0.0-20201211160320-7483bafabd7e h1:Eh/0JuXDdcBHc39j4tFXKTy/AKiK7IQkGJXQxyryXiU= @@ -307,23 +347,44 @@ github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QH github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY= github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o= github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= +github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= +github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= +github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= +github.com/lestrrat-go/jwx v1.2.24/go.mod h1:zoNuZymNl5lgdcu6P7K6ie2QRll5HVfF4xwxBBK1NxY= +github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -341,6 +402,10 @@ github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqky github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI= github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -360,6 +425,7 @@ github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUj github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM= github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -387,6 +453,7 @@ github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f h1:HlPa7RcxTCrva5izPfTEfvYecO7LTahgmMRD1Qp13xg= github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -396,6 +463,8 @@ github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8 github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -461,6 +530,10 @@ github.com/trustbloc/sidetree-core-go v1.0.0 h1:kzfKZOJ0sgDy9D1AYNcoR3JHutqtMtKv github.com/trustbloc/sidetree-core-go v1.0.0/go.mod h1:jdxAFuorlIwFOGVW6O455/lZqxg2mZkRHNTEolcZdDI= github.com/trustbloc/vc-go v0.0.0-20230908212925-754e5de46099 h1:rpv48mIg2wnfMDDlX+jYZYXaXTopVp4H3SnvNZNjNRM= github.com/trustbloc/vc-go v0.0.0-20230908212925-754e5de46099/go.mod h1:7x0KWtXL0cileJlBjdQYeWqX/LMunrOuqFUIar4XHIY= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= @@ -486,6 +559,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= @@ -523,7 +597,11 @@ golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220513210258-46612604a0f9/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= @@ -564,6 +642,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -602,7 +681,9 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= @@ -648,6 +729,7 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -674,8 +756,12 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -704,6 +790,8 @@ golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -752,6 +840,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= @@ -761,6 +850,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -852,10 +942,12 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -868,9 +960,11 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/component/profile/reader/file/reader.go b/component/profile/reader/file/reader.go index 96bce5e75..b50b8f55f 100644 --- a/component/profile/reader/file/reader.go +++ b/component/profile/reader/file/reader.go @@ -28,6 +28,7 @@ import ( "github.com/trustbloc/vcs/internal/logfields" vcskms "github.com/trustbloc/vcs/pkg/kms" profileapi "github.com/trustbloc/vcs/pkg/profile" + issuerrestapi "github.com/trustbloc/vcs/pkg/restapi/v1/issuer" ) const ( @@ -44,12 +45,17 @@ type httpClient interface { Do(req *http.Request) (*http.Response, error) } +type openidCredentialIssuerConfigProvider interface { + GetOpenIDCredentialIssuerConfig(issuerProfile *profileapi.Issuer) (*issuerrestapi.WellKnownOpenIDIssuerConfiguration, string, error) +} + // Config contain config. type Config struct { - KMSRegistry *vcskms.Registry - TLSConfig *tls.Config - CMD *cobra.Command - HTTPClient httpClient + KMSRegistry *vcskms.Registry + TLSConfig *tls.Config + CMD *cobra.Command + HTTPClient httpClient + OpenidIssuerConfigProvider openidCredentialIssuerConfigProvider } // IssuerReader read issuer profiles. diff --git a/component/wallet-cli/cmd/oidc4ci.go b/component/wallet-cli/cmd/oidc4ci.go index 75a042386..b55a1b6c1 100644 --- a/component/wallet-cli/cmd/oidc4ci.go +++ b/component/wallet-cli/cmd/oidc4ci.go @@ -29,24 +29,23 @@ const ( ) type oidc4ciCommandFlags struct { - QRCode string - InitiateIssuanceURL string - DemoIssuerURL string - ClientID string - GrantType string - Scope []string - RedirectURI string - Login string - Password string - IssuerState string - VCFormat string - VCProvider string - CredentialType string - CredentialFormat string - Debug bool - Pin string - DiscoverableClientID bool - JWTSignedCredentialOffer bool + QRCode string + InitiateIssuanceURL string + DemoIssuerURL string + ClientID string + GrantType string + Scope []string + RedirectURI string + Login string + Password string + IssuerState string + VCFormat string + VCProvider string + CredentialType string + CredentialFormat string + Debug bool + Pin string + DiscoverableClientID bool WalletUserId string WalletPassPhrase string @@ -158,18 +157,17 @@ func NewOIDC4CICommand() *cobra.Command { } config := &walletrunner.OIDC4CIConfig{ - InitiateIssuanceURL: initiateIssuanceURL, - ClientID: flags.ClientID, - Scope: flags.Scope, - RedirectURI: flags.RedirectURI, - Login: flags.Login, - Password: flags.Password, - IssuerState: flags.IssuerState, - CredentialType: flags.CredentialType, - CredentialFormat: flags.CredentialFormat, - Pin: flags.Pin, - DiscoverableClientID: flags.DiscoverableClientID, - JWTSignedCredentialOffer: flags.JWTSignedCredentialOffer, + InitiateIssuanceURL: initiateIssuanceURL, + ClientID: flags.ClientID, + Scope: flags.Scope, + RedirectURI: flags.RedirectURI, + Login: flags.Login, + Password: flags.Password, + IssuerState: flags.IssuerState, + CredentialType: flags.CredentialType, + CredentialFormat: flags.CredentialFormat, + Pin: flags.Pin, + DiscoverableClientID: flags.DiscoverableClientID, } if isPreAuthorize { @@ -206,7 +204,6 @@ func NewOIDC4CICommand() *cobra.Command { cmd.Flags().BoolVar(&flags.Debug, "debug", false, "enable debug mode") cmd.Flags().BoolVar(&flags.InsecureTls, "insecure", false, "this option allows to skip the verification of ssl\\tls") cmd.Flags().BoolVar(&flags.DiscoverableClientID, "discoverable-client-id", false, "use discoverable client id scheme") - cmd.Flags().BoolVar(&flags.JWTSignedCredentialOffer, "jwt-signed-credential-offer", false, "allow wallet cli to parse JWT signed credential offer") cmd.Flags().StringVar(&flags.WalletUserId, "wallet-user-id", "", "existing wallet user id") cmd.Flags().StringVar(&flags.WalletPassPhrase, "wallet-passphrase", "", "existing wallet pass phrase") diff --git a/component/wallet-cli/pkg/credentialoffer/credentialoffer.go b/component/wallet-cli/pkg/credentialoffer/credentialoffer.go index 6c05b9e8e..b8216854c 100644 --- a/component/wallet-cli/pkg/credentialoffer/credentialoffer.go +++ b/component/wallet-cli/pkg/credentialoffer/credentialoffer.go @@ -2,7 +2,6 @@ package credentialoffer import ( "encoding/json" - "errors" "fmt" "io" "net/http" @@ -16,13 +15,10 @@ import ( "github.com/trustbloc/vcs/pkg/service/oidc4ci" ) -var errSignedCredentialOfferIsNotSupported = errors.New("credential offer is in JWT format, but it is not supported by configuration") - type Params struct { - InitiateIssuanceURL string - Client *http.Client - VDRRegistry vdrapi.Registry - JWTSignedCredentialOfferSupported bool + InitiateIssuanceURL string + Client *http.Client + VDRRegistry vdrapi.Registry } func ParseInitiateIssuanceUrl(params *Params) (*oidc4ci.CredentialOfferResponse, error) { @@ -39,10 +35,6 @@ func ParseInitiateIssuanceUrl(params *Params) (*oidc4ci.CredentialOfferResponse, // Depends on Issuer configuration, credentialOfferURL might be either JWT signed CredentialOfferResponse, // or encoded oidc4ci.CredentialOfferResponse itself. if jwt.IsJWS(credentialOfferQueryParam) { - if !params.JWTSignedCredentialOfferSupported { - return nil, errSignedCredentialOfferIsNotSupported - } - credentialOfferPayload, err = getCredentialOfferJWTPayload(credentialOfferQueryParam, params.VDRRegistry) if err != nil { return nil, err @@ -76,10 +68,6 @@ func ParseInitiateIssuanceUrl(params *Params) (*oidc4ci.CredentialOfferResponse, // Depends on Issuer configuration, rspBody might be either JWT signed CredentialOfferResponse, // or encoded oidc4ci.CredentialOfferResponse itself. if jwt.IsJWS(string(credentialOfferPayload)) { - if !params.JWTSignedCredentialOfferSupported { - return nil, errSignedCredentialOfferIsNotSupported - } - credentialOfferPayload, err = getCredentialOfferJWTPayload(string(credentialOfferPayload), params.VDRRegistry) if err != nil { return nil, err diff --git a/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci.go b/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci.go index a4dce7e9b..f101611c7 100644 --- a/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci.go +++ b/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci.go @@ -25,9 +25,11 @@ import ( "github.com/samber/lo" "github.com/trustbloc/did-go/method/jwk" didkey "github.com/trustbloc/did-go/method/key" + vdrapi "github.com/trustbloc/did-go/vdr/api" "github.com/trustbloc/kms-go/doc/jose" "github.com/trustbloc/vc-go/jwt" "github.com/trustbloc/vc-go/verifiable" + "github.com/valyala/fastjson" "golang.org/x/oauth2" "github.com/trustbloc/vcs/component/wallet-cli/pkg/credentialoffer" @@ -43,18 +45,17 @@ const ( ) type OIDC4CIConfig struct { - InitiateIssuanceURL string - ClientID string - Scope []string - RedirectURI string - CredentialType string - CredentialFormat string - Pin string - Login string - Password string - IssuerState string - DiscoverableClientID bool - JWTSignedCredentialOffer bool + InitiateIssuanceURL string + ClientID string + Scope []string + RedirectURI string + CredentialType string + CredentialFormat string + Pin string + Login string + Password string + IssuerState string + DiscoverableClientID bool } type OauthClientOpt func(config *oauth2.Config) @@ -81,10 +82,9 @@ func (s *Service) RunOIDC4CI(config *OIDC4CIConfig, hooks *Hooks) error { offerResponse, err := credentialoffer.ParseInitiateIssuanceUrl( &credentialoffer.Params{ - InitiateIssuanceURL: config.InitiateIssuanceURL, - Client: s.httpClient, - VDRRegistry: s.ariesServices.vdrRegistry, - JWTSignedCredentialOfferSupported: config.JWTSignedCredentialOffer, + InitiateIssuanceURL: config.InitiateIssuanceURL, + Client: s.httpClient, + VDRRegistry: s.ariesServices.vdrRegistry, }, ) if err != nil { @@ -454,13 +454,54 @@ func (s *Service) getIssuerCredentialsOIDCConfig( var oidcConfig issuerv1.WellKnownOpenIDIssuerConfiguration - if err = json.NewDecoder(resp.Body).Decode(&oidcConfig); err != nil { + wellKnownOpenIDIssuerConfigurationPayload, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read issuer configuration payload body: %w", err) + } + + if jwt.IsJWS(string(wellKnownOpenIDIssuerConfigurationPayload)) { + wellKnownOpenIDIssuerConfigurationPayload, err = + getWellKnownOpenIDIssuerConfigurationJWTPayload( + string(wellKnownOpenIDIssuerConfigurationPayload), s.ariesServices.vdrRegistry) + if err != nil { + return nil, err + } + } + + if err = json.Unmarshal(wellKnownOpenIDIssuerConfigurationPayload, &oidcConfig); err != nil { return nil, fmt.Errorf("decode issuer well-known: %w", err) } return &oidcConfig, nil } +func getWellKnownOpenIDIssuerConfigurationJWTPayload(rawResponse string, vdrRegistry vdrapi.Registry) ([]byte, error) { + jwtVerifier := jwt.NewVerifier(jwt.KeyResolverFunc( + verifiable.NewVDRKeyResolver(vdrRegistry).PublicKeyFetcher())) + + _, credentialOfferPayload, err := jwt.Parse( + rawResponse, + jwt.WithSignatureVerifier(jwtVerifier), + jwt.WithIgnoreClaimsMapDecoding(true), + ) + if err != nil { + return nil, fmt.Errorf("parse issuer configuration JWT: %w", err) + } + + var fastParser fastjson.Parser + v, err := fastParser.ParseBytes(credentialOfferPayload) + if err != nil { + return nil, fmt.Errorf("decode claims: %w", err) + } + + sb, err := v.Get("well_known_openid_issuer_configuration").Object() + if err != nil { + return nil, fmt.Errorf("fastjson.Parser Get well_known_openid_issuer_configuration: %w", err) + } + + return sb.MarshalTo([]byte{}), nil +} + func (s *Service) getAuthCode( config *OIDC4CIConfig, authCodeURL string, diff --git a/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci_pre_auth.go b/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci_pre_auth.go index cb0051b6d..c7f76cd5e 100644 --- a/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci_pre_auth.go +++ b/component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4ci_pre_auth.go @@ -41,10 +41,9 @@ func (s *Service) RunOIDC4CIPreAuth(config *OIDC4CIConfig) (*verifiable.Credenti log.Printf("Initiate issuance URL:\n\n\t%s\n\n", config.InitiateIssuanceURL) offerResponse, err := credentialoffer.ParseInitiateIssuanceUrl( &credentialoffer.Params{ - InitiateIssuanceURL: config.InitiateIssuanceURL, - Client: s.httpClient, - VDRRegistry: s.ariesServices.vdrRegistry, - JWTSignedCredentialOfferSupported: config.JWTSignedCredentialOffer, + InitiateIssuanceURL: config.InitiateIssuanceURL, + Client: s.httpClient, + VDRRegistry: s.ariesServices.vdrRegistry, }, ) if err != nil { diff --git a/pkg/profile/api.go b/pkg/profile/api.go index faeaf27ec..3190f8be2 100644 --- a/pkg/profile/api.go +++ b/pkg/profile/api.go @@ -108,6 +108,7 @@ type OIDCConfig struct { PreAuthorizedGrantAnonymousAccessSupported bool `json:"pre-authorized_grant_anonymous_access_supported"` WalletInitiatedAuthFlowSupported bool `json:"wallet_initiated_auth_flow_supported"` SignedCredentialOfferSupported bool `json:"signed_credential_offer_supported"` + SignedIssuerMetadataSupported bool `json:"signed_issuer_metadata_supported"` ClaimsEndpoint string `json:"claims_endpoint"` } diff --git a/pkg/restapi/v1/issuer/controller.go b/pkg/restapi/v1/issuer/controller.go index a66cc7f5e..f796b4358 100644 --- a/pkg/restapi/v1/issuer/controller.go +++ b/pkg/restapi/v1/issuer/controller.go @@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0 */ //go:generate oapi-codegen --config=openapi.cfg.yaml ../../../../docs/v1/openapi.yaml -//go:generate mockgen -destination controller_mocks_test.go -self_package mocks -package issuer -source=controller.go -mock_names profileService=MockProfileService,kmsRegistry=MockKMSRegistry,issueCredentialService=MockIssueCredentialService,oidc4ciService=MockOIDC4CIService,vcStatusManager=MockVCStatusManager,eventService=MockEventService +//go:generate mockgen -destination controller_mocks_test.go -self_package github.com/trustbloc/vcs/pkg/restapi/v1/issuer -package issuer -source=controller.go -mock_names profileService=MockProfileService,issueCredentialService=MockIssueCredentialService,oidc4ciService=MockOIDC4CIService,vcStatusManager=MockVCStatusManager,openidCredentialIssuerConfigProvider=MockOpenIDCredentialIssuerConfigProvider,eventService=MockEventService package issuer @@ -35,7 +35,6 @@ import ( vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable" "github.com/trustbloc/vcs/pkg/event/spi" "github.com/trustbloc/vcs/pkg/internal/common/jsonschema" - "github.com/trustbloc/vcs/pkg/kms" "github.com/trustbloc/vcs/pkg/observability/tracing/attributeutil" profileapi "github.com/trustbloc/vcs/pkg/profile" "github.com/trustbloc/vcs/pkg/restapi/resterr" @@ -55,12 +54,6 @@ const ( var _ ServerInterface = (*Controller)(nil) // make sure Controller implements ServerInterface -type kmsManager = kms.VCSKeyManager - -type kmsRegistry interface { - GetKeyManager(config *kms.Config) (kmsManager, error) -} - type profileService interface { GetProfile(profileID profileapi.ID, profileVersion profileapi.Version) (*profileapi.Issuer, error) } @@ -81,47 +74,51 @@ type vcStatusManager interface { credentialstatus.ServiceInterface } -type Config struct { - EventSvc eventService - ProfileSvc profileService - KMSRegistry kmsRegistry - DocumentLoader ld.DocumentLoader - IssueCredentialService issuecredential.ServiceInterface - OIDC4CIService oidc4ciService - VcStatusManager vcStatusManager - ExternalHostURL string - Tracer trace.Tracer +type openidCredentialIssuerConfigProvider interface { + GetOpenIDCredentialIssuerConfig(issuerProfile *profileapi.Issuer) (*WellKnownOpenIDIssuerConfiguration, string, error) } type jsonSchemaValidator interface { Validate(data interface{}, schemaID string, schema []byte) error } +type Config struct { + EventSvc eventService + ProfileSvc profileService + DocumentLoader ld.DocumentLoader + IssueCredentialService issuecredential.ServiceInterface + OIDC4CIService oidc4ciService + VcStatusManager vcStatusManager + OpenidIssuerConfigProvider openidCredentialIssuerConfigProvider + ExternalHostURL string + Tracer trace.Tracer +} + // Controller for Issuer Profile Management API. type Controller struct { - profileSvc profileService - kmsRegistry kmsRegistry - documentLoader ld.DocumentLoader - issueCredentialService issuecredential.ServiceInterface - oidc4ciService oidc4ciService - vcStatusManager vcStatusManager - externalHostURL string - tracer trace.Tracer - schemaValidator jsonSchemaValidator + profileSvc profileService + documentLoader ld.DocumentLoader + issueCredentialService issuecredential.ServiceInterface + oidc4ciService oidc4ciService + vcStatusManager vcStatusManager + openidIssuerConfigProvider openidCredentialIssuerConfigProvider + externalHostURL string + tracer trace.Tracer + schemaValidator jsonSchemaValidator } // NewController creates a new controller for Issuer Profile Management API. func NewController(config *Config) *Controller { return &Controller{ - profileSvc: config.ProfileSvc, - kmsRegistry: config.KMSRegistry, - documentLoader: config.DocumentLoader, - issueCredentialService: config.IssueCredentialService, - oidc4ciService: config.OIDC4CIService, - vcStatusManager: config.VcStatusManager, - externalHostURL: config.ExternalHostURL, - tracer: config.Tracer, - schemaValidator: jsonschema.NewCachingValidator(), + profileSvc: config.ProfileSvc, + documentLoader: config.DocumentLoader, + issueCredentialService: config.IssueCredentialService, + oidc4ciService: config.OIDC4CIService, + vcStatusManager: config.VcStatusManager, + openidIssuerConfigProvider: config.OpenidIssuerConfigProvider, + externalHostURL: config.ExternalHostURL, + tracer: config.Tracer, + schemaValidator: jsonschema.NewCachingValidator(), } } @@ -792,77 +789,21 @@ func (c *Controller) OpenidConfig(ctx echo.Context, profileID, profileVersion st // OpenidCredentialIssuerConfig request openid credentials configuration for issuer. // GET /issuer/{profileID}/{profileVersion}/.well-known/openid-credential-issuer. func (c *Controller) OpenidCredentialIssuerConfig(ctx echo.Context, profileID, profileVersion string) error { - return util.WriteOutput(ctx)(c.getOpenIDIssuerConfig(profileID, profileVersion)) -} - -func (c *Controller) getOpenIDIssuerConfig( - profileID string, - profileVersion string, -) (*WellKnownOpenIDIssuerConfiguration, error) { - host := c.externalHostURL - if !strings.HasSuffix(host, "/") { - host += "/" - } - - issuer, err := c.profileSvc.GetProfile(profileID, profileVersion) + issuerProfile, err := c.profileSvc.GetProfile(profileID, profileVersion) if err != nil { - return nil, err - } - - var finalCredentials []interface{} - for _, t := range issuer.CredentialMetaData.CredentialsSupported { - if issuer.VCConfig != nil { - t["cryptographic_binding_methods_supported"] = []string{string(issuer.VCConfig.DIDMethod)} - t["cryptographic_suites_supported"] = []string{string(issuer.VCConfig.KeyType)} - } - finalCredentials = append(finalCredentials, t) + return err } - var display []CredentialDisplay - - if issuer.CredentialMetaData.Display != nil { - display = make([]CredentialDisplay, 0, len(issuer.CredentialMetaData.Display)) - - for _, d := range issuer.CredentialMetaData.Display { - credentialDisplay := CredentialDisplay{ - BackgroundColor: lo.ToPtr(d.BackgroundColor), - Locale: lo.ToPtr(d.Locale), - Name: lo.ToPtr(d.Name), - TextColor: lo.ToPtr(d.TextColor), - Url: lo.ToPtr(d.URL), - } - - if d.Logo != nil { - credentialDisplay.Logo = &Logo{ - AltText: lo.ToPtr(d.Logo.AlternativeText), - Url: lo.ToPtr(d.Logo.URL), - } - } - - display = append(display, credentialDisplay) - } - } else { - display = []CredentialDisplay{ - { - Locale: lo.ToPtr("en-US"), - Name: lo.ToPtr(issuer.Name), - Url: lo.ToPtr(issuer.URL), - }, - } + config, jwtSignedConfig, err := c.openidIssuerConfigProvider.GetOpenIDCredentialIssuerConfig(issuerProfile) + if err != nil { + return err } - issuerURL, _ := url.JoinPath(c.externalHostURL, "issuer", profileID, profileVersion) - - final := &WellKnownOpenIDIssuerConfiguration{ - AuthorizationServer: fmt.Sprintf("%soidc/authorize", host), - BatchCredentialEndpoint: nil, // no support for now - CredentialEndpoint: fmt.Sprintf("%soidc/credential", host), - CredentialsSupported: finalCredentials, - CredentialIssuer: issuerURL, - Display: lo.ToPtr(display), + if jwtSignedConfig != "" { + return util.WriteRawOutputWithContentType(ctx)([]byte(jwtSignedConfig), "application/jwt", nil) } - return final, nil + return util.WriteOutput(ctx)(config, nil) } func (c *Controller) getOpenIDConfig(profileID, profileVersion string) (*WellKnownOpenIDConfiguration, error) { diff --git a/pkg/restapi/v1/issuer/controller_test.go b/pkg/restapi/v1/issuer/controller_test.go index 9b8f0b5e6..a9cce56a0 100644 --- a/pkg/restapi/v1/issuer/controller_test.go +++ b/pkg/restapi/v1/issuer/controller_test.go @@ -25,14 +25,12 @@ import ( "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/trustbloc/kms-go/spi/kms" "github.com/trustbloc/vc-go/verifiable" "go.opentelemetry.io/otel/trace" "github.com/trustbloc/vcs/pkg/doc/vc" vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable" "github.com/trustbloc/vcs/pkg/internal/testutil" - "github.com/trustbloc/vcs/pkg/kms/mocks" profileapi "github.com/trustbloc/vcs/pkg/profile" "github.com/trustbloc/vcs/pkg/restapi/resterr" "github.com/trustbloc/vcs/pkg/restapi/v1/common" @@ -59,17 +57,6 @@ var ( universityDegreeSchema []byte ) -// nolint:gochecknoglobals -var ariesSupportedKeyTypes = []kms.KeyType{ - kms.ED25519Type, - kms.X25519ECDHKWType, - kms.ECDSASecp256k1TypeIEEEP1363, - kms.ECDSAP256TypeDER, - kms.ECDSAP384TypeDER, - kms.RSAPS256Type, - kms.BLS12381G2Type, -} - func TestController_PostIssueCredentials(t *testing.T) { mockProfileSvc := NewMockProfileService(gomock.NewController(t)) mockIssueCredentialSvc := NewMockIssueCredentialService(gomock.NewController(t)) @@ -513,20 +500,20 @@ func TestController_IssueCredentials(t *testing.T) { getCtx: func() echo.Context { return echoContext(withRequestBody( []byte(`{"credential":{ - "@context": [ - "https://www.w3.org/2018/credentials/v1" - ], - "credentialSubject": { - "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" - }, - "issuer": { - "id": "did:example:76e12ec712ebc6f1c221ebfeb1f" - }, - "type": [ - "VerifiableCredential", - "UniversityDegreeCredential" - ] - },"options":{"credentialStatus":{"type":"statusPurpose"}}}`))) + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + }, + "issuer": { + "id": "did:example:76e12ec712ebc6f1c221ebfeb1f" + }, + "type": [ + "VerifiableCredential", + "UniversityDegreeCredential" + ] + },"options":{"credentialStatus":{"type":"statusPurpose"}}}`))) }, getProfileSvc: func() profileService { mockProfileSvc.EXPECT().GetProfile(profileID, profileVersion).Times(1). @@ -594,12 +581,6 @@ func TestController_IssueCredentials(t *testing.T) { } func TestController_AuthFailed(t *testing.T) { - keyManager := mocks.NewMockVCSKeyManager(gomock.NewController(t)) - keyManager.EXPECT().SupportedKeyTypes().AnyTimes().Return(ariesSupportedKeyTypes) - - kmsRegistry := NewMockKMSRegistry(gomock.NewController(t)) - kmsRegistry.EXPECT().GetKeyManager(gomock.Any()).AnyTimes().Return(keyManager, nil) - mockProfileSvc := NewMockProfileService(gomock.NewController(t)) mockProfileSvc.EXPECT().GetProfile(profileID, profileVersion).AnyTimes(). Return(&profileapi.Issuer{OrganizationID: orgID, SigningDID: &profileapi.SigningDID{}}, nil) @@ -608,9 +589,9 @@ func TestController_AuthFailed(t *testing.T) { c := echoContext(withTenantID(""), withRequestBody([]byte(sampleVCJWT))) controller := NewController(&Config{ - ProfileSvc: mockProfileSvc, - KMSRegistry: kmsRegistry, - Tracer: trace.NewNoopTracerProvider().Tracer(""), + ProfileSvc: mockProfileSvc, + //KMSRegistry: kmsRegistry, + Tracer: trace.NewNoopTracerProvider().Tracer(""), }) err := controller.PostIssueCredentials(c, profileID, profileVersion) @@ -621,9 +602,9 @@ func TestController_AuthFailed(t *testing.T) { c := echoContext(withTenantID("orgID2"), withRequestBody([]byte(sampleVCJWT))) controller := NewController(&Config{ - ProfileSvc: mockProfileSvc, - KMSRegistry: kmsRegistry, - Tracer: trace.NewNoopTracerProvider().Tracer(""), + ProfileSvc: mockProfileSvc, + //KMSRegistry: kmsRegistry, + Tracer: trace.NewNoopTracerProvider().Tracer(""), }) err := controller.PostIssueCredentials(c, profileID, profileVersion) @@ -715,18 +696,12 @@ func Test_validateIssueCredOptions(t *testing.T) { } func TestController_PostCredentialsStatus(t *testing.T) { - keyManager := mocks.NewMockVCSKeyManager(gomock.NewController(t)) - keyManager.EXPECT().SupportedKeyTypes().AnyTimes().Return(ariesSupportedKeyTypes) - - kmsRegistry := NewMockKMSRegistry(gomock.NewController(t)) - kmsRegistry.EXPECT().GetKeyManager(gomock.Any()).AnyTimes().Return(keyManager, nil) - mockVCStatusManager := NewMockVCStatusManager(gomock.NewController(t)) mockVCStatusManager.EXPECT().UpdateVCStatus(context.Background(), gomock.Any()).Return(nil) t.Run("Success", func(t *testing.T) { controller := NewController(&Config{ - KMSRegistry: kmsRegistry, + //KMSRegistry: kmsRegistry, DocumentLoader: testutil.DocumentLoader(t), VcStatusManager: mockVCStatusManager, }) @@ -1535,54 +1510,10 @@ func TestOpenIDConfigurationController(t *testing.T) { assert.NoError(t, c.OpenidConfig(echoContext(), profileID, profileVersion)) } -func TestOpenIDIssuerConfigurationController(t *testing.T) { - profileSvc := NewMockProfileService(gomock.NewController(t)) - profileSvc.EXPECT().GetProfile(profileID, profileVersion).Return(&profileapi.Issuer{ - Name: "random_name", - VCConfig: &profileapi.VCConfig{ - DIDMethod: "orb", - KeyType: "ECDSASecp256k1DER", - }, - CredentialMetaData: &profileapi.CredentialMetaData{ - CredentialsSupported: []map[string]interface{}{ - { - "id": "VerifiedEmployee_JWT", - }, - }, - Display: []*profileapi.CredentialDisplay{ - { - Name: "Test Issuer", - Locale: "en-US", - URL: "https://example.com", - BackgroundColor: "#FFFFFF", - TextColor: "#000000", - Logo: &profileapi.Logo{ - URL: "https://example.com/credentials-logo.png", - AlternativeText: "Issuer Logo", - }, - }, - }, - }, - }, nil) - - c := &Controller{ - externalHostURL: "https://localhost", - profileSvc: profileSvc, - } - - assert.NoError(t, c.OpenidCredentialIssuerConfig(echoContext(), profileID, profileVersion)) -} - -func TestOpenIdIssuerConfiguration(t *testing.T) { +func TestOpenIdCredentialIssuerConfiguration(t *testing.T) { host := "https://localhost" - expected := &WellKnownOpenIDIssuerConfiguration{ - AuthorizationServer: "https://localhost/oidc/authorize", - CredentialEndpoint: "https://localhost/oidc/credential", - CredentialIssuer: "https://localhost/issuer/testID/v1.0", - } - profileSvc := NewMockProfileService(gomock.NewController(t)) - profileSvc.EXPECT().GetProfile(profileID, profileVersion).Return(&profileapi.Issuer{ + profile := &profileapi.Issuer{ Name: "random_name", URL: "https://localhost.com.local/abcd", VCConfig: &profileapi.VCConfig{ @@ -1596,43 +1527,63 @@ func TestOpenIdIssuerConfiguration(t *testing.T) { }, }, }, - }, nil).Times(2) + } + + t.Run("Success JWT", func(t *testing.T) { + openidIssuerConfigProvider := NewMockOpenIDCredentialIssuerConfigProvider(gomock.NewController(t)) + openidIssuerConfigProvider.EXPECT().GetOpenIDCredentialIssuerConfig(profile).Return(nil, "aa.bb.cc", nil).Times(1) + + profileSvc := NewMockProfileService(gomock.NewController(t)) + profileSvc.EXPECT().GetProfile(profileID, profileVersion).Return(profile, nil).Times(1) - t.Run("with /", func(t *testing.T) { c := &Controller{ - externalHostURL: host, - profileSvc: profileSvc, + externalHostURL: host, + profileSvc: profileSvc, + openidIssuerConfigProvider: openidIssuerConfigProvider, } - result, err := c.getOpenIDIssuerConfig(profileID, profileVersion) + recorder := httptest.NewRecorder() + + echoCtx := echoContext(withRecorder(recorder)) + + err := c.OpenidCredentialIssuerConfig(echoCtx, profileID, profileVersion) + assert.NoError(t, err) + + bodyBytes, err := io.ReadAll(recorder.Body) assert.NoError(t, err) - assert.Equal(t, expected.AuthorizationServer, result.AuthorizationServer) - assert.Equal(t, expected.CredentialEndpoint, result.CredentialEndpoint) - - assert.Equal(t, expected.CredentialEndpoint, result.CredentialEndpoint) - assert.Equal(t, "random_name", *(*result.Display)[0].Name) - assert.Equal(t, "en-US", *(*result.Display)[0].Locale) - assert.Equal(t, "https://localhost.com.local/abcd", *(*result.Display)[0].Url) - assert.Len(t, result.CredentialsSupported, 1) - - meta := (result.CredentialsSupported)[0].(map[string]interface{}) //nolint - assert.Equal(t, "VerifiedEmployee_JWT", meta["id"]) - assert.Equal(t, []string{"orb"}, meta["cryptographic_binding_methods_supported"]) - assert.Equal(t, []string{"ECDSASecp256k1DER"}, meta["cryptographic_suites_supported"]) - assert.Equal(t, expected.CredentialIssuer, result.CredentialIssuer) + + assert.Equal(t, "aa.bb.cc", string(bodyBytes)) + assert.Equal(t, "application/jwt", recorder.Header().Get("Content-Type")) }) - t.Run("without /", func(t *testing.T) { + t.Run("Success JSON", func(t *testing.T) { + openidIssuerConfigProvider := NewMockOpenIDCredentialIssuerConfigProvider(gomock.NewController(t)) + openidIssuerConfigProvider.EXPECT().GetOpenIDCredentialIssuerConfig(profile).Return( + &WellKnownOpenIDIssuerConfiguration{ + CredentialIssuer: "https://example.com", + }, "", nil).Times(1) + + profileSvc := NewMockProfileService(gomock.NewController(t)) + profileSvc.EXPECT().GetProfile(profileID, profileVersion).Return(profile, nil).Times(1) + c := &Controller{ - externalHostURL: host + "/", - profileSvc: profileSvc, + externalHostURL: host, + profileSvc: profileSvc, + openidIssuerConfigProvider: openidIssuerConfigProvider, } - result, err := c.getOpenIDIssuerConfig(profileID, profileVersion) + recorder := httptest.NewRecorder() + + echoCtx := echoContext(withRecorder(recorder)) + + err := c.OpenidCredentialIssuerConfig(echoCtx, profileID, profileVersion) assert.NoError(t, err) - assert.Equal(t, expected.AuthorizationServer, result.AuthorizationServer) - assert.Equal(t, expected.CredentialEndpoint, result.CredentialEndpoint) - assert.Equal(t, expected.CredentialIssuer, result.CredentialIssuer) + + bodyBytes, err := io.ReadAll(recorder.Body) + assert.NoError(t, err) + + assert.Contains(t, string(bodyBytes), "\"credential_issuer\":\"https://example.com\"") + assert.Equal(t, "application/json; charset=UTF-8", recorder.Header().Get("Content-Type")) }) t.Run("profile error", func(t *testing.T) { @@ -1644,9 +1595,12 @@ func TestOpenIdIssuerConfiguration(t *testing.T) { profileSvc: svc, } - result, err := c.getOpenIDIssuerConfig(profileID, profileVersion) - assert.Nil(t, result) - assert.ErrorContains(t, err, "unexpected error") + recorder := httptest.NewRecorder() + + echoCtx := echoContext(withRecorder(recorder)) + + err := c.OpenidCredentialIssuerConfig(echoCtx, profileID, profileVersion) + assert.Error(t, err) }) } @@ -1901,8 +1855,9 @@ func Test_getCredentialSubjects(t *testing.T) { } type options struct { - tenantID string - requestBody []byte + tenantID string + requestBody []byte + responseWriter http.ResponseWriter } type contextOpt func(*options) @@ -1919,9 +1874,16 @@ func withRequestBody(body []byte) contextOpt { } } +func withRecorder(w http.ResponseWriter) contextOpt { + return func(o *options) { + o.responseWriter = w + } +} + func echoContext(opts ...contextOpt) echo.Context { o := &options{ - tenantID: orgID, + tenantID: orgID, + responseWriter: httptest.NewRecorder(), } for _, fn := range opts { @@ -1943,9 +1905,7 @@ func echoContext(opts ...contextOpt) echo.Context { req.Header.Set("X-Tenant-ID", o.tenantID) } - rec := httptest.NewRecorder() - - return e.NewContext(req, rec) + return e.NewContext(req, o.responseWriter) } func requireValidationError(t *testing.T, expectedCode resterr.ErrorCode, incorrectValueName string, actual error) { diff --git a/pkg/restapi/v1/util/bind.go b/pkg/restapi/v1/util/bind.go index ba19c3498..410e2b670 100644 --- a/pkg/restapi/v1/util/bind.go +++ b/pkg/restapi/v1/util/bind.go @@ -55,3 +55,13 @@ func WriteOutputWithContentType(ctx echo.Context) func(output interface{}, ct st return ctx.Blob(http.StatusOK, ct, b) } } + +func WriteRawOutputWithContentType(ctx echo.Context) func(output []byte, ct string, err error) error { + return func(output []byte, ct string, err error) error { + if err != nil { + return err + } + + return ctx.Blob(http.StatusOK, ct, output) + } +} diff --git a/pkg/service/wellknown/wellknown_service.go b/pkg/service/wellknown/fetcher/wellknown_service.go similarity index 91% rename from pkg/service/wellknown/wellknown_service.go rename to pkg/service/wellknown/fetcher/wellknown_service.go index bc0180bf9..75f9745f6 100644 --- a/pkg/service/wellknown/wellknown_service.go +++ b/pkg/service/wellknown/fetcher/wellknown_service.go @@ -4,9 +4,9 @@ Copyright Avast Software. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -//go:generate mockgen -destination wellknown_service_mocks_test.go -package wellknown_test -source=wellknown_service.go -mock_names httpClient=MockHTTPClient +//go:generate mockgen -destination wellknown_service_mocks_test.go -package fetcher_test -source=wellknown_service.go -mock_names httpClient=MockHTTPClient -package wellknown +package fetcher import ( "context" diff --git a/pkg/service/wellknown/wellknown_service_test.go b/pkg/service/wellknown/fetcher/wellknown_service_test.go similarity index 93% rename from pkg/service/wellknown/wellknown_service_test.go rename to pkg/service/wellknown/fetcher/wellknown_service_test.go index c5e8da72f..f73003fc9 100644 --- a/pkg/service/wellknown/wellknown_service_test.go +++ b/pkg/service/wellknown/fetcher/wellknown_service_test.go @@ -4,7 +4,7 @@ Copyright Avast Software. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package wellknown_test +package fetcher_test import ( "context" @@ -18,7 +18,7 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "github.com/trustbloc/vcs/pkg/service/wellknown" + "github.com/trustbloc/vcs/pkg/service/wellknown/fetcher" ) func TestWellKnownService(t *testing.T) { @@ -29,7 +29,7 @@ func TestWellKnownService(t *testing.T) { "{\n \"issuer\": \"https://accounts.google.com\",\n \"authorization_endpoint\": \"https://accounts.google.com/o/oauth2/v2/auth\",\n \"device_authorization_endpoint\": \"https://oauth2.googleapis.com/device/code\",\n \"token_endpoint\": \"https://oauth2.googleapis.com/token\",\n \"userinfo_endpoint\": \"https://openidconnect.googleapis.com/v1/userinfo\",\n \"revocation_endpoint\": \"https://oauth2.googleapis.com/revoke\",\n \"jwks_uri\": \"https://www.googleapis.com/oauth2/v3/certs\",\n \"response_types_supported\": [\n \"code\",\n \"token\",\n \"id_token\",\n \"code token\",\n \"code id_token\",\n \"token id_token\",\n \"code token id_token\",\n \"none\"\n ],\n \"subject_types_supported\": [\n \"public\"\n ],\n \"id_token_signing_alg_values_supported\": [\n \"RS256\"\n ],\n \"scopes_supported\": [\n \"openid\",\n \"email\",\n \"profile\"\n ],\n \"token_endpoint_auth_methods_supported\": [\n \"client_secret_post\",\n \"client_secret_basic\"\n ],\n \"claims_supported\": [\n \"aud\",\n \"email\",\n \"email_verified\",\n \"exp\",\n \"family_name\",\n \"given_name\",\n \"iat\",\n \"iss\",\n \"locale\",\n \"name\",\n \"picture\",\n \"sub\"\n ],\n \"code_challenge_methods_supported\": [\n \"plain\",\n \"S256\"\n ],\n \"grant_types_supported\": [\n \"authorization_code\",\n \"refresh_token\",\n \"urn:ietf:params:oauth:grant-type:device_code\",\n \"urn:ietf:params:oauth:grant-type:jwt-bearer\"\n ]\n}")), //nolint:lll }, nil) - srv := wellknown.NewService(httpClient) + srv := fetcher.NewService(httpClient) resp, err := srv.GetOIDCConfiguration(context.TODO(), "https://any.com") assert.NoError(t, err) @@ -45,7 +45,7 @@ func TestWellKnown404(t *testing.T) { StatusCode: http.StatusNotFound, }, nil) - srv := wellknown.NewService(httpClient) + srv := fetcher.NewService(httpClient) resp, err := srv.GetOIDCConfiguration(context.TODO(), "https://any.com") assert.Nil(t, resp) @@ -60,7 +60,7 @@ func TestWellKnownInvalidJson(t *testing.T) { Body: io.NopCloser(strings.NewReader("{")), }, nil) - srv := wellknown.NewService(httpClient) + srv := fetcher.NewService(httpClient) resp, err := srv.GetOIDCConfiguration(context.TODO(), "https://any.com") assert.Nil(t, resp) @@ -73,7 +73,7 @@ func TestClientError(t *testing.T) { httpClient.EXPECT().Do(gomock.Any()).Return(nil, errors.New(text)) - srv := wellknown.NewService(httpClient) + srv := fetcher.NewService(httpClient) resp, err := srv.GetOIDCConfiguration(context.TODO(), "https://any.com") assert.Nil(t, resp) @@ -89,7 +89,7 @@ func TestClientBodyErr(t *testing.T) { Body: io.NopCloser(iotest.ErrReader(errors.New(text))), }, nil) - srv := wellknown.NewService(httpClient) + srv := fetcher.NewService(httpClient) resp, err := srv.GetOIDCConfiguration(context.TODO(), "https://any.com") assert.Nil(t, resp) diff --git a/pkg/service/wellknown/provider/testdata/profile.json b/pkg/service/wellknown/provider/testdata/profile.json new file mode 100644 index 000000000..ee7cf8814 --- /dev/null +++ b/pkg/service/wellknown/provider/testdata/profile.json @@ -0,0 +1,52 @@ +{ + "id": "profileID", + "version": "profileVersion", + "groupID": "", + "name": "random_name", + "active": false, + "oidcConfig": { + "signed_issuer_metadata_supported": true + }, + "vcConfig": { + "keyType": "ECDSASecp256k1DER", + "didMethod": "orb", + "status": { + "type": "", + "disable": false + }, + "sdjwt": {}, + "dataIntegrityProof": { + "enable": false, + "suiteType": "" + } + }, + "kmsConfig": { + "KMSType": "local", + "Endpoint": "https://example.com" + }, + "signingDID": { + "did": "did:orb:bank_issuer", + "kmsKeyID": "123", + "creator": "did:orb:bank_issuer#123" + }, + "credentialMetadata": { + "credentials_supported": [ + { + "id": "VerifiedEmployee_JWT" + } + ], + "display": [ + { + "name": "Test Issuer", + "locale": "en-US", + "url": "https://example.com", + "background_color": "#FFFFFF", + "text_color": "#000000", + "logo": { + "url": "https://example.com/credentials-logo.png", + "alt_text": "Issuer Logo" + } + } + ] + } +} \ No newline at end of file diff --git a/pkg/service/wellknown/provider/wellknown_service.go b/pkg/service/wellknown/provider/wellknown_service.go new file mode 100644 index 000000000..c2db927a5 --- /dev/null +++ b/pkg/service/wellknown/provider/wellknown_service.go @@ -0,0 +1,178 @@ +/* +Copyright Avast Software. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +//go:generate mockgen -destination wellknown_service_mocks_test.go -package provider -source=wellknown_service.go -mock_names kmsRegistry=MockKMSRegistry,cryptoJWTSigner=MockCryptoJWTSigner + +package provider + +import ( + "fmt" + "net/url" + "strings" + "time" + + josejwt "github.com/go-jose/go-jose/v3/jwt" + "github.com/samber/lo" + + "github.com/trustbloc/vc-go/jwt" + "github.com/trustbloc/vcs/pkg/doc/vc" + "github.com/trustbloc/vcs/pkg/kms" + profileapi "github.com/trustbloc/vcs/pkg/profile" + "github.com/trustbloc/vcs/pkg/restapi/v1/issuer" +) + +type kmsRegistry interface { + GetKeyManager(config *kms.Config) (kms.VCSKeyManager, error) +} + +type cryptoJWTSigner interface { + NewJWTSigned(claims interface{}, signerData *vc.Signer) (string, error) +} + +// JWTWellKnownOpenIDIssuerConfigurationClaims is JWT Claims extension +// by WellKnownOpenIDIssuerConfiguration (with custom "well_known_openid_issuer_configuration" claim). +type JWTWellKnownOpenIDIssuerConfigurationClaims struct { + *jwt.Claims + + WellKnownOpenIDIssuerConfiguration *issuer.WellKnownOpenIDIssuerConfiguration `json:"well_known_openid_issuer_configuration,omitempty"` //nolint:lll +} + +type Config struct { + ExternalHostURL string + KMSRegistry kmsRegistry + CryptoJWTSigner cryptoJWTSigner +} + +type Service struct { + externalHostURL string + kmsRegistry kmsRegistry + cryptoJWTSigner cryptoJWTSigner +} + +func NewService(config *Config) *Service { + return &Service{ + externalHostURL: config.ExternalHostURL, + kmsRegistry: config.KMSRegistry, + cryptoJWTSigner: config.CryptoJWTSigner, + } +} + +// GetOpenIDCredentialIssuerConfig returns issuer.WellKnownOpenIDIssuerConfiguration object, and +// it's JWT signed representation, if this feature is enabled for specific profile. +func (s *Service) GetOpenIDCredentialIssuerConfig( + issuerProfile *profileapi.Issuer) (*issuer.WellKnownOpenIDIssuerConfiguration, string, error) { + var ( + jwtSignedIssuerMetadata string + err error + ) + + issuerMetadata := s.getOpenIDIssuerConfig(issuerProfile) + + if issuerProfile.OIDCConfig != nil && issuerProfile.OIDCConfig.SignedIssuerMetadataSupported { + jwtSignedIssuerMetadata, err = s.signIssuerMetadata(issuerProfile, issuerMetadata) + if err != nil { + return nil, "", err + } + } + + return issuerMetadata, jwtSignedIssuerMetadata, nil +} + +func (s *Service) getOpenIDIssuerConfig(issuerProfile *profileapi.Issuer) *issuer.WellKnownOpenIDIssuerConfiguration { + host := s.externalHostURL + if !strings.HasSuffix(host, "/") { + host += "/" + } + + var finalCredentials []interface{} + for _, t := range issuerProfile.CredentialMetaData.CredentialsSupported { + if issuerProfile.VCConfig != nil { + t["cryptographic_binding_methods_supported"] = []string{string(issuerProfile.VCConfig.DIDMethod)} + t["cryptographic_suites_supported"] = []string{string(issuerProfile.VCConfig.KeyType)} + } + finalCredentials = append(finalCredentials, t) + } + + var display []issuer.CredentialDisplay + + if issuerProfile.CredentialMetaData.Display != nil { + display = make([]issuer.CredentialDisplay, 0, len(issuerProfile.CredentialMetaData.Display)) + + for _, d := range issuerProfile.CredentialMetaData.Display { + credentialDisplay := issuer.CredentialDisplay{ + BackgroundColor: lo.ToPtr(d.BackgroundColor), + Locale: lo.ToPtr(d.Locale), + Name: lo.ToPtr(d.Name), + TextColor: lo.ToPtr(d.TextColor), + Url: lo.ToPtr(d.URL), + } + + if d.Logo != nil { + credentialDisplay.Logo = &issuer.Logo{ + AltText: lo.ToPtr(d.Logo.AlternativeText), + Url: lo.ToPtr(d.Logo.URL), + } + } + + display = append(display, credentialDisplay) + } + } else { + display = []issuer.CredentialDisplay{ + { + Locale: lo.ToPtr("en-US"), + Name: lo.ToPtr(issuerProfile.Name), + Url: lo.ToPtr(issuerProfile.URL), + }, + } + } + + issuerURL, _ := url.JoinPath(s.externalHostURL, "issuer", issuerProfile.ID, issuerProfile.Version) + + final := &issuer.WellKnownOpenIDIssuerConfiguration{ + AuthorizationServer: fmt.Sprintf("%soidc/authorize", host), + BatchCredentialEndpoint: nil, // no support for now + CredentialEndpoint: fmt.Sprintf("%soidc/credential", host), + CredentialsSupported: finalCredentials, + CredentialIssuer: issuerURL, + Display: lo.ToPtr(display), + } + + return final +} + +func (s *Service) signIssuerMetadata( + profile *profileapi.Issuer, + meta *issuer.WellKnownOpenIDIssuerConfiguration, +) (string, error) { + keyManager, err := s.kmsRegistry.GetKeyManager(profile.KMSConfig) + if err != nil { + return "", fmt.Errorf("get kms: %w", err) + } + + signerData := &vc.Signer{ + KeyType: profile.VCConfig.KeyType, + KMSKeyID: profile.SigningDID.KMSKeyID, + KMS: keyManager, + SignatureType: profile.VCConfig.SigningAlgorithm, + Creator: profile.SigningDID.Creator, + } + + claims := &JWTWellKnownOpenIDIssuerConfigurationClaims{ + Claims: &jwt.Claims{ + Issuer: profile.SigningDID.DID, + Subject: profile.SigningDID.DID, + IssuedAt: josejwt.NewNumericDate(time.Now()), + }, + WellKnownOpenIDIssuerConfiguration: meta, + } + + signedIssuerMetadata, err := s.cryptoJWTSigner.NewJWTSigned(claims, signerData) + if err != nil { + return "", fmt.Errorf("sign issuer metadata: %w", err) + } + + return signedIssuerMetadata, nil +} diff --git a/pkg/service/wellknown/provider/wellknown_service_test.go b/pkg/service/wellknown/provider/wellknown_service_test.go new file mode 100644 index 000000000..2689bcb79 --- /dev/null +++ b/pkg/service/wellknown/provider/wellknown_service_test.go @@ -0,0 +1,248 @@ +/* +Copyright Avast Software. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package provider + +import ( + _ "embed" + "encoding/json" + "errors" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + + "github.com/trustbloc/vcs/pkg/doc/vc" + vcskms "github.com/trustbloc/vcs/pkg/kms" + profileapi "github.com/trustbloc/vcs/pkg/profile" + "github.com/trustbloc/vcs/pkg/restapi/v1/issuer" +) + +//go:embed testdata/profile.json +var profileJSON []byte //nolint:gochecknoglobals + +func TestController_GetOpenIDCredentialIssuerConfig(t *testing.T) { + var ( + externalHostURL = "https://example.com/" + mockTestIssuerProfile *profileapi.Issuer + mockKMSRegistry = NewMockKMSRegistry(gomock.NewController(t)) + mockCryptoJWTSigner = NewMockCryptoJWTSigner(gomock.NewController(t)) + ) + + tests := []struct { + name string + setup func() + check func(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration, jwt string, err error) + }{ + { + name: "Success", + setup: func() { + mockTestIssuerProfile = loadProfile(t) + + mockKMSRegistry.EXPECT().GetKeyManager(&vcskms.Config{ + KMSType: "local", + Endpoint: "https://example.com", + }).Return(nil, nil) + + mockCryptoJWTSigner.EXPECT().NewJWTSigned(gomock.Any(), &vc.Signer{ + Creator: "did:orb:bank_issuer#123", + KMSKeyID: "123", + KeyType: "ECDSASecp256k1DER", + }).Return("aa.bb.cc", nil) + }, + check: func(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration, jwt string, err error) { + checkWithSuffix(t, res) + + assert.Equal(t, "aa.bb.cc", jwt) + assert.Nil(t, err) + }, + }, + { + name: "Success signed issuer metadata is not supported", + setup: func() { + mockTestIssuerProfile = loadProfile(t) + mockTestIssuerProfile.OIDCConfig = nil + + mockKMSRegistry = NewMockKMSRegistry(gomock.NewController(t)) + mockCryptoJWTSigner = NewMockCryptoJWTSigner(gomock.NewController(t)) + }, + check: func(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration, jwt string, err error) { + checkWithSuffix(t, res) + + assert.Empty(t, "", jwt) + assert.Nil(t, err) + }, + }, + { + name: "Error kmsRegistry", + setup: func() { + mockTestIssuerProfile = loadProfile(t) + + mockKMSRegistry.EXPECT().GetKeyManager(&vcskms.Config{ + KMSType: "local", + Endpoint: "https://example.com", + }).Return(nil, errors.New("some error")) + + mockCryptoJWTSigner = NewMockCryptoJWTSigner(gomock.NewController(t)) + }, + check: func(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration, jwt string, err error) { + assert.Nil(t, res) + assert.Empty(t, jwt) + assert.ErrorContains(t, err, "get kms:") + }, + }, + { + name: "Error cryptoJWTSigner", + setup: func() { + mockTestIssuerProfile = loadProfile(t) + + mockKMSRegistry.EXPECT().GetKeyManager(&vcskms.Config{ + KMSType: "local", + Endpoint: "https://example.com", + }).Return(nil, nil) + + mockCryptoJWTSigner.EXPECT().NewJWTSigned(gomock.Any(), &vc.Signer{ + Creator: "did:orb:bank_issuer#123", + KMSKeyID: "123", + KeyType: "ECDSASecp256k1DER", + }).Return("", errors.New("some error")) + }, + check: func(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration, jwt string, err error) { + assert.Nil(t, res) + assert.Empty(t, jwt) + assert.ErrorContains(t, err, "sign issuer metadata:") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.setup() + + s := NewService(&Config{ + ExternalHostURL: externalHostURL, + KMSRegistry: mockKMSRegistry, + CryptoJWTSigner: mockCryptoJWTSigner, + }) + + res, jwt, err := s.GetOpenIDCredentialIssuerConfig(mockTestIssuerProfile) + + tt.check(t, res, jwt, err) + }) + } +} + +func TestService_getOpenIDIssuerConfig(t *testing.T) { + type fields struct { + externalHostURL string + } + type args struct { + getIssuerProfile func(t *testing.T) *profileapi.Issuer + } + tests := []struct { + name string + fields fields + args args + check func(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration) + }{ + { + name: "Success with suffix /", + fields: fields{ + externalHostURL: "https://example.com/", + }, + args: args{ + getIssuerProfile: loadProfile, + }, + check: checkWithSuffix, + }, + { + name: "Success without suffix /", + fields: fields{ + externalHostURL: "https://example.com", + }, + args: args{ + getIssuerProfile: loadProfile, + }, + check: checkWithSuffix, + }, + { + name: "Success empty issuerProfile.CredentialMetaData.Display", + fields: fields{ + externalHostURL: "https://example.com", + }, + args: args{ + getIssuerProfile: func(t *testing.T) *profileapi.Issuer { + profile := loadProfile(t) + profile.CredentialMetaData.Display = nil + + return profile + }, + }, + check: func(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration) { + t.Helper() + + assert.Equal(t, "https://example.com/oidc/authorize", res.AuthorizationServer) + assert.Nil(t, res.BatchCredentialEndpoint) + assert.Equal(t, "https://example.com/oidc/credential", res.CredentialEndpoint) + assert.Equal(t, "https://example.com/issuer/profileID/profileVersion", res.CredentialIssuer) + + assert.Len(t, res.CredentialsSupported, 1) + resMapped := (res.CredentialsSupported)[0].(map[string]interface{}) //nolint + assert.Equal(t, "VerifiedEmployee_JWT", resMapped["id"]) + assert.Equal(t, []string{"orb"}, resMapped["cryptographic_binding_methods_supported"]) + assert.Equal(t, []string{"ECDSASecp256k1DER"}, resMapped["cryptographic_suites_supported"]) + + assert.Nil(t, (*res.Display)[0].BackgroundColor) + assert.Nil(t, (*res.Display)[0].Logo) + assert.Nil(t, (*res.Display)[0].TextColor) + assert.Empty(t, (*res.Display)[0].Url) + assert.Equal(t, "en-US", *(*res.Display)[0].Locale) + assert.Equal(t, "random_name", *(*res.Display)[0].Name) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Service{ + externalHostURL: tt.fields.externalHostURL, + } + + tt.check(t, s.getOpenIDIssuerConfig(tt.args.getIssuerProfile(t))) + }) + } +} + +func loadProfile(t *testing.T) *profileapi.Issuer { + t.Helper() + + var profile *profileapi.Issuer + assert.NoError(t, json.Unmarshal(profileJSON, &profile)) + + return profile +} + +func checkWithSuffix(t *testing.T, res *issuer.WellKnownOpenIDIssuerConfiguration) { + t.Helper() + + assert.Equal(t, "https://example.com/oidc/authorize", res.AuthorizationServer) + assert.Nil(t, res.BatchCredentialEndpoint) + assert.Equal(t, "https://example.com/oidc/credential", res.CredentialEndpoint) + assert.Equal(t, "https://example.com/issuer/profileID/profileVersion", res.CredentialIssuer) + + assert.Len(t, res.CredentialsSupported, 1) + resMapped := (res.CredentialsSupported)[0].(map[string]interface{}) //nolint + assert.Equal(t, "VerifiedEmployee_JWT", resMapped["id"]) + assert.Equal(t, []string{"orb"}, resMapped["cryptographic_binding_methods_supported"]) + assert.Equal(t, []string{"ECDSASecp256k1DER"}, resMapped["cryptographic_suites_supported"]) + + assert.Equal(t, "#FFFFFF", *(*res.Display)[0].BackgroundColor) + assert.Equal(t, "en-US", *(*res.Display)[0].Locale) + assert.Equal(t, "Test Issuer", *(*res.Display)[0].Name) + assert.Equal(t, "https://example.com", *(*res.Display)[0].Url) + assert.Equal(t, "#000000", *(*res.Display)[0].TextColor) + + assert.Equal(t, "https://example.com/credentials-logo.png", *(*res.Display)[0].Logo.Url) + assert.Equal(t, "Issuer Logo", *(*res.Display)[0].Logo.AltText) +} diff --git a/test/bdd/fixtures/profile/profiles.json b/test/bdd/fixtures/profile/profiles.json index 0f415ba35..250928a5d 100644 --- a/test/bdd/fixtures/profile/profiles.json +++ b/test/bdd/fixtures/profile/profiles.json @@ -661,6 +661,7 @@ "pre-authorized_grant_anonymous_access_supported": true, "wallet_initiated_auth_flow_supported": true, "signed_credential_offer_supported": true, + "signed_issuer_metadata_supported": true, "claims_endpoint": "https://mock-login-consent.example.com:8099/claim-data?credentialType=CrudeProductCredential" }, "credentialTemplates": [ diff --git a/test/bdd/pkg/v1/oidc4vc/oidc4ci.go b/test/bdd/pkg/v1/oidc4vc/oidc4ci.go index b237ae447..532f39988 100644 --- a/test/bdd/pkg/v1/oidc4vc/oidc4ci.go +++ b/test/bdd/pkg/v1/oidc4vc/oidc4ci.go @@ -123,11 +123,10 @@ func (s *Steps) runOIDC4CIPreAuth(initiateOIDC4CIRequest initiateOIDC4CIRequest) } _, err = s.walletRunner.RunOIDC4CIPreAuth(&walletrunner.OIDC4CIConfig{ - InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Pin: *initiateOIDC4CIResponseData.UserPin, - JWTSignedCredentialOffer: true, + InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, + CredentialType: s.issuedCredentialType, + CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), + Pin: *initiateOIDC4CIResponseData.UserPin, }) if err != nil { return fmt.Errorf("s.walletRunner.RunOIDC4CIPreAuth: %w", err) @@ -258,15 +257,14 @@ func (s *Steps) runOIDC4CIAuthWithError(updatedClientID, errorContains string) e } err = s.walletRunner.RunOIDC4CI(&walletrunner.OIDC4CIConfig{ - InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, - ClientID: "oidc4vc_client", - Scope: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - JWTSignedCredentialOffer: true, + InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, + ClientID: "oidc4vc_client", + Scope: []string{"openid", "profile"}, + RedirectURI: "http://127.0.0.1/callback", + CredentialType: s.issuedCredentialType, + CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), + Login: "bdd-test", + Password: "bdd-test-pass", }, &walletrunner.Hooks{ BeforeTokenRequest: []walletrunner.OauthClientOpt{ walletrunner.WithClientID(updatedClientID), @@ -310,15 +308,14 @@ func (s *Steps) runOIDC4CIAuth() error { } err = s.walletRunner.RunOIDC4CI(&walletrunner.OIDC4CIConfig{ - InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, - ClientID: "oidc4vc_client", - Scope: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - JWTSignedCredentialOffer: true, + InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, + ClientID: "oidc4vc_client", + Scope: []string{"openid", "profile"}, + RedirectURI: "http://127.0.0.1/callback", + CredentialType: s.issuedCredentialType, + CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), + Login: "bdd-test", + Password: "bdd-test-pass", }, nil) if err != nil { return fmt.Errorf("s.walletRunner.RunOIDC4CI: %w", err) @@ -352,14 +349,13 @@ func (s *Steps) runOIDC4CIAuthWithClientRegistrationMethod(method string) error } config := &walletrunner.OIDC4CIConfig{ - InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, - Scope: []string{"openid", "profile"}, - RedirectURI: "http://127.0.0.1/callback", - CredentialType: s.issuedCredentialType, - CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), - Login: "bdd-test", - Password: "bdd-test-pass", - JWTSignedCredentialOffer: true, + InitiateIssuanceURL: initiateOIDC4CIResponseData.OfferCredentialURL, + Scope: []string{"openid", "profile"}, + RedirectURI: "http://127.0.0.1/callback", + CredentialType: s.issuedCredentialType, + CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string), + Login: "bdd-test", + Password: "bdd-test-pass", } switch method {