From 734f544abe90444bf23ea91d0beaa9a104ead375 Mon Sep 17 00:00:00 2001 From: reinkrul Date: Tue, 7 May 2024 06:54:20 +0200 Subject: [PATCH] IAM: match user-targeted OpenID4VP PDs against session-wallet (#3009) --- .codeclimate.yml | 1 + auth/api/iam/api.go | 22 +- auth/api/iam/api_test.go | 35 +- auth/api/iam/codegen_sillyness.go | 27 + auth/api/iam/codegen_sillyness_test.go | 39 + auth/api/iam/openid4vp.go | 68 +- auth/api/iam/openid4vp_test.go | 56 +- auth/api/iam/user.go | 14 +- auth/api/iam/user_test.go | 8 + cmd/root.go | 2 +- crypto/interface.go | 4 + crypto/memory.go | 49 + crypto/memory_test.go | 56 + crypto/mock.go | 85 +- e2e-tests/auth/run-tests.sh | 10 - .../{auth/selfsigned => }/browser/chrome.go | 12 +- e2e-tests/browser/client/iam/generated.go | 3022 +++++++++++++++++ e2e-tests/browser/client/iam/types.go | 65 + .../config/nuts.yaml | 12 + .../config/policy/zorgtoepassing.json | 144 + .../docker-compose.yml | 32 + .../openid4vp_employeecredential/main_test.go | 124 + .../openid4vp_employeecredential/openid4vp.go | 81 + .../openid4vp_employeecredential/run-test.sh | 15 + .../rfc019_selfsigned}/apps/selfsigned.go | 0 .../rfc019_selfsigned}/config/node/nuts.yaml | 0 .../rfc019_selfsigned}/docker-compose.yml | 0 .../rfc019_selfsigned}/main_test.go | 33 +- .../rfc019_selfsigned}/run-test.sh | 0 e2e-tests/browser/run-tests.sh | 19 + e2e-tests/browser/util.go | 62 + e2e-tests/oauth-flow/openid4vp/do-test.sh | 4 +- e2e-tests/run-tests.sh | 8 +- makefile | 1 + vcr/api/vcr/v2/client.go | 15 + vcr/holder/memory_wallet.go | 10 +- vcr/holder/presenter.go | 6 +- vcr/holder/presenter_test.go | 24 +- vcr/holder/sql_wallet.go | 4 +- vcr/pe/presentation_definition_test.go | 85 + vdr/api/v2/client.go | 4 +- vdr/api/v2/client_test.go | 6 +- vdr/cmd/cmd.go | 2 +- 43 files changed, 4121 insertions(+), 145 deletions(-) create mode 100644 auth/api/iam/codegen_sillyness.go create mode 100644 auth/api/iam/codegen_sillyness_test.go create mode 100644 crypto/memory.go create mode 100644 crypto/memory_test.go delete mode 100755 e2e-tests/auth/run-tests.sh rename e2e-tests/{auth/selfsigned => }/browser/chrome.go (70%) create mode 100644 e2e-tests/browser/client/iam/generated.go create mode 100644 e2e-tests/browser/client/iam/types.go create mode 100644 e2e-tests/browser/openid4vp_employeecredential/config/nuts.yaml create mode 100644 e2e-tests/browser/openid4vp_employeecredential/config/policy/zorgtoepassing.json create mode 100644 e2e-tests/browser/openid4vp_employeecredential/docker-compose.yml create mode 100644 e2e-tests/browser/openid4vp_employeecredential/main_test.go create mode 100644 e2e-tests/browser/openid4vp_employeecredential/openid4vp.go create mode 100755 e2e-tests/browser/openid4vp_employeecredential/run-test.sh rename e2e-tests/{auth/selfsigned => browser/rfc019_selfsigned}/apps/selfsigned.go (100%) rename e2e-tests/{auth/selfsigned => browser/rfc019_selfsigned}/config/node/nuts.yaml (100%) rename e2e-tests/{auth/selfsigned => browser/rfc019_selfsigned}/docker-compose.yml (100%) rename e2e-tests/{auth/selfsigned => browser/rfc019_selfsigned}/main_test.go (80%) rename e2e-tests/{auth/selfsigned => browser/rfc019_selfsigned}/run-test.sh (100%) create mode 100755 e2e-tests/browser/run-tests.sh create mode 100644 e2e-tests/browser/util.go diff --git a/.codeclimate.yml b/.codeclimate.yml index a6c9215560..4dea741cc2 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -17,6 +17,7 @@ exclude_patterns: - "**/*_mock.go" - "docs/**/*.go" - "**/*.pb.go" + - "e2e-tests/**/*.go" plugins: gofmt: enabled: true diff --git a/auth/api/iam/api.go b/auth/api/iam/api.go index 05f9936a38..0ab3bca40a 100644 --- a/auth/api/iam/api.go +++ b/auth/api/iam/api.go @@ -43,6 +43,7 @@ import ( "github.com/nuts-foundation/nuts-node/core" cryptoNuts "github.com/nuts-foundation/nuts-node/crypto" httpNuts "github.com/nuts-foundation/nuts-node/http" + "github.com/nuts-foundation/nuts-node/jsonld" "github.com/nuts-foundation/nuts-node/policy" "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/vcr" @@ -88,6 +89,7 @@ type Wrapper struct { auth auth.AuthenticationServices policyBackend policy.PDPBackend storageEngine storage.Engine + JSONLDManager jsonld.JSONLD vcr vcr.VCR vdr vdr.VDR jwtSigner cryptoNuts.JWTSigner @@ -95,7 +97,9 @@ type Wrapper struct { jar JAR } -func New(authInstance auth.AuthenticationServices, vcrInstance vcr.VCR, vdrInstance vdr.VDR, storageEngine storage.Engine, policyBackend policy.PDPBackend, jwtSigner cryptoNuts.JWTSigner) *Wrapper { +func New( + authInstance auth.AuthenticationServices, vcrInstance vcr.VCR, vdrInstance vdr.VDR, storageEngine storage.Engine, + policyBackend policy.PDPBackend, jwtSigner cryptoNuts.JWTSigner, jsonldManager jsonld.JSONLD) *Wrapper { templates := template.New("oauth2 templates") _, err := templates.ParseFS(assetsFS, "assets/*.html") if err != nil { @@ -107,6 +111,7 @@ func New(authInstance auth.AuthenticationServices, vcrInstance vcr.VCR, vdrInsta storageEngine: storageEngine, vcr: vcrInstance, vdr: vdrInstance, + JSONLDManager: jsonldManager, jwtSigner: jwtSigner, keyResolver: resolver.DIDKeyResolver{Resolver: vdrInstance.Resolver()}, jar: &jar{ @@ -283,8 +288,8 @@ func (r Wrapper) IntrospectAccessToken(_ context.Context, request IntrospectAcce if token.InputDescriptorConstraintIdMap != nil { for _, reserved := range []string{"iss", "sub", "exp", "iat", "active", "client_id", "scope"} { - if _, exists := token.InputDescriptorConstraintIdMap[reserved]; exists { - return nil, fmt.Errorf("IntrospectAccessToken: InputDescriptorConstraintIdMap contains reserved claim name '%s'", reserved) + if _, isReserved := token.InputDescriptorConstraintIdMap[reserved]; isReserved { + return nil, fmt.Errorf("IntrospectAccessToken: InputDescriptorConstraintIdMap contains reserved claim name: %s", reserved) } } response.AdditionalProperties = token.InputDescriptorConstraintIdMap @@ -339,7 +344,14 @@ func (r Wrapper) HandleAuthorizeRequest(ctx context.Context, request HandleAutho case responseTypeVPToken: // Options: // - OpenID4VP flow, vp_token is sent in Authorization Response - return r.handleAuthorizeRequestFromVerifier(ctx, *ownDID, authzParams) + // non-spec: if the scheme is openid4vp (URL starts with openid4vp:), the OpenID4VP request should be handled by a user wallet, rather than an organization wallet. + // Requests to user wallets can then be rendered as QR-code (or use a cloud wallet). + // Note that it can't be called from the outside, but only by internal dispatch (since Echo doesn't handle openid4vp:, obviously). + walletOwnerType := pe.WalletOwnerOrganization + if strings.HasPrefix(httpRequest.URL.String(), "openid4vp:") { + walletOwnerType = pe.WalletOwnerUser + } + return r.handleAuthorizeRequestFromVerifier(ctx, *ownDID, authzParams, walletOwnerType) default: // TODO: This should be a redirect? redirectURI, _ := url.Parse(session.RedirectURI) @@ -558,7 +570,7 @@ func (r Wrapper) RequestUserAccessToken(ctx context.Context, request RequestUser return nil, err } - // TODO: When we support authentication at an external IdP, + // Note: When we support authentication at an external IdP, // the properties below become conditionally required. if request.Body.PreauthorizedUser == nil { return nil, core.InvalidInputError("missing preauthorized_user") diff --git a/auth/api/iam/api_test.go b/auth/api/iam/api_test.go index 5b703dede3..42ad8e110b 100644 --- a/auth/api/iam/api_test.go +++ b/auth/api/iam/api_test.go @@ -322,10 +322,10 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { oauth.CodeChallengeMethodParam: "S256", } ctx.vdr.EXPECT().IsOwner(gomock.Any(), verifierDID).Return(true, nil) - ctx.jar.EXPECT().Parse(gomock.Any(), verifierDID, url.Values{"key":[]string{"test_value"}}).Return(requestParams, nil) + ctx.jar.EXPECT().Parse(gomock.Any(), verifierDID, url.Values{"key": []string{"test_value"}}).Return(requestParams, nil) // handleAuthorizeRequestFromHolder - expectedURL := "https://example.com/authorize?client_id=did%3Aweb%3Aexample.com%3Aiam%3Averifier&request_uri=https://example.com/oauth2/"+verifierDID.String()+"/request.jwt/&request_uri_method=get" + expectedURL := "https://example.com/authorize?client_id=did%3Aweb%3Aexample.com%3Aiam%3Averifier&request_uri=https://example.com/oauth2/" + verifierDID.String() + "/request.jwt/&request_uri_method=get" serverMetadata := oauth.AuthorizationServerMetadata{ AuthorizationEndpoint: "https://example.com/authorize", ClientIdSchemesSupported: []string{didScheme}, @@ -357,6 +357,16 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { }) t.Run("ok - response_type=vp_token ", func(t *testing.T) { ctx := newTestClient(t) + vmId := did.DIDURL{ + DID: verifierDID, + Fragment: "key", + DecodedFragment: "key", + } + kid := vmId.String() + key := cryptoNuts.NewTestKey(kid) + didDocument := did.Document{ID: verifierDID} + vm, _ := did.NewVerificationMethod(vmId, ssi.JsonWebKey2020, did.DID{}, key.Public()) + didDocument.AddAssertionMethod(vm) // HandleAuthorizeRequest requestParams := oauthParameters{ @@ -384,6 +394,12 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { RedirectURI: "https://example.com/iam/holder/cb", ResponseType: "code", }) + _ = ctx.client.userSessionStore().Put("session-id", UserSession{ + TenantDID: holderDID, + Wallet: UserWallet{ + DID: holderDID, + }, + }) clientMetadata := oauth.OAuthClientMetadata{VPFormats: oauth.DefaultOpenIDSupportedFormats()} ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil) pdEndpoint := "https://example.com/oauth2/did:web:example.com:iam:verifier/presentation_definition?scope=test" @@ -391,8 +407,12 @@ func TestWrapper_HandleAuthorizeRequest(t *testing.T) { ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), holderDID, pe.PresentationDefinition{}, clientMetadata.VPFormats, gomock.Any()).Return(&vc.VerifiablePresentation{}, &pe.PresentationSubmission{}, nil) ctx.iamClient.EXPECT().PostAuthorizationResponse(gomock.Any(), vc.VerifiablePresentation{}, pe.PresentationSubmission{}, "https://example.com/oauth2/did:web:example.com:iam:verifier/response", "state").Return("https://example.com/iam/holder/redirect", nil) - res, err := ctx.client.HandleAuthorizeRequest(requestContext(map[string]interface{}{}), - HandleAuthorizeRequestRequestObject{Did: holderDID.String()}) + res, err := ctx.client.HandleAuthorizeRequest(requestContext(map[string]interface{}{}, func(request *http.Request) { + request.Header = make(http.Header) + request.AddCookie(createUserSessionCookie("session-id", "/")) + }), HandleAuthorizeRequestRequestObject{ + Did: holderDID.String(), + }) require.NoError(t, err) assert.IsType(t, HandleAuthorizeRequest302Response{}, res) @@ -595,7 +615,7 @@ func TestWrapper_IntrospectAccessToken(t *testing.T) { res, err := ctx.client.IntrospectAccessToken(context.Background(), IntrospectAccessTokenRequestObject{Body: &TokenIntrospectionRequest{Token: "token"}}) - require.EqualError(t, err, "IntrospectAccessToken: InputDescriptorConstraintIdMap contains reserved claim name 'iss'") + require.EqualError(t, err, "IntrospectAccessToken: InputDescriptorConstraintIdMap contains reserved claim name: iss") require.Nil(t, res) }) @@ -1393,7 +1413,7 @@ func requireOAuthError(t *testing.T, err error, errorCode oauth.ErrorCode, error assert.Equal(t, errorDescription, oauthErr.Description) } -func requestContext(queryParams map[string]interface{}) context.Context { +func requestContext(queryParams map[string]interface{}, httpRequestFn ...func(header *http.Request)) context.Context { vals := url.Values{} for key, value := range queryParams { switch t := value.(type) { @@ -1412,6 +1432,9 @@ func requestContext(queryParams map[string]interface{}) context.Context { RawQuery: vals.Encode(), }, } + for _, fn := range httpRequestFn { + fn(httpRequest) + } return context.WithValue(audit.TestContext(), httpRequestContextKey{}, httpRequest) } diff --git a/auth/api/iam/codegen_sillyness.go b/auth/api/iam/codegen_sillyness.go new file mode 100644 index 0000000000..aa0e58370b --- /dev/null +++ b/auth/api/iam/codegen_sillyness.go @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package iam + +import "encoding/json" + +var _ json.Marshaler = IntrospectAccessToken200JSONResponse{} + +func (r IntrospectAccessToken200JSONResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(TokenIntrospectionResponse(r)) +} diff --git a/auth/api/iam/codegen_sillyness_test.go b/auth/api/iam/codegen_sillyness_test.go new file mode 100644 index 0000000000..a3fcea1caa --- /dev/null +++ b/auth/api/iam/codegen_sillyness_test.go @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package iam + +import ( + "encoding/json" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestIntrospectAccessToken200JSONResponse_MarshalJSON(t *testing.T) { + // deepmap/oapi-codegen generates TokenIntrospectionResponse.MarshalJSON() function to support additionalProperties. + // But, the type being marshalled (due to the Strict Server Interface) is IntrospectAccessToken200JSONResponse + // which is a type definition for the TokenIntrospectionResponse type, which causes the custom MarshalJSON function to be ignored. + // This, in turn, causes additionalProperties not to be marshalled. + // The only way to circumvent this is to have IntrospectAccessToken200JSONResponse implement the json.Marshaler interface, + // and have it call the TokenIntrospectionResponse.MarshalJSON() function. + response := TokenIntrospectionResponse{AdditionalProperties: map[string]interface{}{ + "message": "hello", + }} + asJSON, _ := json.Marshal(IntrospectAccessToken200JSONResponse(response)) + assert.JSONEq(t, `{"active":false, "message":"hello"}`, string(asJSON)) +} diff --git a/auth/api/iam/openid4vp.go b/auth/api/iam/openid4vp.go index 7caf4d2679..5e33a6a218 100644 --- a/auth/api/iam/openid4vp.go +++ b/auth/api/iam/openid4vp.go @@ -22,9 +22,14 @@ import ( "context" "errors" "fmt" + "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" + "github.com/nuts-foundation/nuts-node/vdr/didjwk" + "github.com/nuts-foundation/nuts-node/vdr/resolver" + "net/http" "net/url" "slices" + "strings" "time" "github.com/nuts-foundation/go-did/did" @@ -213,6 +218,13 @@ func (r Wrapper) nextOpenID4VPFlow(ctx context.Context, state string, session OA RedirectURI: session.redirectURI(), } } + if *walletOwnerType == pe.WalletOwnerUser { + // User wallet, make an openid4vp:// request URL + var newRequestURL url.URL + newRequestURL.Scheme = "openid4vp" + newRequestURL.RawQuery = authServerURL.RawQuery + authServerURL = &newRequestURL + } // use nonce and state to store authorization request in session store if err = r.oauthNonceStore().Put(nonce, state); err != nil { @@ -240,7 +252,7 @@ func (r Wrapper) nextOpenID4VPFlow(ctx context.Context, state string, session OA // there are way more error conditions that listed at: https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-error-response // missing or invalid parameters are all mapped to invalid_request // any operation that fails is mapped to server_error, this includes unreachable or broken backends. -func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, walletDID did.DID, params oauthParameters) (HandleAuthorizeRequestResponseObject, error) { +func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, tenantDID did.DID, params oauthParameters, walletOwnerType WalletOwnerType) (HandleAuthorizeRequestResponseObject, error) { responseMode := params.get(responseModeParam) if responseMode != responseModeDirectPost { return nil, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "invalid response_mode parameter"} @@ -272,6 +284,14 @@ func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, walletD if nonce == "" { return r.sendAndHandleDirectPostError(ctx, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "missing nonce parameter"}, responseURI, state) } + + // TODO: Create session if it does not exist (use client state to get original Authorization Code request)? + // Although it would be quite weird (maybe it expired). + userSession, err := r.loadUserSession(ctx.Value(httpRequestContextKey{}).(*http.Request), tenantDID, nil) + if userSession == nil { + return nil, oauth.OAuth2Error{Code: oauth.InvalidRequest, InternalError: err, Description: "no user session found"} + } + // get verifier metadata metadata, err := r.auth.IAMClient().ClientMetadata(ctx, params.get(clientMetadataURIParam)) if err != nil { @@ -293,25 +313,61 @@ func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, walletD Expires: time.Now().Add(15 * time.Minute), Nonce: nonce, } - vp, submission, err := r.vcr.Wallet().BuildSubmission(ctx, walletDID, *presentationDefinition, metadata.VPFormats, buildParams) + + targetWallet := r.vcr.Wallet() + walletDID := tenantDID + if walletOwnerType == pe.WalletOwnerUser { + // User wallet + var privateKey jwk.Key + privateKey, err = userSession.Wallet.Key() + walletDID = userSession.Wallet.DID + targetWallet = holder.NewMemoryWallet( + r.JSONLDManager.DocumentLoader(), + resolver.DIDKeyResolver{Resolver: didjwk.NewResolver()}, + crypto.MemoryJWTSigner{Key: privateKey}, + map[did.DID][]vc.VerifiableCredential{userSession.Wallet.DID: userSession.Wallet.Credentials}, + ) + } + vp, submission, err := targetWallet.BuildSubmission(ctx, walletDID, *presentationDefinition, metadata.VPFormats, buildParams) if err != nil { if errors.Is(err, holder.ErrNoCredentials) { - return r.sendAndHandleDirectPostError(ctx, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: "no credentials available"}, responseURI, state) + return r.sendAndHandleDirectPostError(ctx, oauth.OAuth2Error{Code: oauth.InvalidRequest, Description: fmt.Sprintf("no credentials available (PD ID: %s, wallet: %s)", presentationDefinition.Id, walletDID)}, responseURI, state) } return r.sendAndHandleDirectPostError(ctx, oauth.OAuth2Error{Code: oauth.ServerError, Description: err.Error()}, responseURI, state) } // any error here is a server error, might need a fixup to prevent exposing to a user - return r.sendAndHandleDirectPost(ctx, *vp, *submission, responseURI, state) + return r.sendAndHandleDirectPost(ctx, tenantDID, *vp, *submission, responseURI, state) } // sendAndHandleDirectPost sends OpenID4VP direct_post to the verifier. The verifier responds with a redirect to the client (including error fields if needed). // If the direct post fails, the user-agent will be redirected back to the client with an error. (Original redirect_uri). -func (r Wrapper) sendAndHandleDirectPost(ctx context.Context, vp vc.VerifiablePresentation, presentationSubmission pe.PresentationSubmission, verifierResponseURI string, state string) (HandleAuthorizeRequestResponseObject, error) { +func (r Wrapper) sendAndHandleDirectPost(ctx context.Context, walletDID did.DID, vp vc.VerifiablePresentation, presentationSubmission pe.PresentationSubmission, verifierResponseURI string, state string) (HandleAuthorizeRequestResponseObject, error) { redirectURI, err := r.auth.IAMClient().PostAuthorizationResponse(ctx, vp, presentationSubmission, verifierResponseURI, state) if err != nil { return nil, err } + // Redirect URI starting with openid4vp: is a signal from the OpenID4VP verifier + // that it requires another Verifiable Presentation, but this time from a user wallet. + if strings.HasPrefix(redirectURI, "openid4vp:") { + parsedRedirectURI, err := url.Parse(redirectURI) + if err != nil { + return nil, fmt.Errorf("verifier returned an invalid redirect URI: %w", err) + } + // Dispatch a new HTTP request to the local OpenID4VP wallet's authorization endpoint that includes request parameters, + // but with openid4vp: as scheme. + originalRequest := ctx.Value(httpRequestContextKey{}).(*http.Request) + dispatchHttpRequest := *originalRequest + dispatchHttpRequest.URL = parsedRedirectURI + ctx = context.WithValue(ctx, httpRequestContextKey{}, &dispatchHttpRequest) + response, err := r.HandleAuthorizeRequest(ctx, HandleAuthorizeRequestRequestObject{ + Did: walletDID.String(), + }) + if err != nil { + return nil, err + } + redirectURI = response.(HandleAuthorizeRequest302Response).Headers.Location + } return HandleAuthorizeRequest302Response{ HandleAuthorizeRequest302ResponseHeaders{ Location: redirectURI, @@ -609,7 +665,7 @@ func (r Wrapper) handleAccessTokenRequest(ctx context.Context, request HandleTok if !validatePKCEParams(oauthSession.PKCEParams) { return nil, oauthError(oauth.InvalidGrant, "invalid code_verifier") } - + // All done, issue access token walletDID, err := did.ParseDID(oauthSession.ClientID) if err != nil { return nil, err diff --git a/auth/api/iam/openid4vp_test.go b/auth/api/iam/openid4vp_test.go index d038b58c3f..a6da32a4fc 100644 --- a/auth/api/iam/openid4vp_test.go +++ b/auth/api/iam/openid4vp_test.go @@ -168,20 +168,33 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { oauth.StateParam: "state", } } - session := OAuthSession{ + authzCodeSession := OAuthSession{ SessionID: "token", OwnDID: &holderDID, RedirectURI: "https://example.com/iam/holder/cb", VerifierDID: &verifierDID, } + userSession := UserSession{ + TenantDID: holderDID, + Wallet: UserWallet{ + DID: did.MustParseDID("did:jwk:123"), + }, + } + httpRequest := &http.Request{ + Header: http.Header{}, + } + const userSessionID = "session_id" + httpRequest.AddCookie(createUserSessionCookie(userSessionID, "/")) + httpRequestCtx := context.WithValue(context.Background(), httpRequestContextKey{}, httpRequest) t.Run("invalid client_id", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() params[oauth.ClientIDParam] = "did:nuts:1" expectPostError(t, ctx, oauth.InvalidRequest, "invalid client_id parameter (only did:web is supported)", responseURI, "state") + _ = ctx.client.userSessionStore().Put(userSessionID, userSession) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) @@ -191,7 +204,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { params[clientIDSchemeParam] = "other" expectPostError(t, ctx, oauth.InvalidRequest, "invalid client_id_scheme parameter", responseURI, "state") - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) @@ -200,8 +213,9 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { params := defaultParams() ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(nil, assert.AnError) expectPostError(t, ctx, oauth.ServerError, "failed to get client metadata (verifier)", responseURI, "state") + _ = ctx.client.userSessionStore().Put(userSessionID, userSession) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) @@ -211,7 +225,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { delete(params, oauth.NonceParam) expectPostError(t, ctx, oauth.InvalidRequest, "missing nonce parameter", responseURI, "state") - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) @@ -220,8 +234,9 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { params := defaultParams() ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(nil, assert.AnError) expectPostError(t, ctx, oauth.ServerError, "failed to get client metadata (verifier)", responseURI, "state") + _ = ctx.client.userSessionStore().Put(userSessionID, userSession) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) @@ -230,7 +245,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { params := defaultParams() delete(params, responseModeParam) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) assert.EqualError(t, err, "invalid_request - invalid response_mode parameter") }) @@ -239,7 +254,7 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { params := defaultParams() delete(params, responseURIParam) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) assert.EqualError(t, err, "invalid_request - missing response_uri parameter") }) @@ -248,45 +263,48 @@ func TestWrapper_handleAuthorizeRequestFromVerifier(t *testing.T) { params := defaultParams() delete(params, responseURIParam) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.Error(t, err) }) t.Run("invalid presentation_definition_uri", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - putState(ctx, "state", session) + putState(ctx, "state", authzCodeSession) ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil) ctx.iamClient.EXPECT().PresentationDefinition(gomock.Any(), pdEndpoint).Return(nil, assert.AnError) expectPostError(t, ctx, oauth.InvalidPresentationDefinitionURI, "failed to retrieve presentation definition on https://example.com/iam/verifier/presentation_definition?scope=test", responseURI, "state") + _ = ctx.client.userSessionStore().Put(userSessionID, userSession) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) t.Run("failed to create verifiable presentation", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - putState(ctx, "state", session) + putState(ctx, "state", authzCodeSession) ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil) ctx.iamClient.EXPECT().PresentationDefinition(gomock.Any(), pdEndpoint).Return(&pe.PresentationDefinition{}, nil) ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), holderDID, pe.PresentationDefinition{}, clientMetadata.VPFormats, gomock.Any()).Return(nil, nil, assert.AnError) expectPostError(t, ctx, oauth.ServerError, assert.AnError.Error(), responseURI, "state") + _ = ctx.client.userSessionStore().Put(userSessionID, userSession) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) t.Run("missing credentials in wallet", func(t *testing.T) { ctx := newTestClient(t) params := defaultParams() - putState(ctx, "state", session) + putState(ctx, "state", authzCodeSession) ctx.iamClient.EXPECT().ClientMetadata(gomock.Any(), "https://example.com/.well-known/authorization-server/iam/verifier").Return(&clientMetadata, nil) ctx.iamClient.EXPECT().PresentationDefinition(gomock.Any(), pdEndpoint).Return(&pe.PresentationDefinition{}, nil) ctx.wallet.EXPECT().BuildSubmission(gomock.Any(), holderDID, pe.PresentationDefinition{}, clientMetadata.VPFormats, gomock.Any()).Return(nil, nil, holder.ErrNoCredentials) - expectPostError(t, ctx, oauth.InvalidRequest, "no credentials available", responseURI, "state") + expectPostError(t, ctx, oauth.InvalidRequest, "no credentials available (PD ID: , wallet: did:web:example.com:iam:holder)", responseURI, "state") + _ = ctx.client.userSessionStore().Put(userSessionID, userSession) - _, err := ctx.client.handleAuthorizeRequestFromVerifier(context.Background(), holderDID, params) + _, err := ctx.client.handleAuthorizeRequestFromVerifier(httpRequestCtx, holderDID, params, pe.WalletOwnerOrganization) require.NoError(t, err) }) @@ -384,7 +402,7 @@ func TestWrapper_HandleAuthorizeResponse(t *testing.T) { require.NoError(t, err) actualRedirectURL, _ := url.Parse(response.(HandleAuthorizeResponse200JSONResponse).RedirectURI) - assert.True(t, strings.HasPrefix(actualRedirectURL.String(), "https://redirect-url?")) + assert.True(t, strings.HasPrefix(actualRedirectURL.String(), "openid4vp:")) assert.Equal(t, verifierDID.String(), actualRedirectURL.Query().Get("client_id")) // etc }) @@ -773,7 +791,9 @@ func Test_validatePresentationNonce(t *testing.T) { // expectPostError is a convenience method to add an expectation to the iamClient mock. // it checks if the right error is posted to the verifier. func expectPostError(t *testing.T, ctx *testCtx, errorCode oauth.ErrorCode, description string, expectedResponseURI string, verifierClientState string) { + t.Helper() ctx.iamClient.EXPECT().PostError(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, err oauth.OAuth2Error, responseURI string, state string) (string, error) { + t.Helper() assert.Equal(t, errorCode, err.Code) assert.Equal(t, description, err.Description) assert.Equal(t, expectedResponseURI, responseURI) @@ -789,7 +809,7 @@ func TestWrapper_sendAndHandleDirectPost(t *testing.T) { ctx := newTestClient(t) ctx.iamClient.EXPECT().PostAuthorizationResponse(gomock.Any(), gomock.Any(), gomock.Any(), "response", "").Return("", assert.AnError) - _, err := ctx.client.sendAndHandleDirectPost(context.Background(), vc.VerifiablePresentation{}, pe.PresentationSubmission{}, "response", "") + _, err := ctx.client.sendAndHandleDirectPost(context.Background(), walletDID, vc.VerifiablePresentation{}, pe.PresentationSubmission{}, "response", "") assert.Equal(t, assert.AnError, err) }) diff --git a/auth/api/iam/user.go b/auth/api/iam/user.go index 306bea8703..8c453d0349 100644 --- a/auth/api/iam/user.go +++ b/auth/api/iam/user.go @@ -182,7 +182,7 @@ func (r Wrapper) loadUserSession(cookies CookieReader, tenantDID did.DID, preAut return nil, errors.New("unknown or expired session") } else if err != nil { // other error occurred - return nil, err + return nil, fmt.Errorf("invalid user session: %w", err) } // Note that the session itself does not have an expiration field: // it depends on the session store to clean up when it expires. @@ -192,7 +192,7 @@ func (r Wrapper) loadUserSession(cookies CookieReader, tenantDID did.DID, preAut // If the existing session was created for a pre-authorized user, the call to RequestUserAccessToken() must be // for the same user. // TODO: When we support external Identity Providers, make sure the existing session was not for a preauthorized user. - if *preAuthorizedUser != *session.PreAuthorizedUser { + if preAuthorizedUser != nil && *preAuthorizedUser != *session.PreAuthorizedUser { return nil, errors.New("session belongs to another pre-authorized user") } return session, nil @@ -217,7 +217,12 @@ func (r Wrapper) createUserSession(ctx echo.Context, session UserSession) error } else { path = "/" } - ctx.SetCookie(&http.Cookie{ + ctx.SetCookie(createUserSessionCookie(sessionID, path)) + return nil +} + +func createUserSessionCookie(sessionID string, path string) *http.Cookie { + return &http.Cookie{ Name: userSessionCookieName, Value: sessionID, Path: path, @@ -225,8 +230,7 @@ func (r Wrapper) createUserSession(ctx echo.Context, session UserSession) error Secure: true, HttpOnly: true, // do not let JavaScript SameSite: http.SameSiteStrictMode, // do not allow the cookie to be sent with cross-site requests - }) - return nil + } } func (r Wrapper) createUserWallet(ctx context.Context, issuerDID did.DID, userDetails UserDetails) (*UserWallet, error) { diff --git a/auth/api/iam/user_test.go b/auth/api/iam/user_test.go index 87e6f6f3cc..bd89e1b2fb 100644 --- a/auth/api/iam/user_test.go +++ b/auth/api/iam/user_test.go @@ -340,3 +340,11 @@ func TestWrapper_loadUserSession(t *testing.T) { assert.Nil(t, actual) }) } + +func Test_generateUserSessionJWK(t *testing.T) { + key, userDID, err := generateUserSessionJWK() + require.NoError(t, err) + require.NotNil(t, key) + require.NotNil(t, userDID) + assert.True(t, strings.HasPrefix(userDID.String(), "did:jwk:")) +} diff --git a/cmd/root.go b/cmd/root.go index 46b2ed3ffe..f2605852fe 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -224,7 +224,7 @@ func CreateSystem(shutdownCallback context.CancelFunc) *core.System { system.RegisterRoutes(statusEngine.(core.Routable)) system.RegisterRoutes(metricsEngine.(core.Routable)) system.RegisterRoutes(&authAPIv1.Wrapper{Auth: authInstance, CredentialResolver: credentialInstance}) - system.RegisterRoutes(authIAMAPI.New(authInstance, credentialInstance, vdrInstance, storageInstance, policyInstance, cryptoInstance)) + system.RegisterRoutes(authIAMAPI.New(authInstance, credentialInstance, vdrInstance, storageInstance, policyInstance, cryptoInstance, jsonld)) system.RegisterRoutes(&authMeansAPI.Wrapper{Auth: authInstance}) system.RegisterRoutes(&didmanAPI.Wrapper{Didman: didmanInstance}) system.RegisterRoutes(&discoveryAPI.Wrapper{Client: discoveryInstance}) diff --git a/crypto/interface.go b/crypto/interface.go index 8c081d5ee6..63f641aab9 100644 --- a/crypto/interface.go +++ b/crypto/interface.go @@ -51,6 +51,7 @@ type KeyResolver interface { // KeyStore defines the functions for working with private keys. type KeyStore interface { Decrypter + JsonWebEncryptor KeyCreator KeyResolver JWTSigner @@ -83,7 +84,10 @@ type JWTSigner interface { // context is used to pass audit information. // Returns ErrPrivateKeyNotFound when the private key is not present. SignJWS(ctx context.Context, payload []byte, headers map[string]interface{}, key interface{}, detached bool) (string, error) +} +// JsonWebEncryptor is the interface used to encrypt and decrypt JWE messages. +type JsonWebEncryptor interface { // EncryptJWE encrypts a payload as bytes into a JWE message with the given key and kid. // The publicKey must be a public key // The kid must be the KeyID and will be placed in the header, if not set. diff --git a/crypto/memory.go b/crypto/memory.go new file mode 100644 index 0000000000..fce8be9014 --- /dev/null +++ b/crypto/memory.go @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package crypto + +import ( + "context" + "errors" + "github.com/lestrrat-go/jwx/v2/jwk" +) + +var _ JWTSigner = &MemoryJWTSigner{} +var errNotSupportedForInMemoryKeyStore = errors.New("not supported on in-memory key store") + +// MemoryJWTSigner is a JWTSigner implementation that performs cryptographic operations on an in-memory JWK. +// This should only be used for low-assurance use cases, e.g. session-bound user keys. +type MemoryJWTSigner struct { + Key jwk.Key +} + +func (m MemoryJWTSigner) SignJWT(_ context.Context, claims map[string]interface{}, headers map[string]interface{}, rawKey interface{}) (string, error) { + keyID, ok := rawKey.(string) + if !ok { + return "", errors.New("key should be string (key ID)") + } + if keyID != m.Key.KeyID() { + return "", ErrPrivateKeyNotFound + } + return signJWT(m.Key, claims, headers) +} + +func (m MemoryJWTSigner) SignJWS(_ context.Context, _ []byte, _ map[string]interface{}, _ interface{}, _ bool) (string, error) { + return "", errNotSupportedForInMemoryKeyStore +} diff --git a/crypto/memory_test.go b/crypto/memory_test.go new file mode 100644 index 0000000000..e0cc265ce3 --- /dev/null +++ b/crypto/memory_test.go @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package crypto + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestMemoryKeyStore_SignJWS(t *testing.T) { + _, err := MemoryJWTSigner{}.SignJWS(context.Background(), nil, nil, nil, false) + assert.ErrorIs(t, err, errNotSupportedForInMemoryKeyStore) +} + +func TestMemoryKeyStore_SignJWT(t *testing.T) { + pk, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + privateKeyJWK, _ := jwk.FromRaw(pk) + privateKeyJWK.Set(jwk.KeyIDKey, "123") + alg, _ := ecAlg(pk) + privateKeyJWK.Set(jwk.AlgorithmKey, alg) + + t.Run("ok", func(t *testing.T) { + signedJWT, err := MemoryJWTSigner{ + Key: privateKeyJWK, + }.SignJWT(context.Background(), nil, nil, "123") + assert.NoError(t, err) + assert.NotEmpty(t, signedJWT) + }) + t.Run("unknown key", func(t *testing.T) { + _, err := MemoryJWTSigner{ + Key: privateKeyJWK, + }.SignJWT(context.Background(), nil, nil, "456") + assert.ErrorIs(t, err, ErrPrivateKeyNotFound) + }) +} diff --git a/crypto/mock.go b/crypto/mock.go index 4448ccc1ff..0d2bd28ed7 100644 --- a/crypto/mock.go +++ b/crypto/mock.go @@ -353,37 +353,6 @@ func (m *MockJWTSigner) EXPECT() *MockJWTSignerMockRecorder { return m.recorder } -// DecryptJWE mocks base method. -func (m *MockJWTSigner) DecryptJWE(ctx context.Context, message string) ([]byte, map[string]any, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DecryptJWE", ctx, message) - ret0, _ := ret[0].([]byte) - ret1, _ := ret[1].(map[string]any) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// DecryptJWE indicates an expected call of DecryptJWE. -func (mr *MockJWTSignerMockRecorder) DecryptJWE(ctx, message any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptJWE", reflect.TypeOf((*MockJWTSigner)(nil).DecryptJWE), ctx, message) -} - -// EncryptJWE mocks base method. -func (m *MockJWTSigner) EncryptJWE(ctx context.Context, payload []byte, headers map[string]any, publicKey any) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EncryptJWE", ctx, payload, headers, publicKey) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// EncryptJWE indicates an expected call of EncryptJWE. -func (mr *MockJWTSignerMockRecorder) EncryptJWE(ctx, payload, headers, publicKey any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptJWE", reflect.TypeOf((*MockJWTSigner)(nil).EncryptJWE), ctx, payload, headers, publicKey) -} - // SignJWS mocks base method. func (m *MockJWTSigner) SignJWS(ctx context.Context, payload []byte, headers map[string]any, key any, detached bool) (string, error) { m.ctrl.T.Helper() @@ -414,6 +383,60 @@ func (mr *MockJWTSignerMockRecorder) SignJWT(ctx, claims, headers, key any) *gom return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignJWT", reflect.TypeOf((*MockJWTSigner)(nil).SignJWT), ctx, claims, headers, key) } +// MockJsonWebEncryptor is a mock of JsonWebEncryptor interface. +type MockJsonWebEncryptor struct { + ctrl *gomock.Controller + recorder *MockJsonWebEncryptorMockRecorder +} + +// MockJsonWebEncryptorMockRecorder is the mock recorder for MockJsonWebEncryptor. +type MockJsonWebEncryptorMockRecorder struct { + mock *MockJsonWebEncryptor +} + +// NewMockJsonWebEncryptor creates a new mock instance. +func NewMockJsonWebEncryptor(ctrl *gomock.Controller) *MockJsonWebEncryptor { + mock := &MockJsonWebEncryptor{ctrl: ctrl} + mock.recorder = &MockJsonWebEncryptorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockJsonWebEncryptor) EXPECT() *MockJsonWebEncryptorMockRecorder { + return m.recorder +} + +// DecryptJWE mocks base method. +func (m *MockJsonWebEncryptor) DecryptJWE(ctx context.Context, message string) ([]byte, map[string]any, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DecryptJWE", ctx, message) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(map[string]any) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// DecryptJWE indicates an expected call of DecryptJWE. +func (mr *MockJsonWebEncryptorMockRecorder) DecryptJWE(ctx, message any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptJWE", reflect.TypeOf((*MockJsonWebEncryptor)(nil).DecryptJWE), ctx, message) +} + +// EncryptJWE mocks base method. +func (m *MockJsonWebEncryptor) EncryptJWE(ctx context.Context, payload []byte, headers map[string]any, publicKey any) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EncryptJWE", ctx, payload, headers, publicKey) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EncryptJWE indicates an expected call of EncryptJWE. +func (mr *MockJsonWebEncryptorMockRecorder) EncryptJWE(ctx, payload, headers, publicKey any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptJWE", reflect.TypeOf((*MockJsonWebEncryptor)(nil).EncryptJWE), ctx, payload, headers, publicKey) +} + // MockKey is a mock of Key interface. type MockKey struct { ctrl *gomock.Controller diff --git a/e2e-tests/auth/run-tests.sh b/e2e-tests/auth/run-tests.sh deleted file mode 100755 index c9b9e15fc8..0000000000 --- a/e2e-tests/auth/run-tests.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -set -e # make script fail if any of the tests returns a non-zero exit code - -echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" -echo "!! Running test: Employee Credential !!" -echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" -pushd selfsigned -./run-test.sh -popd diff --git a/e2e-tests/auth/selfsigned/browser/chrome.go b/e2e-tests/browser/chrome.go similarity index 70% rename from e2e-tests/auth/selfsigned/browser/chrome.go rename to e2e-tests/browser/chrome.go index a1b20a9e3e..fba9551f7e 100644 --- a/e2e-tests/auth/selfsigned/browser/chrome.go +++ b/e2e-tests/browser/chrome.go @@ -29,9 +29,19 @@ func NewChrome(headless bool) (context.Context, context.CancelFunc) { var execCtx context.Context var execCtxCancel context.CancelFunc if headless { + // TODO: Fix acceptInsecureCerts for headless execCtx, execCtxCancel = chromedp.NewRemoteAllocator(context.Background(), "http://localhost:9222") } else { - execCtx, execCtxCancel = chromedp.NewExecAllocator(context.Background(), append(chromedp.DefaultExecAllocatorOptions[:], chromedp.Flag("headless", false))...) + options := append(chromedp.DefaultExecAllocatorOptions[:], + chromedp.Flag("headless", false), + // Required for non-headless mode, which runs on the host machine. + // Otherwise, it can't resolve docker hostnames (so map them to localhost). + chromedp.Flag("host-resolver-rules", "MAP nodeA localhost"), + chromedp.Flag("host-rules", "MAP nodeA localhost"), + chromedp.Flag("ignore-certificate-errors", true), + chromedp.Flag("acceptInsecureCerts", true), + ) + execCtx, execCtxCancel = chromedp.NewExecAllocator(context.Background(), options...) } ctx, cancel := chromedp.NewContext( diff --git a/e2e-tests/browser/client/iam/generated.go b/e2e-tests/browser/client/iam/generated.go new file mode 100644 index 0000000000..15730d5867 --- /dev/null +++ b/e2e-tests/browser/client/iam/generated.go @@ -0,0 +1,3022 @@ +// Package iam provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT. +package iam + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/oapi-codegen/runtime" +) + +const ( + JwtBearerAuthScopes = "jwtBearerAuth.Scopes" +) + +// RedirectResponseWithID defines model for RedirectResponseWithID. +type RedirectResponseWithID struct { + // RedirectUri The URL to which the user-agent will be redirected after the authorization request. + RedirectUri string `json:"redirect_uri"` + + // SessionId The session ID that can be used to retrieve the access token by the calling application. + SessionId string `json:"session_id"` +} + +// TokenIntrospectionRequest Token introspection request as described in RFC7662 section 2.1 +// Alongside the defined properties, it can return values (additionalProperties) from the Verifiable Credentials that resulted from the Presentation Exchange. +type TokenIntrospectionRequest struct { + Token string `json:"token"` +} + +// TokenIntrospectionResponse Token introspection response as described in RFC7662 section 2.2 +type TokenIntrospectionResponse struct { + // Active True if the token is active, false if the token is expired, malformed etc. Required per RFC7662 + Active bool `json:"active"` + + // Aud RFC7662 - Service-specific string identifier or list of string identifiers representing the intended audience for this token, as defined in JWT [RFC7519]. + Aud *string `json:"aud,omitempty"` + + // ClientId The client (DID) the access token was issued to + ClientId *string `json:"client_id,omitempty"` + + // Exp Expiration date in seconds since UNIX epoch + Exp *int `json:"exp,omitempty"` + + // Iat Issuance time in seconds since UNIX epoch + Iat *int `json:"iat,omitempty"` + + // Iss Contains the DID of the authorizer. Should be equal to 'sub' + Iss *string `json:"iss,omitempty"` + + // PresentationDefinitions Presentation Definitions, as described in Presentation Exchange specification, fulfilled to obtain the access token + // The map key is the wallet owner (user/organization) + PresentationDefinitions *RequiredPresentationDefinitions `json:"presentation_definitions,omitempty"` + + // PresentationSubmissions Mapping of Presentation Definition IDs that were fulfilled to Presentation Submissions. + PresentationSubmissions *map[string]PresentationSubmission `json:"presentation_submissions,omitempty"` + + // Scope granted scopes + Scope *string `json:"scope,omitempty"` + + // Sub Contains the DID of the resource owner + Sub *string `json:"sub,omitempty"` + + // Vps The Verifiable Presentations that were used to request the access token using the same encoding as used in the access token request. + Vps *[]VerifiablePresentation `json:"vps,omitempty"` + AdditionalProperties map[string]interface{} `json:"-"` +} + +// UserDetails Claims about the authorized user. +type UserDetails struct { + // Id Machine-readable identifier, uniquely identifying the user in the issuing system. + Id string `json:"id"` + + // Name Human-readable name of the user. + Name string `json:"name"` + + // Role Role of the user. + Role string `json:"role"` +} + +// CallbackOid4vciCredentialIssuanceParams defines parameters for CallbackOid4vciCredentialIssuance. +type CallbackOid4vciCredentialIssuanceParams struct { + // Code The oauth2 code response. + Code string `form:"code" json:"code"` + + // State The oauth2 state, required as the authorize request sends it. + State string `form:"state" json:"state"` + + // Error The error code. + Error *string `form:"error,omitempty" json:"error,omitempty"` + + // ErrorDescription The error description. + ErrorDescription *string `form:"error_description,omitempty" json:"error_description,omitempty"` +} + +// RequestOid4vciCredentialIssuanceJSONBody defines parameters for RequestOid4vciCredentialIssuance. +type RequestOid4vciCredentialIssuanceJSONBody struct { + AuthorizationDetails []struct { + CredentialDefinition *map[string]interface{} `json:"credential_definition,omitempty"` + Format *string `json:"format,omitempty"` + Type *string `json:"type,omitempty"` + } `json:"authorization_details"` + Issuer string `json:"issuer"` + + // RedirectUri The URL to which the user-agent will be redirected after the authorization request. + RedirectUri string `json:"redirect_uri"` +} + +// RequestServiceAccessTokenJSONBody defines parameters for RequestServiceAccessToken. +type RequestServiceAccessTokenJSONBody struct { + // Scope The scope that will be the service for which this access token can be used. + Scope string `json:"scope"` + Verifier string `json:"verifier"` +} + +// RequestUserAccessTokenJSONBody defines parameters for RequestUserAccessToken. +type RequestUserAccessTokenJSONBody struct { + // PreauthorizedUser Claims about the authorized user. + PreauthorizedUser *UserDetails `json:"preauthorized_user,omitempty"` + + // RedirectUri The URL to which the user-agent will be redirected after the authorization request. + // This is the URL of the calling application. + // The OAuth2 flow will finish at the /callback URL of the node and the node will redirect the user to this redirect_uri. + RedirectUri string `json:"redirect_uri"` + + // Scope The scope that will be the service for which this access token can be used. + Scope string `json:"scope"` + + // Verifier The DID of the verifier, the relying party for which this access token is requested. + Verifier string `json:"verifier"` +} + +// HandleAuthorizeRequestParams defines parameters for HandleAuthorizeRequest. +type HandleAuthorizeRequestParams struct { + Params *map[string]string `form:"params,omitempty" json:"params,omitempty"` +} + +// CallbackParams defines parameters for Callback. +type CallbackParams struct { + // Code The authorization code received from the authorization server. + Code *string `form:"code,omitempty" json:"code,omitempty"` + + // State The client state. + State *string `form:"state,omitempty" json:"state,omitempty"` + + // Error The error code. + Error *string `form:"error,omitempty" json:"error,omitempty"` + + // ErrorDescription The error description. + ErrorDescription *string `form:"error_description,omitempty" json:"error_description,omitempty"` +} + +// PresentationDefinitionParams defines parameters for PresentationDefinition. +type PresentationDefinitionParams struct { + Scope string `form:"scope" json:"scope"` + WalletOwnerType *WalletOwnerType `form:"wallet_owner_type,omitempty" json:"wallet_owner_type,omitempty"` +} + +// HandleAuthorizeResponseFormdataBody defines parameters for HandleAuthorizeResponse. +type HandleAuthorizeResponseFormdataBody struct { + // Error error code as defined by the OAuth2 specification + Error *string `form:"error,omitempty" json:"error,omitempty"` + + // ErrorDescription error description as defined by the OAuth2 specification + ErrorDescription *string `form:"error_description,omitempty" json:"error_description,omitempty"` + PresentationSubmission *string `form:"presentation_submission,omitempty" json:"presentation_submission,omitempty"` + + // State the client state for the verifier + State *string `form:"state,omitempty" json:"state,omitempty"` + + // VpToken A Verifiable Presentation in either JSON-LD or JWT format. + VpToken *string `form:"vp_token,omitempty" json:"vp_token,omitempty"` +} + +// HandleTokenRequestFormdataBody defines parameters for HandleTokenRequest. +type HandleTokenRequestFormdataBody struct { + Assertion *string `form:"assertion,omitempty" json:"assertion,omitempty"` + ClientId *string `form:"client_id,omitempty" json:"client_id,omitempty"` + Code *string `form:"code,omitempty" json:"code,omitempty"` + CodeVerifier *string `form:"code_verifier,omitempty" json:"code_verifier,omitempty"` + GrantType string `form:"grant_type" json:"grant_type"` + PresentationSubmission *string `form:"presentation_submission,omitempty" json:"presentation_submission,omitempty"` + Scope *string `form:"scope,omitempty" json:"scope,omitempty"` +} + +// IntrospectAccessTokenFormdataRequestBody defines body for IntrospectAccessToken for application/x-www-form-urlencoded ContentType. +type IntrospectAccessTokenFormdataRequestBody = TokenIntrospectionRequest + +// RequestOid4vciCredentialIssuanceJSONRequestBody defines body for RequestOid4vciCredentialIssuance for application/json ContentType. +type RequestOid4vciCredentialIssuanceJSONRequestBody RequestOid4vciCredentialIssuanceJSONBody + +// RequestServiceAccessTokenJSONRequestBody defines body for RequestServiceAccessToken for application/json ContentType. +type RequestServiceAccessTokenJSONRequestBody RequestServiceAccessTokenJSONBody + +// RequestUserAccessTokenJSONRequestBody defines body for RequestUserAccessToken for application/json ContentType. +type RequestUserAccessTokenJSONRequestBody RequestUserAccessTokenJSONBody + +// HandleAuthorizeResponseFormdataRequestBody defines body for HandleAuthorizeResponse for application/x-www-form-urlencoded ContentType. +type HandleAuthorizeResponseFormdataRequestBody HandleAuthorizeResponseFormdataBody + +// HandleTokenRequestFormdataRequestBody defines body for HandleTokenRequest for application/x-www-form-urlencoded ContentType. +type HandleTokenRequestFormdataRequestBody HandleTokenRequestFormdataBody + +// Getter for additional properties for TokenIntrospectionResponse. Returns the specified +// element and whether it was found +func (a TokenIntrospectionResponse) Get(fieldName string) (value interface{}, found bool) { + if a.AdditionalProperties != nil { + value, found = a.AdditionalProperties[fieldName] + } + return +} + +// Setter for additional properties for TokenIntrospectionResponse +func (a *TokenIntrospectionResponse) Set(fieldName string, value interface{}) { + if a.AdditionalProperties == nil { + a.AdditionalProperties = make(map[string]interface{}) + } + a.AdditionalProperties[fieldName] = value +} + +// Override default JSON handling for TokenIntrospectionResponse to handle AdditionalProperties +func (a *TokenIntrospectionResponse) UnmarshalJSON(b []byte) error { + object := make(map[string]json.RawMessage) + err := json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["active"]; found { + err = json.Unmarshal(raw, &a.Active) + if err != nil { + return fmt.Errorf("error reading 'active': %w", err) + } + delete(object, "active") + } + + if raw, found := object["aud"]; found { + err = json.Unmarshal(raw, &a.Aud) + if err != nil { + return fmt.Errorf("error reading 'aud': %w", err) + } + delete(object, "aud") + } + + if raw, found := object["client_id"]; found { + err = json.Unmarshal(raw, &a.ClientId) + if err != nil { + return fmt.Errorf("error reading 'client_id': %w", err) + } + delete(object, "client_id") + } + + if raw, found := object["exp"]; found { + err = json.Unmarshal(raw, &a.Exp) + if err != nil { + return fmt.Errorf("error reading 'exp': %w", err) + } + delete(object, "exp") + } + + if raw, found := object["iat"]; found { + err = json.Unmarshal(raw, &a.Iat) + if err != nil { + return fmt.Errorf("error reading 'iat': %w", err) + } + delete(object, "iat") + } + + if raw, found := object["iss"]; found { + err = json.Unmarshal(raw, &a.Iss) + if err != nil { + return fmt.Errorf("error reading 'iss': %w", err) + } + delete(object, "iss") + } + + if raw, found := object["presentation_definitions"]; found { + err = json.Unmarshal(raw, &a.PresentationDefinitions) + if err != nil { + return fmt.Errorf("error reading 'presentation_definitions': %w", err) + } + delete(object, "presentation_definitions") + } + + if raw, found := object["presentation_submissions"]; found { + err = json.Unmarshal(raw, &a.PresentationSubmissions) + if err != nil { + return fmt.Errorf("error reading 'presentation_submissions': %w", err) + } + delete(object, "presentation_submissions") + } + + if raw, found := object["scope"]; found { + err = json.Unmarshal(raw, &a.Scope) + if err != nil { + return fmt.Errorf("error reading 'scope': %w", err) + } + delete(object, "scope") + } + + if raw, found := object["sub"]; found { + err = json.Unmarshal(raw, &a.Sub) + if err != nil { + return fmt.Errorf("error reading 'sub': %w", err) + } + delete(object, "sub") + } + + if raw, found := object["vps"]; found { + err = json.Unmarshal(raw, &a.Vps) + if err != nil { + return fmt.Errorf("error reading 'vps': %w", err) + } + delete(object, "vps") + } + + if len(object) != 0 { + a.AdditionalProperties = make(map[string]interface{}) + for fieldName, fieldBuf := range object { + var fieldVal interface{} + err := json.Unmarshal(fieldBuf, &fieldVal) + if err != nil { + return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err) + } + a.AdditionalProperties[fieldName] = fieldVal + } + } + return nil +} + +// Override default JSON handling for TokenIntrospectionResponse to handle AdditionalProperties +func (a TokenIntrospectionResponse) MarshalJSON() ([]byte, error) { + var err error + object := make(map[string]json.RawMessage) + + object["active"], err = json.Marshal(a.Active) + if err != nil { + return nil, fmt.Errorf("error marshaling 'active': %w", err) + } + + if a.Aud != nil { + object["aud"], err = json.Marshal(a.Aud) + if err != nil { + return nil, fmt.Errorf("error marshaling 'aud': %w", err) + } + } + + if a.ClientId != nil { + object["client_id"], err = json.Marshal(a.ClientId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'client_id': %w", err) + } + } + + if a.Exp != nil { + object["exp"], err = json.Marshal(a.Exp) + if err != nil { + return nil, fmt.Errorf("error marshaling 'exp': %w", err) + } + } + + if a.Iat != nil { + object["iat"], err = json.Marshal(a.Iat) + if err != nil { + return nil, fmt.Errorf("error marshaling 'iat': %w", err) + } + } + + if a.Iss != nil { + object["iss"], err = json.Marshal(a.Iss) + if err != nil { + return nil, fmt.Errorf("error marshaling 'iss': %w", err) + } + } + + if a.PresentationDefinitions != nil { + object["presentation_definitions"], err = json.Marshal(a.PresentationDefinitions) + if err != nil { + return nil, fmt.Errorf("error marshaling 'presentation_definitions': %w", err) + } + } + + if a.PresentationSubmissions != nil { + object["presentation_submissions"], err = json.Marshal(a.PresentationSubmissions) + if err != nil { + return nil, fmt.Errorf("error marshaling 'presentation_submissions': %w", err) + } + } + + if a.Scope != nil { + object["scope"], err = json.Marshal(a.Scope) + if err != nil { + return nil, fmt.Errorf("error marshaling 'scope': %w", err) + } + } + + if a.Sub != nil { + object["sub"], err = json.Marshal(a.Sub) + if err != nil { + return nil, fmt.Errorf("error marshaling 'sub': %w", err) + } + } + + if a.Vps != nil { + object["vps"], err = json.Marshal(a.Vps) + if err != nil { + return nil, fmt.Errorf("error marshaling 'vps': %w", err) + } + } + + for fieldName, field := range a.AdditionalProperties { + object[fieldName], err = json.Marshal(field) + if err != nil { + return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err) + } + } + return json.Marshal(object) +} + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GetRootWebDID request + GetRootWebDID(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RootOAuthAuthorizationServerMetadata request + RootOAuthAuthorizationServerMetadata(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // OAuthAuthorizationServerMetadata request + OAuthAuthorizationServerMetadata(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CallbackOid4vciCredentialIssuance request + CallbackOid4vciCredentialIssuance(ctx context.Context, params *CallbackOid4vciCredentialIssuanceParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTenantWebDID request + GetTenantWebDID(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // IntrospectAccessTokenWithBody request with any body + IntrospectAccessTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + IntrospectAccessTokenWithFormdataBody(ctx context.Context, body IntrospectAccessTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RetrieveAccessToken request + RetrieveAccessToken(ctx context.Context, sessionID string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RequestOid4vciCredentialIssuanceWithBody request with any body + RequestOid4vciCredentialIssuanceWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + RequestOid4vciCredentialIssuance(ctx context.Context, did string, body RequestOid4vciCredentialIssuanceJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RequestServiceAccessTokenWithBody request with any body + RequestServiceAccessTokenWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + RequestServiceAccessToken(ctx context.Context, did string, body RequestServiceAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // RequestUserAccessTokenWithBody request with any body + RequestUserAccessTokenWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + RequestUserAccessToken(ctx context.Context, did string, body RequestUserAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // HandleAuthorizeRequest request + HandleAuthorizeRequest(ctx context.Context, did string, params *HandleAuthorizeRequestParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // Callback request + Callback(ctx context.Context, did string, params *CallbackParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // OAuthClientMetadata request + OAuthClientMetadata(ctx context.Context, did string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PresentationDefinition request + PresentationDefinition(ctx context.Context, did string, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // HandleAuthorizeResponseWithBody request with any body + HandleAuthorizeResponseWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + HandleAuthorizeResponseWithFormdataBody(ctx context.Context, did string, body HandleAuthorizeResponseFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // HandleTokenRequestWithBody request with any body + HandleTokenRequestWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + HandleTokenRequestWithFormdataBody(ctx context.Context, did string, body HandleTokenRequestFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // StatusList request + StatusList(ctx context.Context, did string, page int, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) GetRootWebDID(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetRootWebDIDRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RootOAuthAuthorizationServerMetadata(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRootOAuthAuthorizationServerMetadataRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) OAuthAuthorizationServerMetadata(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOAuthAuthorizationServerMetadataRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CallbackOid4vciCredentialIssuance(ctx context.Context, params *CallbackOid4vciCredentialIssuanceParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCallbackOid4vciCredentialIssuanceRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetTenantWebDID(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTenantWebDIDRequest(c.Server, id) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) IntrospectAccessTokenWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewIntrospectAccessTokenRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) IntrospectAccessTokenWithFormdataBody(ctx context.Context, body IntrospectAccessTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewIntrospectAccessTokenRequestWithFormdataBody(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RetrieveAccessToken(ctx context.Context, sessionID string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRetrieveAccessTokenRequest(c.Server, sessionID) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RequestOid4vciCredentialIssuanceWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRequestOid4vciCredentialIssuanceRequestWithBody(c.Server, did, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RequestOid4vciCredentialIssuance(ctx context.Context, did string, body RequestOid4vciCredentialIssuanceJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRequestOid4vciCredentialIssuanceRequest(c.Server, did, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RequestServiceAccessTokenWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRequestServiceAccessTokenRequestWithBody(c.Server, did, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RequestServiceAccessToken(ctx context.Context, did string, body RequestServiceAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRequestServiceAccessTokenRequest(c.Server, did, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RequestUserAccessTokenWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRequestUserAccessTokenRequestWithBody(c.Server, did, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) RequestUserAccessToken(ctx context.Context, did string, body RequestUserAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewRequestUserAccessTokenRequest(c.Server, did, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) HandleAuthorizeRequest(ctx context.Context, did string, params *HandleAuthorizeRequestParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHandleAuthorizeRequestRequest(c.Server, did, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) Callback(ctx context.Context, did string, params *CallbackParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCallbackRequest(c.Server, did, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) OAuthClientMetadata(ctx context.Context, did string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewOAuthClientMetadataRequest(c.Server, did) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PresentationDefinition(ctx context.Context, did string, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPresentationDefinitionRequest(c.Server, did, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) HandleAuthorizeResponseWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHandleAuthorizeResponseRequestWithBody(c.Server, did, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) HandleAuthorizeResponseWithFormdataBody(ctx context.Context, did string, body HandleAuthorizeResponseFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHandleAuthorizeResponseRequestWithFormdataBody(c.Server, did, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) HandleTokenRequestWithBody(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHandleTokenRequestRequestWithBody(c.Server, did, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) HandleTokenRequestWithFormdataBody(ctx context.Context, did string, body HandleTokenRequestFormdataRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewHandleTokenRequestRequestWithFormdataBody(c.Server, did, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) StatusList(ctx context.Context, did string, page int, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStatusListRequest(c.Server, did, page) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewGetRootWebDIDRequest generates requests for GetRootWebDID +func NewGetRootWebDIDRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/.well-known/did.json") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewRootOAuthAuthorizationServerMetadataRequest generates requests for RootOAuthAuthorizationServerMetadata +func NewRootOAuthAuthorizationServerMetadataRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/.well-known/oauth-authorization-server") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewOAuthAuthorizationServerMetadataRequest generates requests for OAuthAuthorizationServerMetadata +func NewOAuthAuthorizationServerMetadataRequest(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/.well-known/oauth-authorization-server/iam/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCallbackOid4vciCredentialIssuanceRequest generates requests for CallbackOid4vciCredentialIssuance +func NewCallbackOid4vciCredentialIssuanceRequest(server string, params *CallbackOid4vciCredentialIssuanceParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/iam/oid4vci/callback") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "code", runtime.ParamLocationQuery, params.Code); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "state", runtime.ParamLocationQuery, params.State); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + if params.Error != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "error", runtime.ParamLocationQuery, *params.Error); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.ErrorDescription != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "error_description", runtime.ParamLocationQuery, *params.ErrorDescription); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTenantWebDIDRequest generates requests for GetTenantWebDID +func NewGetTenantWebDIDRequest(server string, id string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "id", runtime.ParamLocationPath, id) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/iam/%s/did.json", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewIntrospectAccessTokenRequestWithFormdataBody calls the generic IntrospectAccessToken builder with application/x-www-form-urlencoded body +func NewIntrospectAccessTokenRequestWithFormdataBody(server string, body IntrospectAccessTokenFormdataRequestBody) (*http.Request, error) { + var bodyReader io.Reader + bodyStr, err := runtime.MarshalForm(body, nil) + if err != nil { + return nil, err + } + bodyReader = strings.NewReader(bodyStr.Encode()) + return NewIntrospectAccessTokenRequestWithBody(server, "application/x-www-form-urlencoded", bodyReader) +} + +// NewIntrospectAccessTokenRequestWithBody generates requests for IntrospectAccessToken with any type of body +func NewIntrospectAccessTokenRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/internal/auth/v2/accesstoken/introspect") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewRetrieveAccessTokenRequest generates requests for RetrieveAccessToken +func NewRetrieveAccessTokenRequest(server string, sessionID string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "sessionID", runtime.ParamLocationPath, sessionID) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/internal/auth/v2/accesstoken/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewRequestOid4vciCredentialIssuanceRequest calls the generic RequestOid4vciCredentialIssuance builder with application/json body +func NewRequestOid4vciCredentialIssuanceRequest(server string, did string, body RequestOid4vciCredentialIssuanceJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRequestOid4vciCredentialIssuanceRequestWithBody(server, did, "application/json", bodyReader) +} + +// NewRequestOid4vciCredentialIssuanceRequestWithBody generates requests for RequestOid4vciCredentialIssuance with any type of body +func NewRequestOid4vciCredentialIssuanceRequestWithBody(server string, did string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/internal/auth/v2/%s/request-credential", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewRequestServiceAccessTokenRequest calls the generic RequestServiceAccessToken builder with application/json body +func NewRequestServiceAccessTokenRequest(server string, did string, body RequestServiceAccessTokenJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRequestServiceAccessTokenRequestWithBody(server, did, "application/json", bodyReader) +} + +// NewRequestServiceAccessTokenRequestWithBody generates requests for RequestServiceAccessToken with any type of body +func NewRequestServiceAccessTokenRequestWithBody(server string, did string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/internal/auth/v2/%s/request-service-access-token", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewRequestUserAccessTokenRequest calls the generic RequestUserAccessToken builder with application/json body +func NewRequestUserAccessTokenRequest(server string, did string, body RequestUserAccessTokenJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewRequestUserAccessTokenRequestWithBody(server, did, "application/json", bodyReader) +} + +// NewRequestUserAccessTokenRequestWithBody generates requests for RequestUserAccessToken with any type of body +func NewRequestUserAccessTokenRequestWithBody(server string, did string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/internal/auth/v2/%s/request-user-access-token", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewHandleAuthorizeRequestRequest generates requests for HandleAuthorizeRequest +func NewHandleAuthorizeRequestRequest(server string, did string, params *HandleAuthorizeRequestParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/oauth2/%s/authorize", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Params != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "params", runtime.ParamLocationQuery, *params.Params); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCallbackRequest generates requests for Callback +func NewCallbackRequest(server string, did string, params *CallbackParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/oauth2/%s/callback", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Code != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "code", runtime.ParamLocationQuery, *params.Code); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.State != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "state", runtime.ParamLocationQuery, *params.State); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Error != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "error", runtime.ParamLocationQuery, *params.Error); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.ErrorDescription != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "error_description", runtime.ParamLocationQuery, *params.ErrorDescription); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewOAuthClientMetadataRequest generates requests for OAuthClientMetadata +func NewOAuthClientMetadataRequest(server string, did string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/oauth2/%s/oauth-client", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPresentationDefinitionRequest generates requests for PresentationDefinition +func NewPresentationDefinitionRequest(server string, did string, params *PresentationDefinitionParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/oauth2/%s/presentation_definition", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "scope", runtime.ParamLocationQuery, params.Scope); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + if params.WalletOwnerType != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "wallet_owner_type", runtime.ParamLocationQuery, *params.WalletOwnerType); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewHandleAuthorizeResponseRequestWithFormdataBody calls the generic HandleAuthorizeResponse builder with application/x-www-form-urlencoded body +func NewHandleAuthorizeResponseRequestWithFormdataBody(server string, did string, body HandleAuthorizeResponseFormdataRequestBody) (*http.Request, error) { + var bodyReader io.Reader + bodyStr, err := runtime.MarshalForm(body, nil) + if err != nil { + return nil, err + } + bodyReader = strings.NewReader(bodyStr.Encode()) + return NewHandleAuthorizeResponseRequestWithBody(server, did, "application/x-www-form-urlencoded", bodyReader) +} + +// NewHandleAuthorizeResponseRequestWithBody generates requests for HandleAuthorizeResponse with any type of body +func NewHandleAuthorizeResponseRequestWithBody(server string, did string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/oauth2/%s/response", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewHandleTokenRequestRequestWithFormdataBody calls the generic HandleTokenRequest builder with application/x-www-form-urlencoded body +func NewHandleTokenRequestRequestWithFormdataBody(server string, did string, body HandleTokenRequestFormdataRequestBody) (*http.Request, error) { + var bodyReader io.Reader + bodyStr, err := runtime.MarshalForm(body, nil) + if err != nil { + return nil, err + } + bodyReader = strings.NewReader(bodyStr.Encode()) + return NewHandleTokenRequestRequestWithBody(server, did, "application/x-www-form-urlencoded", bodyReader) +} + +// NewHandleTokenRequestRequestWithBody generates requests for HandleTokenRequest with any type of body +func NewHandleTokenRequestRequestWithBody(server string, did string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/oauth2/%s/token", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewStatusListRequest generates requests for StatusList +func NewStatusListRequest(server string, did string, page int) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0 = did + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithLocation("simple", false, "page", runtime.ParamLocationPath, page) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/statuslist/%s/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GetRootWebDIDWithResponse request + GetRootWebDIDWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetRootWebDIDResponse, error) + + // RootOAuthAuthorizationServerMetadataWithResponse request + RootOAuthAuthorizationServerMetadataWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*RootOAuthAuthorizationServerMetadataResponse, error) + + // OAuthAuthorizationServerMetadataWithResponse request + OAuthAuthorizationServerMetadataWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*OAuthAuthorizationServerMetadataResponse, error) + + // CallbackOid4vciCredentialIssuanceWithResponse request + CallbackOid4vciCredentialIssuanceWithResponse(ctx context.Context, params *CallbackOid4vciCredentialIssuanceParams, reqEditors ...RequestEditorFn) (*CallbackOid4vciCredentialIssuanceResponse, error) + + // GetTenantWebDIDWithResponse request + GetTenantWebDIDWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetTenantWebDIDResponse, error) + + // IntrospectAccessTokenWithBodyWithResponse request with any body + IntrospectAccessTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*IntrospectAccessTokenResponse, error) + + IntrospectAccessTokenWithFormdataBodyWithResponse(ctx context.Context, body IntrospectAccessTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*IntrospectAccessTokenResponse, error) + + // RetrieveAccessTokenWithResponse request + RetrieveAccessTokenWithResponse(ctx context.Context, sessionID string, reqEditors ...RequestEditorFn) (*RetrieveAccessTokenResponse, error) + + // RequestOid4vciCredentialIssuanceWithBodyWithResponse request with any body + RequestOid4vciCredentialIssuanceWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RequestOid4vciCredentialIssuanceResponse, error) + + RequestOid4vciCredentialIssuanceWithResponse(ctx context.Context, did string, body RequestOid4vciCredentialIssuanceJSONRequestBody, reqEditors ...RequestEditorFn) (*RequestOid4vciCredentialIssuanceResponse, error) + + // RequestServiceAccessTokenWithBodyWithResponse request with any body + RequestServiceAccessTokenWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RequestServiceAccessTokenResponse, error) + + RequestServiceAccessTokenWithResponse(ctx context.Context, did string, body RequestServiceAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RequestServiceAccessTokenResponse, error) + + // RequestUserAccessTokenWithBodyWithResponse request with any body + RequestUserAccessTokenWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RequestUserAccessTokenResponse, error) + + RequestUserAccessTokenWithResponse(ctx context.Context, did string, body RequestUserAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RequestUserAccessTokenResponse, error) + + // HandleAuthorizeRequestWithResponse request + HandleAuthorizeRequestWithResponse(ctx context.Context, did string, params *HandleAuthorizeRequestParams, reqEditors ...RequestEditorFn) (*HandleAuthorizeRequestResponse, error) + + // CallbackWithResponse request + CallbackWithResponse(ctx context.Context, did string, params *CallbackParams, reqEditors ...RequestEditorFn) (*CallbackResponse, error) + + // OAuthClientMetadataWithResponse request + OAuthClientMetadataWithResponse(ctx context.Context, did string, reqEditors ...RequestEditorFn) (*OAuthClientMetadataResponse, error) + + // PresentationDefinitionWithResponse request + PresentationDefinitionWithResponse(ctx context.Context, did string, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*PresentationDefinitionResponse, error) + + // HandleAuthorizeResponseWithBodyWithResponse request with any body + HandleAuthorizeResponseWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*HandleAuthorizeResponseResponse, error) + + HandleAuthorizeResponseWithFormdataBodyWithResponse(ctx context.Context, did string, body HandleAuthorizeResponseFormdataRequestBody, reqEditors ...RequestEditorFn) (*HandleAuthorizeResponseResponse, error) + + // HandleTokenRequestWithBodyWithResponse request with any body + HandleTokenRequestWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*HandleTokenRequestResponse, error) + + HandleTokenRequestWithFormdataBodyWithResponse(ctx context.Context, did string, body HandleTokenRequestFormdataRequestBody, reqEditors ...RequestEditorFn) (*HandleTokenRequestResponse, error) + + // StatusListWithResponse request + StatusListWithResponse(ctx context.Context, did string, page int, reqEditors ...RequestEditorFn) (*StatusListResponse, error) +} + +type GetRootWebDIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *DIDDocument +} + +// Status returns HTTPResponse.Status +func (r GetRootWebDIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetRootWebDIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RootOAuthAuthorizationServerMetadataResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *OAuthAuthorizationServerMetadata + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r RootOAuthAuthorizationServerMetadataResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RootOAuthAuthorizationServerMetadataResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type OAuthAuthorizationServerMetadataResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *OAuthAuthorizationServerMetadata + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r OAuthAuthorizationServerMetadataResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r OAuthAuthorizationServerMetadataResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CallbackOid4vciCredentialIssuanceResponse struct { + Body []byte + HTTPResponse *http.Response + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r CallbackOid4vciCredentialIssuanceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CallbackOid4vciCredentialIssuanceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetTenantWebDIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *DIDDocument +} + +// Status returns HTTPResponse.Status +func (r GetTenantWebDIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTenantWebDIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type IntrospectAccessTokenResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TokenIntrospectionResponse +} + +// Status returns HTTPResponse.Status +func (r IntrospectAccessTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r IntrospectAccessTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RetrieveAccessTokenResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TokenResponse + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r RetrieveAccessTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RetrieveAccessTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RequestOid4vciCredentialIssuanceResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *RedirectResponse + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r RequestOid4vciCredentialIssuanceResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RequestOid4vciCredentialIssuanceResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RequestServiceAccessTokenResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TokenResponse + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r RequestServiceAccessTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RequestServiceAccessTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type RequestUserAccessTokenResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *RedirectResponseWithID + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r RequestUserAccessTokenResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r RequestUserAccessTokenResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type HandleAuthorizeRequestResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r HandleAuthorizeRequestResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r HandleAuthorizeRequestResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CallbackResponse struct { + Body []byte + HTTPResponse *http.Response + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r CallbackResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CallbackResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type OAuthClientMetadataResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *OAuthClientMetadata + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r OAuthClientMetadataResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r OAuthClientMetadataResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PresentationDefinitionResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *PresentationDefinition + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r PresentationDefinitionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PresentationDefinitionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type HandleAuthorizeResponseResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *RedirectResponse +} + +// Status returns HTTPResponse.Status +func (r HandleAuthorizeResponseResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r HandleAuthorizeResponseResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type HandleTokenRequestResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TokenResponse + JSONDefault *ErrorResponse +} + +// Status returns HTTPResponse.Status +func (r HandleTokenRequestResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r HandleTokenRequestResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type StatusListResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *VerifiableCredential + ApplicationproblemJSONDefault *struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } +} + +// Status returns HTTPResponse.Status +func (r StatusListResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r StatusListResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// GetRootWebDIDWithResponse request returning *GetRootWebDIDResponse +func (c *ClientWithResponses) GetRootWebDIDWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetRootWebDIDResponse, error) { + rsp, err := c.GetRootWebDID(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetRootWebDIDResponse(rsp) +} + +// RootOAuthAuthorizationServerMetadataWithResponse request returning *RootOAuthAuthorizationServerMetadataResponse +func (c *ClientWithResponses) RootOAuthAuthorizationServerMetadataWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*RootOAuthAuthorizationServerMetadataResponse, error) { + rsp, err := c.RootOAuthAuthorizationServerMetadata(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseRootOAuthAuthorizationServerMetadataResponse(rsp) +} + +// OAuthAuthorizationServerMetadataWithResponse request returning *OAuthAuthorizationServerMetadataResponse +func (c *ClientWithResponses) OAuthAuthorizationServerMetadataWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*OAuthAuthorizationServerMetadataResponse, error) { + rsp, err := c.OAuthAuthorizationServerMetadata(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseOAuthAuthorizationServerMetadataResponse(rsp) +} + +// CallbackOid4vciCredentialIssuanceWithResponse request returning *CallbackOid4vciCredentialIssuanceResponse +func (c *ClientWithResponses) CallbackOid4vciCredentialIssuanceWithResponse(ctx context.Context, params *CallbackOid4vciCredentialIssuanceParams, reqEditors ...RequestEditorFn) (*CallbackOid4vciCredentialIssuanceResponse, error) { + rsp, err := c.CallbackOid4vciCredentialIssuance(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseCallbackOid4vciCredentialIssuanceResponse(rsp) +} + +// GetTenantWebDIDWithResponse request returning *GetTenantWebDIDResponse +func (c *ClientWithResponses) GetTenantWebDIDWithResponse(ctx context.Context, id string, reqEditors ...RequestEditorFn) (*GetTenantWebDIDResponse, error) { + rsp, err := c.GetTenantWebDID(ctx, id, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTenantWebDIDResponse(rsp) +} + +// IntrospectAccessTokenWithBodyWithResponse request with arbitrary body returning *IntrospectAccessTokenResponse +func (c *ClientWithResponses) IntrospectAccessTokenWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*IntrospectAccessTokenResponse, error) { + rsp, err := c.IntrospectAccessTokenWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseIntrospectAccessTokenResponse(rsp) +} + +func (c *ClientWithResponses) IntrospectAccessTokenWithFormdataBodyWithResponse(ctx context.Context, body IntrospectAccessTokenFormdataRequestBody, reqEditors ...RequestEditorFn) (*IntrospectAccessTokenResponse, error) { + rsp, err := c.IntrospectAccessTokenWithFormdataBody(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseIntrospectAccessTokenResponse(rsp) +} + +// RetrieveAccessTokenWithResponse request returning *RetrieveAccessTokenResponse +func (c *ClientWithResponses) RetrieveAccessTokenWithResponse(ctx context.Context, sessionID string, reqEditors ...RequestEditorFn) (*RetrieveAccessTokenResponse, error) { + rsp, err := c.RetrieveAccessToken(ctx, sessionID, reqEditors...) + if err != nil { + return nil, err + } + return ParseRetrieveAccessTokenResponse(rsp) +} + +// RequestOid4vciCredentialIssuanceWithBodyWithResponse request with arbitrary body returning *RequestOid4vciCredentialIssuanceResponse +func (c *ClientWithResponses) RequestOid4vciCredentialIssuanceWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RequestOid4vciCredentialIssuanceResponse, error) { + rsp, err := c.RequestOid4vciCredentialIssuanceWithBody(ctx, did, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRequestOid4vciCredentialIssuanceResponse(rsp) +} + +func (c *ClientWithResponses) RequestOid4vciCredentialIssuanceWithResponse(ctx context.Context, did string, body RequestOid4vciCredentialIssuanceJSONRequestBody, reqEditors ...RequestEditorFn) (*RequestOid4vciCredentialIssuanceResponse, error) { + rsp, err := c.RequestOid4vciCredentialIssuance(ctx, did, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRequestOid4vciCredentialIssuanceResponse(rsp) +} + +// RequestServiceAccessTokenWithBodyWithResponse request with arbitrary body returning *RequestServiceAccessTokenResponse +func (c *ClientWithResponses) RequestServiceAccessTokenWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RequestServiceAccessTokenResponse, error) { + rsp, err := c.RequestServiceAccessTokenWithBody(ctx, did, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRequestServiceAccessTokenResponse(rsp) +} + +func (c *ClientWithResponses) RequestServiceAccessTokenWithResponse(ctx context.Context, did string, body RequestServiceAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RequestServiceAccessTokenResponse, error) { + rsp, err := c.RequestServiceAccessToken(ctx, did, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRequestServiceAccessTokenResponse(rsp) +} + +// RequestUserAccessTokenWithBodyWithResponse request with arbitrary body returning *RequestUserAccessTokenResponse +func (c *ClientWithResponses) RequestUserAccessTokenWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RequestUserAccessTokenResponse, error) { + rsp, err := c.RequestUserAccessTokenWithBody(ctx, did, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRequestUserAccessTokenResponse(rsp) +} + +func (c *ClientWithResponses) RequestUserAccessTokenWithResponse(ctx context.Context, did string, body RequestUserAccessTokenJSONRequestBody, reqEditors ...RequestEditorFn) (*RequestUserAccessTokenResponse, error) { + rsp, err := c.RequestUserAccessToken(ctx, did, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseRequestUserAccessTokenResponse(rsp) +} + +// HandleAuthorizeRequestWithResponse request returning *HandleAuthorizeRequestResponse +func (c *ClientWithResponses) HandleAuthorizeRequestWithResponse(ctx context.Context, did string, params *HandleAuthorizeRequestParams, reqEditors ...RequestEditorFn) (*HandleAuthorizeRequestResponse, error) { + rsp, err := c.HandleAuthorizeRequest(ctx, did, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseHandleAuthorizeRequestResponse(rsp) +} + +// CallbackWithResponse request returning *CallbackResponse +func (c *ClientWithResponses) CallbackWithResponse(ctx context.Context, did string, params *CallbackParams, reqEditors ...RequestEditorFn) (*CallbackResponse, error) { + rsp, err := c.Callback(ctx, did, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseCallbackResponse(rsp) +} + +// OAuthClientMetadataWithResponse request returning *OAuthClientMetadataResponse +func (c *ClientWithResponses) OAuthClientMetadataWithResponse(ctx context.Context, did string, reqEditors ...RequestEditorFn) (*OAuthClientMetadataResponse, error) { + rsp, err := c.OAuthClientMetadata(ctx, did, reqEditors...) + if err != nil { + return nil, err + } + return ParseOAuthClientMetadataResponse(rsp) +} + +// PresentationDefinitionWithResponse request returning *PresentationDefinitionResponse +func (c *ClientWithResponses) PresentationDefinitionWithResponse(ctx context.Context, did string, params *PresentationDefinitionParams, reqEditors ...RequestEditorFn) (*PresentationDefinitionResponse, error) { + rsp, err := c.PresentationDefinition(ctx, did, params, reqEditors...) + if err != nil { + return nil, err + } + return ParsePresentationDefinitionResponse(rsp) +} + +// HandleAuthorizeResponseWithBodyWithResponse request with arbitrary body returning *HandleAuthorizeResponseResponse +func (c *ClientWithResponses) HandleAuthorizeResponseWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*HandleAuthorizeResponseResponse, error) { + rsp, err := c.HandleAuthorizeResponseWithBody(ctx, did, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseHandleAuthorizeResponseResponse(rsp) +} + +func (c *ClientWithResponses) HandleAuthorizeResponseWithFormdataBodyWithResponse(ctx context.Context, did string, body HandleAuthorizeResponseFormdataRequestBody, reqEditors ...RequestEditorFn) (*HandleAuthorizeResponseResponse, error) { + rsp, err := c.HandleAuthorizeResponseWithFormdataBody(ctx, did, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseHandleAuthorizeResponseResponse(rsp) +} + +// HandleTokenRequestWithBodyWithResponse request with arbitrary body returning *HandleTokenRequestResponse +func (c *ClientWithResponses) HandleTokenRequestWithBodyWithResponse(ctx context.Context, did string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*HandleTokenRequestResponse, error) { + rsp, err := c.HandleTokenRequestWithBody(ctx, did, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseHandleTokenRequestResponse(rsp) +} + +func (c *ClientWithResponses) HandleTokenRequestWithFormdataBodyWithResponse(ctx context.Context, did string, body HandleTokenRequestFormdataRequestBody, reqEditors ...RequestEditorFn) (*HandleTokenRequestResponse, error) { + rsp, err := c.HandleTokenRequestWithFormdataBody(ctx, did, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseHandleTokenRequestResponse(rsp) +} + +// StatusListWithResponse request returning *StatusListResponse +func (c *ClientWithResponses) StatusListWithResponse(ctx context.Context, did string, page int, reqEditors ...RequestEditorFn) (*StatusListResponse, error) { + rsp, err := c.StatusList(ctx, did, page, reqEditors...) + if err != nil { + return nil, err + } + return ParseStatusListResponse(rsp) +} + +// ParseGetRootWebDIDResponse parses an HTTP response from a GetRootWebDIDWithResponse call +func ParseGetRootWebDIDResponse(rsp *http.Response) (*GetRootWebDIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetRootWebDIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest DIDDocument + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseRootOAuthAuthorizationServerMetadataResponse parses an HTTP response from a RootOAuthAuthorizationServerMetadataWithResponse call +func ParseRootOAuthAuthorizationServerMetadataResponse(rsp *http.Response) (*RootOAuthAuthorizationServerMetadataResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RootOAuthAuthorizationServerMetadataResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest OAuthAuthorizationServerMetadata + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseOAuthAuthorizationServerMetadataResponse parses an HTTP response from a OAuthAuthorizationServerMetadataWithResponse call +func ParseOAuthAuthorizationServerMetadataResponse(rsp *http.Response) (*OAuthAuthorizationServerMetadataResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &OAuthAuthorizationServerMetadataResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest OAuthAuthorizationServerMetadata + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseCallbackOid4vciCredentialIssuanceResponse parses an HTTP response from a CallbackOid4vciCredentialIssuanceWithResponse call +func ParseCallbackOid4vciCredentialIssuanceResponse(rsp *http.Response) (*CallbackOid4vciCredentialIssuanceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CallbackOid4vciCredentialIssuanceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseGetTenantWebDIDResponse parses an HTTP response from a GetTenantWebDIDWithResponse call +func ParseGetTenantWebDIDResponse(rsp *http.Response) (*GetTenantWebDIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTenantWebDIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest DIDDocument + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseIntrospectAccessTokenResponse parses an HTTP response from a IntrospectAccessTokenWithResponse call +func ParseIntrospectAccessTokenResponse(rsp *http.Response) (*IntrospectAccessTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &IntrospectAccessTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TokenIntrospectionResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseRetrieveAccessTokenResponse parses an HTTP response from a RetrieveAccessTokenWithResponse call +func ParseRetrieveAccessTokenResponse(rsp *http.Response) (*RetrieveAccessTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RetrieveAccessTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TokenResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseRequestOid4vciCredentialIssuanceResponse parses an HTTP response from a RequestOid4vciCredentialIssuanceWithResponse call +func ParseRequestOid4vciCredentialIssuanceResponse(rsp *http.Response) (*RequestOid4vciCredentialIssuanceResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RequestOid4vciCredentialIssuanceResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest RedirectResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseRequestServiceAccessTokenResponse parses an HTTP response from a RequestServiceAccessTokenWithResponse call +func ParseRequestServiceAccessTokenResponse(rsp *http.Response) (*RequestServiceAccessTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RequestServiceAccessTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TokenResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseRequestUserAccessTokenResponse parses an HTTP response from a RequestUserAccessTokenWithResponse call +func ParseRequestUserAccessTokenResponse(rsp *http.Response) (*RequestUserAccessTokenResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &RequestUserAccessTokenResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest RedirectResponseWithID + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseHandleAuthorizeRequestResponse parses an HTTP response from a HandleAuthorizeRequestWithResponse call +func ParseHandleAuthorizeRequestResponse(rsp *http.Response) (*HandleAuthorizeRequestResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &HandleAuthorizeRequestResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseCallbackResponse parses an HTTP response from a CallbackWithResponse call +func ParseCallbackResponse(rsp *http.Response) (*CallbackResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CallbackResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseOAuthClientMetadataResponse parses an HTTP response from a OAuthClientMetadataWithResponse call +func ParseOAuthClientMetadataResponse(rsp *http.Response) (*OAuthClientMetadataResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &OAuthClientMetadataResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest OAuthClientMetadata + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParsePresentationDefinitionResponse parses an HTTP response from a PresentationDefinitionWithResponse call +func ParsePresentationDefinitionResponse(rsp *http.Response) (*PresentationDefinitionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PresentationDefinitionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PresentationDefinition + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} + +// ParseHandleAuthorizeResponseResponse parses an HTTP response from a HandleAuthorizeResponseWithResponse call +func ParseHandleAuthorizeResponseResponse(rsp *http.Response) (*HandleAuthorizeResponseResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &HandleAuthorizeResponseResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest RedirectResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseHandleTokenRequestResponse parses an HTTP response from a HandleTokenRequestWithResponse call +func ParseHandleTokenRequestResponse(rsp *http.Response) (*HandleTokenRequestResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &HandleTokenRequestResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TokenResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseStatusListResponse parses an HTTP response from a StatusListWithResponse call +func ParseStatusListResponse(rsp *http.Response) (*StatusListResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &StatusListResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest VerifiableCredential + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest struct { + // Detail A human-readable explanation specific to this occurrence of the problem. + Detail string `json:"detail"` + + // Status HTTP statuscode + Status float32 `json:"status"` + + // Title A short, human-readable summary of the problem type. + Title string `json:"title"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.ApplicationproblemJSONDefault = &dest + + } + + return response, nil +} diff --git a/e2e-tests/browser/client/iam/types.go b/e2e-tests/browser/client/iam/types.go new file mode 100644 index 0000000000..6180368045 --- /dev/null +++ b/e2e-tests/browser/client/iam/types.go @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package iam + +import ( + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/go-did/vc" + "github.com/nuts-foundation/nuts-node/auth/oauth" + "github.com/nuts-foundation/nuts-node/vcr/pe" + "github.com/nuts-foundation/nuts-node/vdr/resolver" +) + +// DIDDocument is an alias +type DIDDocument = did.Document + +// DIDDocumentMetadata is an alias +type DIDDocumentMetadata = resolver.DocumentMetadata + +// VerifiablePresentation is an alias +type VerifiablePresentation = vc.VerifiablePresentation + +// VerifiableCredential is an alias +type VerifiableCredential = vc.VerifiableCredential + +// ErrorResponse is an alias +type ErrorResponse = oauth.OAuth2Error + +// PresentationDefinition is an alias +type PresentationDefinition = pe.PresentationDefinition + +// PresentationSubmission is an alias +type PresentationSubmission = pe.PresentationSubmission + +type RedirectResponse = oauth.Redirect + +// TokenResponse is an alias +type TokenResponse = oauth.TokenResponse + +// OAuthAuthorizationServerMetadata is an alias +type OAuthAuthorizationServerMetadata = oauth.AuthorizationServerMetadata + +// OAuthClientMetadata is an alias +type OAuthClientMetadata = oauth.OAuthClientMetadata + +// WalletOwnerType is an alias +type WalletOwnerType = pe.WalletOwnerType + +// RequiredPresentationDefinitions is an alias +type RequiredPresentationDefinitions = pe.WalletOwnerMapping diff --git a/e2e-tests/browser/openid4vp_employeecredential/config/nuts.yaml b/e2e-tests/browser/openid4vp_employeecredential/config/nuts.yaml new file mode 100644 index 0000000000..dc26447a79 --- /dev/null +++ b/e2e-tests/browser/openid4vp_employeecredential/config/nuts.yaml @@ -0,0 +1,12 @@ +url: https://nodeA +strictmode: false +verbosity: debug +http: + log: metadata-and-body + internal: + address: :8081 +auth: + contractvalidators: + - dummy +policy: + directory: /opt/nuts/policy \ No newline at end of file diff --git a/e2e-tests/browser/openid4vp_employeecredential/config/policy/zorgtoepassing.json b/e2e-tests/browser/openid4vp_employeecredential/config/policy/zorgtoepassing.json new file mode 100644 index 0000000000..976ab3458d --- /dev/null +++ b/e2e-tests/browser/openid4vp_employeecredential/config/policy/zorgtoepassing.json @@ -0,0 +1,144 @@ +{ + "zorgtoepassing": { + "organization": { + "format": { + "ldp_vc": { + "proof_type": [ + "JsonWebSignature2020" + ] + }, + "ldp_vp": { + "proof_type": [ + "JsonWebSignature2020" + ] + }, + "jwt_vc": { + "alg": [ + "ES256" + ] + }, + "jwt_vp": { + "alg": [ + "ES256" + ] + } + }, + "id": "pd_any_care_organization", + "name": "Care organization", + "purpose": "Finding a care organization for authorizing access to medical metadata", + "input_descriptors": [ + { + "id": "id_nuts_care_organization_cred", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "const": "NutsOrganizationCredential" + } + }, + { + "id": "organization_name", + "path": [ + "$.credentialSubject.organization.name", + "$.credentialSubject[0].organization.name" + ], + "filter": { + "type": "string" + } + }, + { + "id": "organization_city", + "path": [ + "$.credentialSubject.organization.city", + "$.credentialSubject[0].organization.city" + ], + "filter": { + "type": "string" + } + } + ] + } + } + ] + }, + "user": { + "format": { + "ldp_vc": { + "proof_type": [ + "JsonWebSignature2020" + ] + }, + "ldp_vp": { + "proof_type": [ + "JsonWebSignature2020" + ] + }, + "jwt_vc": { + "alg": [ + "ES256" + ] + }, + "jwt_vp": { + "alg": [ + "ES256" + ] + } + }, + "id": "pd_any_employee_credential", + "name": "Employee", + "purpose": "Finding an employee for authorizing access to medical metadata", + "input_descriptors": [ + { + "id": "id_employee_credential_cred", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "const": "EmployeeCredential" + } + }, + { + "id": "employee_identifier", + "path": [ + "$.credentialSubject.identifier", + "$.credentialSubject[0].identifier" + ], + "filter": { + "type": "string" + } + }, + { + "id": "employee_name", + "path": [ + "$.credentialSubject.name", + "$.credentialSubject[0].name" + ], + "filter": { + "type": "string" + } + }, + { + "id": "employee_role", + "path": [ + "$.credentialSubject.roleName", + "$.credentialSubject[0].roleName" + ], + "filter": { + "type": "string" + } + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/e2e-tests/browser/openid4vp_employeecredential/docker-compose.yml b/e2e-tests/browser/openid4vp_employeecredential/docker-compose.yml new file mode 100644 index 0000000000..4406e7b490 --- /dev/null +++ b/e2e-tests/browser/openid4vp_employeecredential/docker-compose.yml @@ -0,0 +1,32 @@ +services: + nodeA-backend: + image: "${IMAGE_NODE_A:-nutsfoundation/nuts-node:master}" + environment: + NUTS_CONFIGFILE: /opt/nuts/nuts.yaml + ports: + - 8080:8080 + - 8081:8081 + volumes: + - "./config/nuts.yaml:/opt/nuts/nuts.yaml" + - "./config/policy/:/opt/nuts/policy:ro" + # did:web resolver uses the OS CA bundle, but e2e tests use a self-signed CA which can be found in truststore.pem + # So we need to mount that file to the OS CA bundle location, otherwise did:web resolving will fail due to untrusted certs. + - "../../tls-certs/truststore.pem:/etc/ssl/certs/Nuts_RootCA.pem:ro" + nodeA: + image: nginx:1.25.1 + ports: + - "443:443" + volumes: + - "../../shared_config/nodeA-http-nginx.conf:/etc/nginx/conf.d/nuts-http.conf:ro" + - "../../tls-certs/nodeA-certificate.pem:/etc/nginx/ssl/server.pem:ro" + - "../../tls-certs/nodeA-certificate.pem:/etc/nginx/ssl/key.pem:ro" + - "../../tls-certs/truststore.pem:/etc/nginx/ssl/truststore.pem:ro" + chrome-headless-shell: + image: chromedp/headless-shell:latest + ports: + - 9222:9222 + # Copied the entrypoint from the image's Dockerfile (https://github.com/chromedp/docker-headless-shell/blob/master/Dockerfile) + # and added "--ignore-certificate-errors" to ignore self-signed certs in the e2e tests. + # Otherwise, it fails with: + # page load error net::ERR_CERT_AUTHORITY_INVALID + entrypoint: ["/headless-shell/headless-shell", "--no-sandbox", "--use-gl=angle", "--use-angle=swiftshader", "--remote-debugging-address=0.0.0.0", "--remote-debugging-port=9222", "--ignore-certificate-errors"] diff --git a/e2e-tests/browser/openid4vp_employeecredential/main_test.go b/e2e-tests/browser/openid4vp_employeecredential/main_test.go new file mode 100644 index 0000000000..385b622903 --- /dev/null +++ b/e2e-tests/browser/openid4vp_employeecredential/main_test.go @@ -0,0 +1,124 @@ +//go:build e2e_tests + +/* + * Copyright (C) 2023 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package openid4vp_employeecredential + +import ( + "github.com/chromedp/chromedp" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/core" + "github.com/nuts-foundation/nuts-node/e2e-tests/browser" + iamAPI "github.com/nuts-foundation/nuts-node/e2e-tests/browser/client/iam" + "github.com/nuts-foundation/nuts-node/e2e-tests/browser/rfc019_selfsigned/apps" + didAPI "github.com/nuts-foundation/nuts-node/vdr/api/v2" + "github.com/stretchr/testify/require" + "os" + "testing" + "time" +) + +var nodeClientConfig = core.ClientConfig{Address: "http://localhost:8081"} + +func init() { + // uncomment this to get feedback during development + // os.Setenv("SHOW_BROWSER", "true") + // os.Setenv("KEEP_BROWSER_OPEN", "true") +} + +func Test_UserAccessToken_EmployeeCredential(t *testing.T) { + const oauth2Scope = "zorgtoepassing" + + headless := os.Getenv("SHOW_BROWSER") != "true" + ctx, cancel := browser.NewChrome(headless) + defer func() { + if t.Failed() && !headless { + duration := time.Minute + t.Logf("Test failed, keeping browser open for %s", duration) + time.Sleep(duration) + } + cancel() + }() + + verifyingOrganization, err := createDID("verifier") + require.NoError(t, err) + err = browser.IssueOrganizationCredential(verifyingOrganization, "Verifying Organization", "Testland") + require.NoError(t, err) + + requesterOrganization, err := createDID("requester") + require.NoError(t, err) + err = browser.IssueOrganizationCredential(requesterOrganization, "Requesting Organization", "Testland") + require.NoError(t, err) + + iamClient, err := iamAPI.NewClient(nodeClientConfig.GetAddress()) + require.NoError(t, err) + openid4vp := OpenID4VP{ + ctx: ctx, + iamClient: iamClient, + } + err = chromedp.Run(ctx, chromedp.Navigate("about:blank")) + require.NoError(t, err) + // Request an access token with user from verifying organization + userDetails := iamAPI.UserDetails{ + Id: "jdoe@example.com", + Name: "John Doe", + Role: "Accountant", + } + redirectSession, err := openid4vp.RequesterUserAccessToken(requesterOrganization.ID, verifyingOrganization.ID, userDetails, oauth2Scope) + require.NoError(t, err) + // Navigate browser to redirect URL, which performs the OAuth2 authorization code flow + err = chromedp.Run(ctx, chromedp.Navigate(redirectSession.RedirectUri)) + require.NoError(t, err) + // The browser was now successfully redirected back to the redirect URI (actually the node's public / URL), + // indicating the flow was successful. We can now retrieve the access token. + accessToken, err := openid4vp.RetrieveAccessToken(redirectSession.SessionId) + require.NoError(t, err) + // In a real-world scenario, this client would now use the access token to request some resources. + // We just introspect the access token (which we can since Client and Authorization Server are the same Nuts node), + // to verify the access token. + tokenInfo, err := openid4vp.IntrospectAccessToken(accessToken) + require.NoError(t, err) + require.True(t, tokenInfo.Active) + require.Equal(t, oauth2Scope, *tokenInfo.Scope) + // Note to reviewer: audience is empty? + require.Equal(t, requesterOrganization.ID.String(), *tokenInfo.ClientId) + require.Equal(t, verifyingOrganization.ID.String(), *tokenInfo.Iss) + // Note to reviewer: is "sub" right? + require.Equal(t, verifyingOrganization.ID.String(), *tokenInfo.Sub) + require.NotEmpty(t, tokenInfo.Exp) + require.NotEmpty(t, tokenInfo.Iat) + // Check the mapped input descriptor fields: for organization credential and employee credential + require.NotEmpty(t, tokenInfo.AdditionalProperties) + require.Equal(t, "Requesting Organization", tokenInfo.AdditionalProperties["organization_name"].(string)) + require.Equal(t, "Testland", tokenInfo.AdditionalProperties["organization_city"].(string)) + require.Equal(t, "jdoe@example.com", tokenInfo.AdditionalProperties["employee_identifier"].(string)) + require.Equal(t, "John Doe", tokenInfo.AdditionalProperties["employee_name"].(string)) + require.Equal(t, "Accountant", tokenInfo.AdditionalProperties["employee_role"].(string)) + + if os.Getenv("KEEP_BROWSER_OPEN") == "true" { + timeout := time.Minute + t.Logf("Keeping browser open for %s", timeout) + time.Sleep(timeout) + } +} + +func createDID(id string) (*did.Document, error) { + didClient := didAPI.HTTPClient{ClientConfig: apps.NodeClientConfig} + return didClient.Create(didAPI.CreateDIDOptions{Tenant: &id}) +} diff --git a/e2e-tests/browser/openid4vp_employeecredential/openid4vp.go b/e2e-tests/browser/openid4vp_employeecredential/openid4vp.go new file mode 100644 index 0000000000..d7ad8a90f1 --- /dev/null +++ b/e2e-tests/browser/openid4vp_employeecredential/openid4vp.go @@ -0,0 +1,81 @@ +//go:build e2e_tests + +/* + * Copyright (C) 2024 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package openid4vp_employeecredential + +import ( + "context" + "fmt" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/e2e-tests/browser/client/iam" +) + +type OpenID4VP struct { + ctx context.Context + iamClient iam.ClientInterface +} + +func (o OpenID4VP) RequesterUserAccessToken(requesterDID, verifierDID did.DID, user iam.UserDetails, scope string) (*iam.RedirectResponseWithID, error) { + httpResponse, err := o.iamClient.RequestUserAccessToken(o.ctx, requesterDID.String(), iam.RequestUserAccessTokenJSONRequestBody{ + PreauthorizedUser: &user, + RedirectUri: "https://nodeA", // doesn't really matter where we redirect to + Scope: scope, + Verifier: verifierDID.String(), + }) + if err != nil { + return nil, err + } + response, err := iam.ParseRequestUserAccessTokenResponse(httpResponse) + if err != nil { + return nil, err + } + if response.ApplicationproblemJSONDefault != nil { + return nil, fmt.Errorf("application problem: %s", response.ApplicationproblemJSONDefault.Detail) + } + return response.JSON200, nil +} +func (o OpenID4VP) RetrieveAccessToken(sessionID string) (string, error) { + httpResponse, err := o.iamClient.RetrieveAccessToken(o.ctx, sessionID) + if err != nil { + return "", err + } + response, err := iam.ParseRetrieveAccessTokenResponse(httpResponse) + if err != nil { + return "", err + } + if response.ApplicationproblemJSONDefault != nil { + return "", fmt.Errorf("application problem: %s", response.ApplicationproblemJSONDefault.Detail) + } + return response.JSON200.AccessToken, nil +} + +func (o OpenID4VP) IntrospectAccessToken(token string) (*iam.TokenIntrospectionResponse, error) { + httpResponse, err := o.iamClient.IntrospectAccessTokenWithFormdataBody(o.ctx, iam.IntrospectAccessTokenFormdataRequestBody{ + Token: token, + }) + if err != nil { + return nil, err + } + response, err := iam.ParseIntrospectAccessTokenResponse(httpResponse) + if err != nil { + return nil, err + } + return response.JSON200, nil +} diff --git a/e2e-tests/browser/openid4vp_employeecredential/run-test.sh b/e2e-tests/browser/openid4vp_employeecredential/run-test.sh new file mode 100755 index 0000000000..7e89ef487e --- /dev/null +++ b/e2e-tests/browser/openid4vp_employeecredential/run-test.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +source ../../util.sh + +set -e # make script fail if any of the tests returns a non-zero exit code + +# Shut down existing containers +docker compose stop +docker compose rm -f -v + +# Start new stack +docker compose up --wait + +go test -v --tags=e2e_tests -count=1 . + +docker compose stop \ No newline at end of file diff --git a/e2e-tests/auth/selfsigned/apps/selfsigned.go b/e2e-tests/browser/rfc019_selfsigned/apps/selfsigned.go similarity index 100% rename from e2e-tests/auth/selfsigned/apps/selfsigned.go rename to e2e-tests/browser/rfc019_selfsigned/apps/selfsigned.go diff --git a/e2e-tests/auth/selfsigned/config/node/nuts.yaml b/e2e-tests/browser/rfc019_selfsigned/config/node/nuts.yaml similarity index 100% rename from e2e-tests/auth/selfsigned/config/node/nuts.yaml rename to e2e-tests/browser/rfc019_selfsigned/config/node/nuts.yaml diff --git a/e2e-tests/auth/selfsigned/docker-compose.yml b/e2e-tests/browser/rfc019_selfsigned/docker-compose.yml similarity index 100% rename from e2e-tests/auth/selfsigned/docker-compose.yml rename to e2e-tests/browser/rfc019_selfsigned/docker-compose.yml diff --git a/e2e-tests/auth/selfsigned/main_test.go b/e2e-tests/browser/rfc019_selfsigned/main_test.go similarity index 80% rename from e2e-tests/auth/selfsigned/main_test.go rename to e2e-tests/browser/rfc019_selfsigned/main_test.go index f2e374cf55..b2f1deea2b 100644 --- a/e2e-tests/auth/selfsigned/main_test.go +++ b/e2e-tests/browser/rfc019_selfsigned/main_test.go @@ -18,14 +18,13 @@ * */ -package selfsigned +package rfc019_selfsigned import ( "github.com/nuts-foundation/go-did/did" didmanAPI "github.com/nuts-foundation/nuts-node/didman/api/v1" - "github.com/nuts-foundation/nuts-node/e2e-tests/auth/selfsigned/apps" - "github.com/nuts-foundation/nuts-node/e2e-tests/auth/selfsigned/browser" - vcrAPI "github.com/nuts-foundation/nuts-node/vcr/api/vcr/v2" + "github.com/nuts-foundation/nuts-node/e2e-tests/browser" + "github.com/nuts-foundation/nuts-node/e2e-tests/browser/rfc019_selfsigned/apps" didAPI "github.com/nuts-foundation/nuts-node/vdr/api/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -52,14 +51,14 @@ func Test_LoginWithSelfSignedMeans(t *testing.T) { require.NoError(t, err) err = registerCompoundService(verifyingOrganization.ID, purposeOfUse) require.NoError(t, err) - err = issueOrganizationCredential(verifyingOrganization, "Verifying Organization", "Testland") + err = browser.IssueOrganizationCredential(verifyingOrganization, "Verifying Organization", "Testland") require.NoError(t, err) issuingOrganization, err := createDID() require.NoError(t, err) err = registerCompoundService(issuingOrganization.ID, purposeOfUse) require.NoError(t, err) - err = issueOrganizationCredential(issuingOrganization, "Issuing Organization", "Testland") + err = browser.IssueOrganizationCredential(issuingOrganization, "Issuing Organization", "Testland") require.NoError(t, err) selfSigned := apps.SelfSigned{ @@ -114,28 +113,6 @@ func Test_LoginWithSelfSignedMeans(t *testing.T) { } } -func issueOrganizationCredential(organization *did.Document, name, city string) error { - vcrClient := vcrAPI.HTTPClient{ClientConfig: apps.NodeClientConfig} - visibility := vcrAPI.Public - request := vcrAPI.IssueVCRequest{ - Issuer: organization.ID.String(), - CredentialSubject: map[string]interface{}{ - "id": organization.ID.String(), - "organization": map[string]interface{}{ - "name": name, - "city": city, - }, - }, - Visibility: &visibility, - } - err := request.Type.FromIssueVCRequestType1([]string{"VerifiableCredential", "NutsOrganizationCredential"}) - if err != nil { - return err - } - _, err = vcrClient.IssueVC(request) - return err -} - func registerCompoundService(id did.DID, compoundServiceType string) error { client := didmanAPI.HTTPClient{ClientConfig: apps.NodeClientConfig} _, err := client.AddCompoundService(id.String(), compoundServiceType, map[string]string{ diff --git a/e2e-tests/auth/selfsigned/run-test.sh b/e2e-tests/browser/rfc019_selfsigned/run-test.sh similarity index 100% rename from e2e-tests/auth/selfsigned/run-test.sh rename to e2e-tests/browser/rfc019_selfsigned/run-test.sh diff --git a/e2e-tests/browser/run-tests.sh b/e2e-tests/browser/run-tests.sh new file mode 100755 index 0000000000..f883a8ce01 --- /dev/null +++ b/e2e-tests/browser/run-tests.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e # make script fail if any of the tests returns a non-zero exit code + +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +echo "!! Running test: OpenID4VP Employee Credential !!" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +pushd openid4vp_employeecredential +./run-test.sh +popd + + +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +echo "!! Running test: RFC019 Employee Credential !!" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +pushd rfc019_selfsigned +./run-test.sh +popd + diff --git a/e2e-tests/browser/util.go b/e2e-tests/browser/util.go new file mode 100644 index 0000000000..4e40b9fc3d --- /dev/null +++ b/e2e-tests/browser/util.go @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 Nuts community + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +//go:build e2e_tests + +package browser + +import ( + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/nuts-node/e2e-tests/browser/rfc019_selfsigned/apps" + vcrAPI "github.com/nuts-foundation/nuts-node/vcr/api/vcr/v2" +) + +func IssueOrganizationCredential(organization *did.Document, name, city string) error { + vcrClient := vcrAPI.HTTPClient{ClientConfig: apps.NodeClientConfig} + request := vcrAPI.IssueVCRequest{ + Issuer: organization.ID.String(), + CredentialSubject: map[string]interface{}{ + "id": organization.ID.String(), + "organization": map[string]interface{}{ + "name": name, + "city": city, + }, + }, + } + switch organization.ID.Method { + case "web": + withStatusList2021Revocation := false + request.WithStatusList2021Revocation = &withStatusList2021Revocation + case "nuts": + visibility := vcrAPI.Public + request.Visibility = &visibility + } + err := request.Type.FromIssueVCRequestType1(vcrAPI.IssueVCRequestType1{"VerifiableCredential", "NutsOrganizationCredential"}) + if err != nil { + return err + } + issuedCredential, err := vcrClient.IssueVC(request) + if err != nil { + return err + } + if organization.ID.Method == "web" { + // Need to load it into tbe wallet + return vcrClient.LoadVC(organization.ID, *issuedCredential) + } + return nil +} diff --git a/e2e-tests/oauth-flow/openid4vp/do-test.sh b/e2e-tests/oauth-flow/openid4vp/do-test.sh index 7e5e700666..51a2b369ea 100755 --- a/e2e-tests/oauth-flow/openid4vp/do-test.sh +++ b/e2e-tests/oauth-flow/openid4vp/do-test.sh @@ -64,7 +64,7 @@ echo "Redirect user to local OAuth server..." echo "--------------------------------------" LOCATION=$(echo $LOCATION | sed -E 's/nodeB/localhost:20443/') -RESPONSE=$(curl -D ./node-B/headers.txt $LOCATION -k) +RESPONSE=$(curl --cookie-jar ./node-B/cookies.txt -D ./node-B/headers.txt $LOCATION -k) if grep -q 'Location' ./node-B/headers.txt; then LOCATION=$(grep 'Location' ./node-B/headers.txt | sed -E 's/Location: (.*)/\1/' | tr -d '\r') echo "REDIRECTURL: $LOCATION" @@ -94,7 +94,7 @@ echo "Build VP..." echo "---------------------------------------" LOCATION=$(echo $LOCATION | sed -E 's/nodeB/localhost:20443/') -RESPONSE=$(curl -D ./node-B/headers.txt $LOCATION -k) +RESPONSE=$(curl --cookie ./node-B/cookies.txt -D ./node-B/headers.txt $LOCATION -k) if grep -q 'Location' ./node-B/headers.txt; then LOCATION=$(grep 'Location' ./node-B/headers.txt | sed -E 's/Location: (.*)/\1/' | tr -d '\r') echo "REDIRECTURL: $LOCATION" diff --git a/e2e-tests/run-tests.sh b/e2e-tests/run-tests.sh index 1d110f798f..c6df3fb2ae 100755 --- a/e2e-tests/run-tests.sh +++ b/e2e-tests/run-tests.sh @@ -30,10 +30,10 @@ pushd openid4vci ./run-tests.sh popd -echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" -echo "!! Running test suite: Auth !!" -echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" -pushd auth +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +echo "!! Running test suite: browser !!" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +pushd browser ./run-tests.sh popd diff --git a/makefile b/makefile index 0353a22acf..96cc327172 100644 --- a/makefile +++ b/makefile @@ -75,6 +75,7 @@ gen-api: oapi-codegen --config codegen/configs/auth_client_v1.yaml docs/_static/auth/v1.yaml | gofmt > auth/api/auth/v1/client/generated.go oapi-codegen --config codegen/configs/auth_employeeid.yaml auth/services/selfsigned/web/spec.yaml | gofmt > auth/services/selfsigned/web/generated.go oapi-codegen --config codegen/configs/auth_iam.yaml docs/_static/auth/iam.yaml | gofmt > auth/api/iam/generated.go + oapi-codegen -generate client,types --config codegen/configs/auth_iam.yaml docs/_static/auth/iam.yaml | gofmt > e2e-tests/browser/client/iam/generated.go oapi-codegen --config codegen/configs/didman_v1.yaml docs/_static/didman/v1.yaml | gofmt > didman/api/v1/generated.go oapi-codegen --config codegen/configs/discovery_v1.yaml docs/_static/discovery/v1.yaml | gofmt > discovery/api/v1/generated.go oapi-codegen --config codegen/configs/discovery_server.yaml docs/_static/discovery/server.yaml | gofmt > discovery/api/server/generated.go diff --git a/vcr/api/vcr/v2/client.go b/vcr/api/vcr/v2/client.go index b9020ca95b..db0676a6d2 100644 --- a/vcr/api/vcr/v2/client.go +++ b/vcr/api/vcr/v2/client.go @@ -23,6 +23,8 @@ import ( "context" "encoding/json" "fmt" + "github.com/nuts-foundation/go-did/did" + "github.com/nuts-foundation/go-did/vc" "github.com/nuts-foundation/nuts-node/core" "io" "net/http" @@ -90,6 +92,19 @@ func (hb HTTPClient) Untrusted(credentialType string) ([]string, error) { return handleTrustedResponse(hb.client().ListUntrusted(ctx, credentialType)) } +// LoadVC loads the given Verifiable Credential into the holder's wallet. +func (hb HTTPClient) LoadVC(holder did.DID, credential vc.VerifiableCredential) error { + ctx := context.Background() + + httpResponse, err := hb.client().LoadVC(ctx, holder.String(), credential) + if err != nil { + return err + } else if err := core.TestResponseCode(http.StatusNoContent, httpResponse); err != nil { + return err + } + return nil +} + // IssueVC issues a new Verifiable Credential and returns it func (hb HTTPClient) IssueVC(request IssueVCRequest) (*VerifiableCredential, error) { ctx := context.Background() diff --git a/vcr/holder/memory_wallet.go b/vcr/holder/memory_wallet.go index 050c6d3b63..d5ac93d76d 100644 --- a/vcr/holder/memory_wallet.go +++ b/vcr/holder/memory_wallet.go @@ -31,13 +31,13 @@ import ( "github.com/piprate/json-gold/ld" ) -func NewMemoryWallet(documentLoader ld.DocumentLoader, keyResolver resolver.KeyResolver, keyStore crypto.KeyStore, +func NewMemoryWallet(documentLoader ld.DocumentLoader, keyResolver resolver.KeyResolver, signer crypto.JWTSigner, credentials map[did.DID][]vc.VerifiableCredential) Wallet { return &memoryWallet{ credentials: credentials, documentLoader: documentLoader, keyResolver: keyResolver, - keyStore: keyStore, + signer: signer, } } @@ -45,7 +45,7 @@ type memoryWallet struct { credentials map[did.DID][]vc.VerifiableCredential documentLoader ld.DocumentLoader keyResolver resolver.KeyResolver - keyStore crypto.KeyStore + signer crypto.JWTSigner } var _ Wallet = (*memoryWallet)(nil) @@ -53,7 +53,7 @@ var _ Wallet = (*memoryWallet)(nil) func (m memoryWallet) BuildPresentation(ctx context.Context, credentials []vc.VerifiableCredential, options PresentationOptions, signerDID *did.DID, validateVC bool) (*vc.VerifiablePresentation, error) { return presenter{ documentLoader: m.documentLoader, - keyStore: m.keyStore, + signer: m.signer, keyResolver: m.keyResolver, }.buildPresentation(ctx, signerDID, credentials, options) } @@ -61,7 +61,7 @@ func (m memoryWallet) BuildPresentation(ctx context.Context, credentials []vc.Ve func (m memoryWallet) BuildSubmission(ctx context.Context, walletDID did.DID, presentationDefinition pe.PresentationDefinition, acceptedFormats map[string]map[string][]string, params BuildParams) (*vc.VerifiablePresentation, *pe.PresentationSubmission, error) { return presenter{ documentLoader: m.documentLoader, - keyStore: m.keyStore, + signer: m.signer, keyResolver: m.keyResolver, }.buildSubmission(ctx, walletDID, m.credentials[walletDID], presentationDefinition, acceptedFormats, params) } diff --git a/vcr/holder/presenter.go b/vcr/holder/presenter.go index 18f78a5e50..1e3d9b2f07 100644 --- a/vcr/holder/presenter.go +++ b/vcr/holder/presenter.go @@ -43,7 +43,7 @@ import ( type presenter struct { documentLoader ld.DocumentLoader - keyStore crypto.KeyStore + signer crypto.JWTSigner keyResolver resolver.KeyResolver } @@ -164,7 +164,7 @@ func (p presenter) buildJWTPresentation(ctx context.Context, subjectDID did.DID, for claimName, value := range options.ProofOptions.AdditionalProperties { claims[claimName] = value } - token, err := p.keyStore.SignJWT(ctx, claims, headers, keyID) + token, err := p.signer.SignJWT(ctx, claims, headers, keyID) if err != nil { return nil, fmt.Errorf("unable to sign JWT presentation: %w", err) } @@ -200,7 +200,7 @@ func (p presenter) buildJSONLDPresentation(ctx context.Context, subjectDID did.D ldProof := proof.NewLDProof(options.ProofOptions) signingResult, err := ldProof. - Sign(ctx, document, signature.JSONWebSignature2020{ContextLoader: p.documentLoader, Signer: p.keyStore}, keyID) + Sign(ctx, document, signature.JSONWebSignature2020{ContextLoader: p.documentLoader, Signer: p.signer}, keyID) if err != nil { return nil, fmt.Errorf("unable to sign VP with LD proof: %w", err) } diff --git a/vcr/holder/presenter_test.go b/vcr/holder/presenter_test.go index e6879cb922..12d1f16e0a 100644 --- a/vcr/holder/presenter_test.go +++ b/vcr/holder/presenter_test.go @@ -59,7 +59,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver := resolver.NewMockKeyResolver(ctrl) keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} result, err := w.buildPresentation(ctx, &testDID, []vc.VerifiableCredential{testCredential}, PresentationOptions{}) @@ -73,7 +73,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver := resolver.NewMockKeyResolver(ctrl) keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} result, err := w.buildPresentation(ctx, &testDID, []vc.VerifiableCredential{testCredential}, PresentationOptions{Format: JSONLDPresentationFormat}) @@ -106,7 +106,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} result, err := w.buildPresentation(ctx, &testDID, []vc.VerifiableCredential{testCredential}, options) @@ -129,7 +129,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDA.URI(), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} resultingPresentation, err := w.buildPresentation(ctx, &testDID, []vc.VerifiableCredential{testCredential, testCredential}, PresentationOptions{Format: JSONLDPresentationFormat}) @@ -145,7 +145,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver := resolver.NewMockKeyResolver(ctrl) keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} result, err := w.buildPresentation(ctx, &testDID, []vc.VerifiableCredential{testCredential}, options) @@ -166,7 +166,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(vdr.TestMethodDIDA.URI(), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} result, err := w.buildPresentation(ctx, &testDID, []vc.VerifiableCredential{testCredential, testCredential}, options) @@ -197,7 +197,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver := resolver.NewMockKeyResolver(ctrl) keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} result, err := w.buildPresentation(ctx, &testDID, []vc.VerifiableCredential{testCredential}, options) @@ -224,7 +224,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver.EXPECT().ResolveKey(testDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(kid), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} resultingPresentation, err := w.buildPresentation(ctx, nil, []vc.VerifiableCredential{testCredential, testCredential}, options) @@ -239,7 +239,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver := resolver.NewMockKeyResolver(ctrl) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} resultingPresentation, err := w.buildPresentation(ctx, nil, []vc.VerifiableCredential{testCredential, secondCredential}, options) @@ -254,7 +254,7 @@ func TestPresenter_buildPresentation(t *testing.T) { keyResolver := resolver.NewMockKeyResolver(ctrl) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} resultingPresentation, err := w.buildPresentation(ctx, nil, []vc.VerifiableCredential{testCredential, secondCredential}, options) @@ -287,7 +287,7 @@ func TestPresenter_buildSubmission(t *testing.T) { keyResolver := resolver.NewMockKeyResolver(ctrl) keyResolver.EXPECT().ResolveKey(walletDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(key.KID()), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} vp, submission, err := w.buildSubmission(ctx, walletDID, credentials, presentationDefinition, vpFormats, BuildParams{Audience: verifierDID.String(), Expires: time.Now().Add(time.Second), Nonce: ""}) @@ -312,7 +312,7 @@ func TestPresenter_buildSubmission(t *testing.T) { ctrl := gomock.NewController(t) keyResolver := resolver.NewMockKeyResolver(ctrl) keyResolver.EXPECT().ResolveKey(walletDID, nil, resolver.NutsSigningKeyType).Return(ssi.MustParseURI(key.KID()), key.Public(), nil) - w := presenter{documentLoader: jsonldManager.DocumentLoader(), keyStore: keyStore, keyResolver: keyResolver} + w := presenter{documentLoader: jsonldManager.DocumentLoader(), signer: keyStore, keyResolver: keyResolver} vp, submission, err := w.buildSubmission(ctx, walletDID, credentials, pe.PresentationDefinition{}, vpFormats, BuildParams{Audience: verifierDID.String(), Expires: time.Now().Add(time.Second), Nonce: ""}) diff --git a/vcr/holder/sql_wallet.go b/vcr/holder/sql_wallet.go index 9356eac958..734614efbe 100644 --- a/vcr/holder/sql_wallet.go +++ b/vcr/holder/sql_wallet.go @@ -80,7 +80,7 @@ func (h sqlWallet) BuildSubmission(ctx context.Context, walletDID did.DID, prese } return presenter{ documentLoader: h.jsonldManager.DocumentLoader(), - keyStore: h.keyStore, + signer: h.keyStore, keyResolver: h.keyResolver, }.buildSubmission(ctx, walletDID, credentials, presentationDefinition, acceptedFormats, params) } @@ -96,7 +96,7 @@ func (h sqlWallet) BuildPresentation(ctx context.Context, credentials []vc.Verif } return presenter{ documentLoader: h.jsonldManager.DocumentLoader(), - keyStore: h.keyStore, + signer: h.keyStore, keyResolver: h.keyResolver, }.buildPresentation(ctx, signerDID, credentials, options) } diff --git a/vcr/pe/presentation_definition_test.go b/vcr/pe/presentation_definition_test.go index 154b50d069..bc5b001bc6 100644 --- a/vcr/pe/presentation_definition_test.go +++ b/vcr/pe/presentation_definition_test.go @@ -100,6 +100,91 @@ func TestParsePresentationDefinition(t *testing.T) { }) } +func TestEmployeeCredential(t *testing.T) { + pd, err := ParsePresentationDefinition([]byte(`{ + "format": { + "ldp_vc": { + "proof_type": [ + "JsonWebSignature2020" + ] + }, + "ldp_vp": { + "proof_type": [ + "JsonWebSignature2020" + ] + }, + "jwt_vc": { + "alg": [ + "ES256" + ] + }, + "jwt_vp": { + "alg": [ + "ES256" + ] + } + }, + "id": "pd_any_employee_credential", + "name": "Employee", + "purpose": "Finding an employee for authorizing access to medical metadata", + "input_descriptors": [ + { + "id": "id_employee_credential_cred", + "constraints": { + "fields": [ + { + "path": [ + "$.type" + ], + "filter": { + "type": "string", + "const": "EmployeeCredential" + } + }, + { + "id": "employee_identifier", + "path": [ + "$.credentialSubject.identifier", + "$.credentialSubject[0].identifier" + ], + "filter": { + "type": "string" + } + }, + { + "id": "employee_name", + "path": [ + "$.credentialSubject.name", + "$.credentialSubject[0].name" + ], + "filter": { + "type": "string" + } + }, + { + "id": "employee_role", + "path": [ + "$.credentialSubject.roleName", + "$.credentialSubject[0].roleName" + ], + "filter": { + "type": "string" + } + } + ] + } + } + ] + }`)) + require.NoError(t, err) + cred, err := vc.ParseVerifiableCredential(`eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDp3ZWI6bm9kZUE6aWFtOnJlcXVlc3RlciMwIiwidHlwIjoiSldUIn0.eyJleHAiOjE3MTI1NzI2MTIsImlzcyI6ImRpZDp3ZWI6bm9kZUE6aWFtOnJlcXVlc3RlciIsImp0aSI6ImRpZDp3ZWI6bm9kZUE6aWFtOnJlcXVlc3RlciM4ZjNkMWI0OS1iODYzLTQxZjYtYmY4Ny05ZTVhODY2YWMyMzEiLCJuYmYiOjE3MTI1NjkwMTIsInN1YiI6ImRpZDpqd2s6ZXlKamNuWWlPaUpRTFRJMU5pSXNJbXQwZVNJNklrVkRJaXdpZUNJNklsOVFVVk5TVkY5UmRFWmpOMFpvU0VwUGFGVXRibEJ2TVVaNGRFZDRTRmRqUzJ0b2FqUTVhRE5hT1VVaUxDSjVJam9pTmpoek16TTFOVkJIYm5CcVVVSkVNamRKTjE4NVpFUnpkRzU2TVVwZlZtZzNZVlprUlVOVlNHOXpRU0o5IiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiLCJodHRwczovL251dHMubmwvY3JlZGVudGlhbHMvdjEiXSwiY3JlZGVudGlhbFN1YmplY3QiOlt7ImlkIjoiZGlkOmp3azpleUpqY25ZaU9pSlFMVEkxTmlJc0ltdDBlU0k2SWtWRElpd2llQ0k2SWw5UVVWTlNWRjlSZEVaak4wWm9TRXBQYUZVdGJsQnZNVVo0ZEVkNFNGZGpTMnRvYWpRNWFETmFPVVVpTENKNUlqb2lOamh6TXpNMU5WQkhibkJxVVVKRU1qZEpOMTg1WkVSemRHNTZNVXBmVm1nM1lWWmtSVU5WU0c5elFTSjkiLCJpZGVudGlmaWVyIjoiamRvZUBleGFtcGxlLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsInJvbGVOYW1lIjoiQWNjb3VudGFudCJ9XSwidHlwZSI6WyJFbXBsb3llZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdfX0.6VeGDsTEy2BpQW3RKCiczIVoAAdlfl_EP4KioE9lavWIuXTASTAPkcY9oOlfG_HFLZvu82Nnt6L-ntK8XzR7Ew`) + + credentials, _, err := pd.Match([]vc.VerifiableCredential{*cred}) + + require.NoError(t, err) + require.Len(t, credentials, 1) +} + func TestMatch(t *testing.T) { jsonldVC := vcrTest.ValidNutsOrganizationCredential(t) jwtVC := vcrTest.JWTNutsOrganizationCredential(t, did.MustParseDID("did:web:example.com")) diff --git a/vdr/api/v2/client.go b/vdr/api/v2/client.go index f93c5cb3b4..4b36f8a075 100644 --- a/vdr/api/v2/client.go +++ b/vdr/api/v2/client.go @@ -46,10 +46,10 @@ func (hb HTTPClient) client() ClientInterface { // Create calls the server and creates a new DID Document // It does not parse a custom id but depends on the server to generate one -func (hb HTTPClient) Create() (*did.Document, error) { +func (hb HTTPClient) Create(options CreateDIDOptions) (*did.Document, error) { ctx := context.Background() - if response, err := hb.client().CreateDID(ctx, CreateDIDJSONRequestBody{}); err != nil { + if response, err := hb.client().CreateDID(ctx, options); err != nil { return nil, err } else if err := core.TestResponseCode(http.StatusOK, response); err != nil { return nil, err diff --git a/vdr/api/v2/client_test.go b/vdr/api/v2/client_test.go index 4cd94046cf..4e1986b78b 100644 --- a/vdr/api/v2/client_test.go +++ b/vdr/api/v2/client_test.go @@ -41,7 +41,7 @@ func TestHTTPClient_Create(t *testing.T) { t.Run("ok", func(t *testing.T) { s := httptest.NewServer(&http2.Handler{StatusCode: http.StatusOK, ResponseData: didDoc}) c := getClient(s.URL) - doc, err := c.Create() + doc, err := c.Create(CreateDIDOptions{}) require.NoError(t, err) assert.NotNil(t, doc) }) @@ -49,13 +49,13 @@ func TestHTTPClient_Create(t *testing.T) { t.Run("error - server error", func(t *testing.T) { s := httptest.NewServer(&http2.Handler{StatusCode: http.StatusInternalServerError, ResponseData: ""}) c := getClient(s.URL) - _, err := c.Create() + _, err := c.Create(CreateDIDOptions{}) assert.Error(t, err) }) t.Run("error - wrong address", func(t *testing.T) { c := getClient("not_an_address") - _, err := c.Create() + _, err := c.Create(CreateDIDOptions{}) assert.Error(t, err) }) } diff --git a/vdr/cmd/cmd.go b/vdr/cmd/cmd.go index 1183bafb81..4de0d14253 100644 --- a/vdr/cmd/cmd.go +++ b/vdr/cmd/cmd.go @@ -91,7 +91,7 @@ func createCmd() *cobra.Command { err error ) if useV2 { - doc, err = httpClientV2(clientConfig).Create() + doc, err = httpClientV2(clientConfig).Create(apiv2.CreateDIDOptions{}) } else { doc, err = httpClient(clientConfig).Create(createRequest) }