diff --git a/auth/api/iam/error.go b/auth/api/iam/error.go index a70c005052..ca7bc5cca7 100644 --- a/auth/api/iam/error.go +++ b/auth/api/iam/error.go @@ -39,6 +39,8 @@ const ( UnsupportedGrantType ErrorCode = "unsupported_grant_type" // UnsupportedResponseType is returned when the authorization server does not support obtaining an authorization code using this method. UnsupportedResponseType ErrorCode = "unsupported_response_type" + // InvalidScope is returned when the requested scope is invalid, unknown, or malformed. + InvalidScope ErrorCode = "invalid_scope" // ServerError is returned when the Authorization Server encounters an unexpected condition that prevents it from fulfilling the request. ServerError ErrorCode = "server_error" ) diff --git a/auth/api/iam/s2s_vptoken.go b/auth/api/iam/s2s_vptoken.go index a3124561ad..d824fabd02 100644 --- a/auth/api/iam/s2s_vptoken.go +++ b/auth/api/iam/s2s_vptoken.go @@ -54,8 +54,10 @@ func (s Wrapper) handleS2SAccessTokenRequest(ctx context.Context, issuer did.DID scope := params[scopeParam] assertionEncoded := params["assertion"] if submissionEncoded == "" || scope == "" || assertionEncoded == "" { - // TODO: OAuth2 error - return nil, errors.New("missing required parameters") + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "missing required parameters", + } } // Unmarshal VP, which can be in URL-encoded JSON or JWT format @@ -66,34 +68,49 @@ func (s Wrapper) handleS2SAccessTokenRequest(ctx context.Context, issuer did.DID // (VP as URL-encoded JSON) assertionDecoded, err := url.QueryUnescape(assertionEncoded) if err != nil { - // TODO: OAuth2 error - return nil, fmt.Errorf("assertion parameter is invalid: %w", err) + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "assertion parameter is invalid: invalid query escaping", + InternalError: err, + } } var vp vc.VerifiablePresentation err = json.Unmarshal([]byte(assertionDecoded), &vp) if err != nil { - // TODO: OAuth2 error - return nil, fmt.Errorf("assertion parameter is invalid: %w", err) + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "assertion parameter is invalid: invalid JSON", + InternalError: err, + } } // Unmarshal presentation submission var submission pe.PresentationSubmission submissionDecoded, err := url.QueryUnescape(submissionEncoded) if err != nil { - // TODO: OAuth2 error - return nil, fmt.Errorf("presentation_submission parameter is invalid: %w", err) + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "presentation_submission parameter is invalid: invalid query escaping", + InternalError: err, + } } if err = json.Unmarshal([]byte(submissionDecoded), &submission); err != nil { - // TODO: OAuth2 error - return nil, fmt.Errorf("presentation_submission parameter is invalid: %w", err) + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "presentation_submission parameter is invalid: invalid JSON", + InternalError: err, + } } // Check signatures and VC issuer trust issueTime := time.Now() _, err = s.vcr.Verifier().VerifyVP(vp, true, false, &issueTime) if err != nil { - // TODO: OAuth2 error - return nil, fmt.Errorf("verifiable presentation verification failed: %w", err) + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "verifiable presentation is invalid", + InternalError: err, + } } // Validate the presentation submission: @@ -103,7 +120,10 @@ func (s Wrapper) handleS2SAccessTokenRequest(ctx context.Context, issuer did.DID // This actually creates a new submission. presentationDefinition := s.auth.PresentationDefinitions().ByScope(scope) if presentationDefinition == nil { - return nil, fmt.Errorf("unsupported scope for presentation exchange: %s", scope) + return nil, OAuth2Error{ + Code: InvalidScope, + Description: fmt.Sprintf("unsupported scope for presentation exchange: %s", scope), + } } submissionCredentials, err := submission.Credentials(vp) if err != nil { @@ -112,12 +132,19 @@ func (s Wrapper) handleS2SAccessTokenRequest(ctx context.Context, issuer did.DID // Match the credentials from the presentation submission with the presentation definition we got through the scope _, matchedCredentials, err := presentationDefinition.Match(submissionCredentials) if err != nil { - return nil, fmt.Errorf("presentation_submission parameter is invalid: re-evaluation of the credentials against the presentation definition fails: %w", err) + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "presentation_submission parameter is invalid: re-evaluation of the credentials against the presentation definition fails", + InternalError: err, + } } submissionCredentialsJSON, _ := json.Marshal(submissionCredentials) matchedCredentialsJSON, _ := json.Marshal(matchedCredentials) if !bytes.Equal(submissionCredentialsJSON, matchedCredentialsJSON) { - return nil, errors.New("presentation_submission parameter is invalid: re-evaluation of the credentials against the presentation definition yields different credentials (bug or attempted hack)") + return nil, OAuth2Error{ + Code: InvalidRequest, + Description: "presentation_submission parameter is invalid: re-evaluation of the credentials against the presentation definition yields different credentials (bug or attempted hack)", + } } // TODO: Check that the returned credentials fulfill the presentation definition of the scope.