Skip to content

Commit

Permalink
feat(sdk): pass server error code to consumer (#866)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrii Holovko <[email protected]>
  • Loading branch information
aholovko authored Feb 4, 2025
1 parent 8357843 commit 936b08f
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (i *IssuerInitiatedInteraction) RequestCredentialWithPreAuth(
) (*verifiable.CredentialsArray, error) {
credentials, _, err := i.requestCredentialWithPreAuth(vm, opts)
if err != nil {
return nil, wrapper.ToMobileErrorWithTrace(err, i.oTel)
return nil, err
}

return toGomobileCredentials(credentials), nil
Expand All @@ -120,7 +120,7 @@ func (i *IssuerInitiatedInteraction) RequestCredentialWithPreAuthV2(
) (*verifiable.CredentialsArrayV2, error) {
credentials, configIDs, err := i.requestCredentialWithPreAuth(vm, opts)
if err != nil {
return nil, wrapper.ToMobileErrorWithTrace(err, i.oTel)
return nil, err
}

return toGomobileCredentialsV2(credentials, configIDs), nil
Expand Down
4 changes: 4 additions & 0 deletions cmd/wallet-sdk-gomobile/walleterror/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ type Error struct {
Details string `json:"details"`
// ID of Open Telemetry root trace. Can be used to trace API calls. Only present in certain errors.
TraceID string `json:"trace_id"`
// Server error code.
ServerCode string `json:"server_code,omitempty"`
// Server error message.
ServerMessage string `json:"server_message,omitempty"`
}

// Parse used to parse exception message on mobile side.
Expand Down
12 changes: 7 additions & 5 deletions cmd/wallet-sdk-gomobile/wrapper/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,13 @@ func convertToGomobileError(err error, trace *otel.Trace) *walleterror.Error {
mergedErrorMessage := higherLevelErrorMessage + walletError.ParentError

return &walleterror.Error{
Code: walletError.Code,
Category: walletError.Category,
Message: walletError.Message,
Details: mergedErrorMessage,
TraceID: traceID,
Code: walletError.Code,
Category: walletError.Category,
Message: walletError.Message,
Details: mergedErrorMessage,
TraceID: traceID,
ServerCode: walletError.ServerCode,
ServerMessage: walletError.ServerMessage,
}
}

Expand Down
48 changes: 34 additions & 14 deletions pkg/openid4ci/interaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,61 +543,81 @@ func (i *interaction) getVCsFromCredentialResponses(
return vcs, nil
}

//nolint:funlen
func processCredentialErrorResponse(statusCode int, respBytes []byte) error {
detailedErr := fmt.Errorf("received status code [%d] with body [%s] from issuer's credential endpoint",
statusCode, string(respBytes))

var errorResponse errorResponse
var errResponse errorResponse

err := json.Unmarshal(respBytes, &errorResponse)
err := json.Unmarshal(respBytes, &errResponse)
if err != nil {
return walleterror.NewExecutionError(ErrorModule,
OtherCredentialRequestErrorCode,
OtherCredentialRequestError,
detailedErr)
}

switch errorResponse.Error {
switch errResponse.Error {
case "invalid_request":
return walleterror.NewExecutionError(ErrorModule,
InvalidCredentialRequestErrorCode,
InvalidCredentialRequestError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "invalid_token":
return walleterror.NewExecutionError(ErrorModule,
InvalidTokenErrorCode,
InvalidTokenError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "unsupported_credential_format":
return walleterror.NewExecutionError(ErrorModule,
UnsupportedCredentialFormatErrorCode,
UnsupportedCredentialFormatError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "unsupported_credential_type":
return walleterror.NewExecutionError(ErrorModule,
UnsupportedCredentialTypeErrorCode,
UnsupportedCredentialTypeError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "invalid_or_missing_proof":
return walleterror.NewExecutionError(ErrorModule,
InvalidOrMissingProofErrorCode,
InvalidOrMissingProofError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "expired_ack_id":
return walleterror.NewExecutionError(ErrorModule,
AcknowledgmentExpiredErrorCode,
AcknowledgmentExpiredError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "invalid_proof":
return NewInvalidProofError(walleterror.NewExecutionError(ErrorModule,
AcknowledgmentExpiredErrorCode,
AcknowledgmentExpiredError,
detailedErr), errorResponse.CNonce, errorResponse.CNonceExpiresIn)
return NewInvalidProofError(
walleterror.NewExecutionError(ErrorModule,
AcknowledgmentExpiredErrorCode,
AcknowledgmentExpiredError,
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription),
),
errResponse.CNonce, errResponse.CNonceExpiresIn)
default:
return walleterror.NewExecutionError(ErrorModule,
OtherCredentialRequestErrorCode,
OtherCredentialRequestError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
}
}

Expand Down
16 changes: 12 additions & 4 deletions pkg/openid4ci/issuerinitiatedinteraction.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,22 +625,30 @@ func tokenErrorResponseHandler(statusCode int, respBody []byte) error {
return walleterror.NewExecutionError(ErrorModule,
InvalidTokenRequestErrorCode,
InvalidTokenRequestError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "invalid_grant":
return walleterror.NewExecutionError(ErrorModule,
InvalidGrantErrorCode,
InvalidGrantError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
case "invalid_client":
return walleterror.NewExecutionError(ErrorModule,
InvalidClientErrorCode,
InvalidClientError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
default:
return walleterror.NewExecutionError(ErrorModule,
OtherTokenResponseErrorCode,
OtherTokenRequestError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
}
}

Expand Down
3 changes: 2 additions & 1 deletion pkg/openid4ci/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ type batchCredentialRequest struct {
}

type errorResponse struct {
Error string `json:"error,omitempty"`
Error string `json:"error,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
// containing a nonce to be used to create a proof of possession of key material
// when requesting a Credential.
CNonce string `json:"c_nonce"`
Expand Down
3 changes: 2 additions & 1 deletion pkg/openid4vp/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ const (
)

type errorResponse struct {
Error string `json:"error,omitempty"`
Error string `json:"error,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
}

type msEntraErrorResponse struct {
Expand Down
4 changes: 3 additions & 1 deletion pkg/openid4vp/openid4vp.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,9 @@ func processAuthorizationErrorResponse(statusCode int, respBytes []byte) error {
return walleterror.NewExecutionError(ErrorModule,
OtherAuthorizationResponseErrorCode,
OtherAuthorizationResponseError,
detailedErr)
detailedErr,
walleterror.WithServerErrorCode(errResponse.Error),
walleterror.WithServerErrorMessage(errResponse.ErrorDescription))
}
}

Expand Down
46 changes: 41 additions & 5 deletions pkg/walleterror/walleterror.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ type Error struct {
Message string
// The full underlying error.
ParentError string
// A short code provided by the server to represent the specific error.
ServerCode string
// A descriptive message from the server that explains the error in more detail.
ServerMessage string
}

// NewValidationError creates validation error.
Expand All @@ -37,12 +41,44 @@ func NewValidationError(module string, code int, category string, parentError er
}
}

type serverErrorOpts struct {
code string
message string
}

// ServerErrorOpt is a functional option used to customize server-related error fields.
type ServerErrorOpt func(opts *serverErrorOpts)

// NewExecutionError creates execution error.
func NewExecutionError(module string, code int, scenario string, cause error) *Error {
return &Error{
Code: getErrorCode(module, executionError, code),
Category: scenario,
ParentError: cause.Error(),
func NewExecutionError(module string, code int, scenario string, cause error, opts ...ServerErrorOpt) *Error {
errOpts := &serverErrorOpts{}

for _, opt := range opts {
opt(errOpts)
}

err := &Error{
Code: getErrorCode(module, executionError, code),
Category: scenario,
ParentError: cause.Error(),
ServerCode: errOpts.code,
ServerMessage: errOpts.message,
}

return err
}

// WithServerErrorCode sets the server error code.
func WithServerErrorCode(code string) ServerErrorOpt {
return func(opts *serverErrorOpts) {
opts.code = code
}
}

// WithServerErrorMessage sets the server error message.
func WithServerErrorMessage(message string) ServerErrorOpt {
return func(opts *serverErrorOpts) {
opts.message = message
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/integration/fixtures/.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# vc services
VC_REST_IMAGE=ghcr.io/trustbloc-cicd/vc-server
VC_REST_IMAGE_TAG=v1.12.1-snapshot-8d5b3bf
VC_REST_IMAGE_TAG=v1.12.2-snapshot-4a9fa1a

# Remote JSON-LD context provider
CONTEXT_PROVIDER_URL=https://file-server.trustbloc.local:10096/ld-contexts.json
Expand Down

0 comments on commit 936b08f

Please sign in to comment.