From 93ff9b88984780b1147fc99297678c484f8efee2 Mon Sep 17 00:00:00 2001 From: Wout Slakhorst Date: Tue, 12 Dec 2023 17:00:26 +0100 Subject: [PATCH] wip4 --- auth/api/iam/openid4vp.go | 31 ++++++++++++++++++++++++++++++- auth/client/iam/client.go | 30 ++++++++++++++++++++++++++++++ auth/oauth/types.go | 2 ++ auth/services/oauth/holder.go | 13 ++++++++++++- auth/services/oauth/interface.go | 2 ++ 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/auth/api/iam/openid4vp.go b/auth/api/iam/openid4vp.go index 76b45b5d2d..db6aa5a454 100644 --- a/auth/api/iam/openid4vp.go +++ b/auth/api/iam/openid4vp.go @@ -37,6 +37,7 @@ import ( "github.com/nuts-foundation/nuts-node/storage" "github.com/nuts-foundation/nuts-node/vcr/credential" "github.com/nuts-foundation/nuts-node/vcr/holder" + "github.com/nuts-foundation/nuts-node/vcr/pe" "github.com/nuts-foundation/nuts-node/vdr/didweb" "net/http" "net/url" @@ -266,8 +267,36 @@ func (r Wrapper) handleAuthorizeRequestFromVerifier(ctx context.Context, walletD }, nil } +// 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). +// If no redirect_uri is present, the user-agent will be redirected to the error page. +func (r Wrapper) sendAndHandleDirectPost(ctx context.Context, vp vc.VerifiablePresentation, presentationSubmission pe.PresentationSubmission, verifierResponseURI string, clientRedirectURI string) (HandleAuthorizeRequestResponseObject, error) { + redirectURI, err := r.auth.Holder().PostAuthorizationResponse(ctx, vp, presentationSubmission, verifierResponseURI) + if err == nil { + return HandleAuthorizeRequest302Response{ + HandleAuthorizeRequest302ResponseHeaders{ + Location: redirectURI, + }, + }, nil + } + + msg := fmt.Sprintf("failed to post authorization response to verifier @ %s", verifierResponseURI) + log.Logger().WithError(err).Error(msg) + + // clientRedirectURI has been checked earlier in te process. + redirectURI, _ = url.Parse(clientRedirectURI) + clientRedirectURL := httpNuts.AddQueryParams(*redirectURI, map[string]string{ + oauth.ErrorParam: string(oauth.ServerError), + oauth.ErrorDescriptionParam: msg, + }) + return HandleAuthorizeRequest302Response{ + HandleAuthorizeRequest302ResponseHeaders{ + Location: clientRedirectURL.String(), + }, + }, nil +} + // sendAndHandleDirectPostError sends errors from handleAuthorizeRequestFromVerifier as direct_post to the verifier. The verifier responds with a redirect to the client (including error fields). -// This methods can never return an error, if an error occurs it will be logged and the request will be redirected to the client with an error. // If the direct post fails, the user-agent will be redirected back to the client with an error. (Original redirect_uri). // If no redirect_uri is present, the user-agent will be redirected to the error page. func (r Wrapper) sendAndHandleDirectPostError(ctx context.Context, auth2Error oauth.OAuth2Error, verifierResponseURI string) (HandleAuthorizeRequestResponseObject, error) { diff --git a/auth/client/iam/client.go b/auth/client/iam/client.go index 7f9a24273e..a37d2aad41 100644 --- a/auth/client/iam/client.go +++ b/auth/client/iam/client.go @@ -250,3 +250,33 @@ func (hb HTTPClient) PostError(ctx context.Context, auth2Error oauth.OAuth2Error } return redirect.RedirectURI, nil } + +func (hb HTTPClient) PostAuthorizationResponse(ctx context.Context, vp vc.VerifiablePresentation, presentationSubmission pe.PresentationSubmission, verifierResponseURI string) (string, error) { + // initiate http client, create a POST request with x-www-form-urlencoded body and send it to the redirect URL + data := url.Values{} + data.Set(oauth.VpTokenParam, vp.Raw()) + data.Set(oauth.PresentationSubmissionParam, string(presentationSubmission)) + request, err := http.NewRequestWithContext(ctx, http.MethodPost, verifierResponseURI, strings.NewReader(data.Encode())) + request.Header.Add("Accept", "application/json") + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + if err != nil { + return "", err + } + response, err := hb.httpClient.Do(request.WithContext(ctx)) + if err != nil { + return "", err + } + if err = core.TestResponseCode(http.StatusOK, response); err != nil { + return "", err + } + // take the redirectURL from the response body and return it + var responseData []byte + if responseData, err = io.ReadAll(response.Body); err != nil { + return "", fmt.Errorf("unable to read response: %w", err) + } + var redirect oauth.Redirect + if err = json.Unmarshal(responseData, &redirect); err != nil { + return "", fmt.Errorf("unable to unmarshal response: %w, %s", err, string(responseData)) + } + return redirect.RedirectURI, nil +} diff --git a/auth/oauth/types.go b/auth/oauth/types.go index ca9ae655ed..a8ab2d32d0 100644 --- a/auth/oauth/types.go +++ b/auth/oauth/types.go @@ -50,6 +50,8 @@ const ( ScopeParam = "scope" // PresentationSubmissionParam is the parameter name for the presentation_submission parameter PresentationSubmissionParam = "presentation_submission" + // VpTokenParam is the parameter name for the vp_token parameter + VpTokenParam = "vp_token" // VpTokenGrantType is the grant_type for the vp_token-bearer grant type VpTokenGrantType = "vp_token-bearer" ) diff --git a/auth/services/oauth/holder.go b/auth/services/oauth/holder.go index 08fc071d1d..77dd3191cb 100644 --- a/auth/services/oauth/holder.go +++ b/auth/services/oauth/holder.go @@ -109,5 +109,16 @@ func (v *HolderService) PostError(ctx context.Context, auth2Error oauth.OAuth2Er return redirectURL, nil } - return "", fmt.Errorf("failed to post error to verifier: %s", err.Error()) + return "", fmt.Errorf("failed to post error to verifier: %w", err) +} + +func (v *HolderService) PostAuthorizationResponse(ctx context.Context, vp vc.VerifiablePresentation, presentationSubmission pe.PresentationSubmission, verifierResponseURI string) (string, error) { + iamClient := iam.NewHTTPClient(v.strictMode, v.httpClientTimeout, v.httpClientTLS) + + redirectURL, err := iamClient.PostAuthorizationResponse(ctx, vp, presentationSubmission, verifierResponseURI) + if err == nil { + return redirectURL, nil + } + + return "", fmt.Errorf("failed to post authorization response to verifier: %w", err) } diff --git a/auth/services/oauth/interface.go b/auth/services/oauth/interface.go index 64cecbff91..072bd32eaa 100644 --- a/auth/services/oauth/interface.go +++ b/auth/services/oauth/interface.go @@ -72,4 +72,6 @@ type Holder interface { ClientMetadata(ctx context.Context, endpoint string) (*oauth.AuthorizationServerMetadata, error) // PostError posts an error to the verifier. If it fails, an error is returned. PostError(ctx context.Context, auth2Error oauth.OAuth2Error, verifierResponseURI string) (string, error) + // PostAuthorizationResponse posts the authorization response to the verifier. If it fails, an error is returned. + PostAuthorizationResponse(ctx context.Context, vp vc.VerifiablePresentation, presentationSubmission pe.PresentationSubmission, verifierResponseURI string) (string, error) }