Skip to content

Commit

Permalink
test(sdk): update integration tests
Browse files Browse the repository at this point in the history
Signed-off-by: Andrii Holovko <[email protected]>
  • Loading branch information
aholovko committed Feb 20, 2024
1 parent 8b165bb commit 616166d
Show file tree
Hide file tree
Showing 16 changed files with 297 additions and 74 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export TERM := xterm-256color

ANDROID_EMULATOR_NAME ?= WalletSDKDeviceEmulator

VCS_COMMIT ?= 977063efde1950edc11e14bfe37f370f540c76a7
VCS_COMMIT ?= 85f957d9f6554679b3fff86828d4e97ae556d05d

.PHONY: all
all: checks unit-test integration-test
Expand Down Expand Up @@ -106,7 +106,7 @@ vc-rest-docker:
--build-arg ALPINE_VER=$(ALPINE_VER) .

.PHONY: integration-test
integration-test: mock-login-consent-docker mock-trust-registry-docker generate-test-keys
integration-test: mock-login-consent-docker mock-trust-registry-docker vc-rest-docker generate-test-keys
@cd test/integration && go mod tidy && ENABLE_COMPOSITION=true go test -count=1 -v -cover . -p 1 -timeout=10m -race

.PHONY: build-integration-cli
Expand Down
2 changes: 1 addition & 1 deletion pkg/credentialschema/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,6 @@ type ResolvedClaim struct {

// Logo represents display information for a logo.
type Logo struct {
URL string `json:"url,omitempty"`
URL string `json:"uri,omitempty"`
AltText string `json:"alt_text,omitempty"`
}
2 changes: 1 addition & 1 deletion pkg/models/issuer/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func (c *Claim) OrderAsInt() (int, error) {

// Logo represents display information for a logo.
type Logo struct {
URL string `json:"url,omitempty"`
URL string `json:"uri,omitempty"`
AltText string `json:"alt_text,omitempty"`
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/openid4ci/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ package openid4ci
const (
ErrorModule = "OCI"
InvalidIssuanceURIError = "INVALID_ISSUANCE_URI"
InvalidCredentialOfferError = "INVALID_CREDENTIAL_OFFER" //nolint:gosec //false positive
InvalidCredentialOfferError = "INVALID_CREDENTIAL_OFFER" //nolint:gosec //false positive
InvalidCredentialConfigurationIDError = "INVALID_CREDENTIAL_CONFIGURATION_ID" //nolint:gosec //false positive
UnsupportedCredentialTypeInOfferError = "UNSUPPORTED_CREDENTIAL_TYPE_IN_OFFER"
IssuerOpenIDConfigFetchFailedError = "ISSUER_OPENID_CONFIG_FETCH_FAILED"
MetadataFetchFailedError = "METADATA_FETCH_FAILED"
Expand Down Expand Up @@ -59,4 +60,5 @@ const (
UnsupportedIssuanceURISchemeCode = 19
NoTokenEndpointAvailableErrorCode = 20
AcknowledgmentExpiredErrorCode = 21
InvalidCredentialConfigurationIDCode = 22
)
4 changes: 2 additions & 2 deletions pkg/openid4ci/interaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (i *interaction) instantiateCodeVerifier() error {
func (i *interaction) generateAuthorizationDetails(format string, types []string) ([]byte, error) {
// TODO: Add support for requesting multiple credentials at once (by sending an array).
// Currently we always use the first credential type specified in the offer.
authorizationDetailsDTO := &authorizationDetails{
authorizationDetailsDTO := authorizationDetails{
CredentialConfigurationID: "",
CredentialDefinition: &issuer.CredentialDefinition{
Context: nil,
Expand All @@ -169,7 +169,7 @@ func (i *interaction) generateAuthorizationDetails(format string, types []string
authorizationDetailsDTO.Locations = []string{i.issuerMetadata.CredentialIssuer}
}

authorizationDetailsBytes, err := json.Marshal(authorizationDetailsDTO)
authorizationDetailsBytes, err := json.Marshal([]authorizationDetails{authorizationDetailsDTO})
if err != nil {
return nil, err
}
Expand Down
88 changes: 57 additions & 31 deletions pkg/openid4ci/issuerinitiatedinteraction.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ type IssuerInitiatedInteraction struct {

// NewIssuerInitiatedInteraction creates a new OpenID4CI IssuerInitiatedInteraction.
// If no ActivityLogger is provided (via the ClientConfig object), then no activity logging will take place.
func NewIssuerInitiatedInteraction(initiateIssuanceURI string,
func NewIssuerInitiatedInteraction(
initiateIssuanceURI string,
config *ClientConfig,
) (*IssuerInitiatedInteraction, error) {
timeStartNewInteraction := time.Now()
Expand All @@ -87,6 +88,21 @@ func NewIssuerInitiatedInteraction(initiateIssuanceURI string,
return nil, err
}

issuerInteraction := &interaction{
issuerURI: credentialOffer.CredentialIssuer,
didResolver: config.DIDResolver,
activityLogger: config.ActivityLogger,
metricsLogger: config.MetricsLogger,
disableVCProofChecks: config.DisableVCProofChecks,
documentLoader: config.DocumentLoader,
httpClient: config.HTTPClient,
}

err = issuerInteraction.populateIssuerMetadata(getIssuerMetadataEventText)
if err != nil {
return nil, err
}

// TODO https://github.com/trustbloc/wallet-sdk/issues/457 Add support for determining
// grant types when no grants are specified.
// See https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-11.html#section-4.1.1 for more info.
Expand All @@ -95,29 +111,25 @@ func NewIssuerInitiatedInteraction(initiateIssuanceURI string,
return nil, err
}

credentialTypes, credentialFormats, err := determineCredentialTypesAndFormats(credentialOffer)
credentialTypes, credentialFormats, err := determineCredentialTypesAndFormats(credentialOffer,
issuerInteraction.issuerMetadata)
if err != nil {
return nil, err
}

return &IssuerInitiatedInteraction{
interaction: &interaction{
issuerURI: credentialOffer.CredentialIssuer,
didResolver: config.DIDResolver,
activityLogger: config.ActivityLogger,
metricsLogger: config.MetricsLogger,
disableVCProofChecks: config.DisableVCProofChecks,
documentLoader: config.DocumentLoader,
httpClient: config.HTTPClient,
},
interaction: issuerInteraction,
preAuthorizedCodeGrantParams: preAuthorizedCodeGrantParams,
authorizationCodeGrantParams: authorizationCodeGrantParams,
credentialTypes: credentialTypes,
credentialFormats: credentialFormats,
}, config.MetricsLogger.Log(&api.MetricsEvent{
Event: newInteractionEventText,
Duration: time.Since(timeStartNewInteraction),
})
},
config.MetricsLogger.Log(
&api.MetricsEvent{
Event: newInteractionEventText,
Duration: time.Since(timeStartNewInteraction),
},
)
}

// CreateAuthorizationURL creates an authorization URL that can be opened in a browser to proceed to the login page.
Expand Down Expand Up @@ -565,30 +577,45 @@ func getCredentialOfferJSONFromCredentialOfferURI(credentialOfferURI string,
return responseBytes, nil
}

func determineCredentialTypesAndFormats(credentialOffer *CredentialOffer) ([][]string, []string, error) {
// TODO Add support for credential offer objects that contain a credentials field with JSON strings instead.
// See https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-11.html#section-4.1.1 for more info.
credentialTypes := make([][]string, len(credentialOffer.Credentials))
credentialFormats := make([]string, len(credentialOffer.Credentials))
func determineCredentialTypesAndFormats(
credentialOffer *CredentialOffer,
issuerMetadata *issuer.Metadata,
) ([][]string, []string, error) {
types := make([][]string, len(credentialOffer.CredentialConfigurationIDs))
formats := make([]string, len(credentialOffer.CredentialConfigurationIDs))

for i := 0; i < len(credentialOffer.CredentialConfigurationIDs); i++ {
id := credentialOffer.CredentialConfigurationIDs[i]

configuration, ok := issuerMetadata.CredentialConfigurationsSupported[id]
if !ok {
return nil, nil, walleterror.NewValidationError(
ErrorModule,
InvalidCredentialConfigurationIDCode,
InvalidCredentialConfigurationIDError,
fmt.Errorf("invalid credential configuration ID (%s) in credential offer", id),
)
}

types[i] = configuration.CredentialDefinition.Type

for i := 0; i < len(credentialOffer.Credentials); i++ {
if credentialOffer.Credentials[i].Format != jwtVCJSONCredentialFormat &&
credentialOffer.Credentials[i].Format != jwtVCJSONLDCredentialFormat &&
credentialOffer.Credentials[i].Format != ldpVCCredentialFormat {
if configuration.Format != jwtVCJSONCredentialFormat &&
configuration.Format != jwtVCJSONLDCredentialFormat &&
configuration.Format != ldpVCCredentialFormat {
return nil, nil, walleterror.NewValidationError(
ErrorModule,
UnsupportedCredentialTypeInOfferCode,
UnsupportedCredentialTypeInOfferError,
fmt.Errorf("unsupported credential type (%s) in credential offer at index %d of "+
"credentials object (must be jwt_vc_json or jwt_vc_json-ld)",
credentialOffer.Credentials[i].Format, i))
"credential_configurations_supported (must be jwt_vc_json or jwt_vc_json-ld)",
configuration.Format, i),
)
}

credentialTypes[i] = credentialOffer.Credentials[i].Types
credentialFormats[i] = credentialOffer.Credentials[i].Format
formats[i] = configuration.Format
}

return credentialTypes, credentialFormats, nil
return types, formats, nil
}

func validateSignerKeyID(jwtSigner api.JWTSigner) error {
Expand Down Expand Up @@ -620,8 +647,7 @@ func getSubjectIDs(vcs []*verifiable.Credential) []string {

func signToken(claims interface{}, signer api.JWTSigner) (string, error) {
headers := jose.Headers{}
// TODO: Send "typ" header.
// headers["typ"] = "openid4vci-proof+jwt"
headers["typ"] = "openid4vci-proof+jwt"

token, err := jwt.NewSigned(claims, jwt.SignParameters{AdditionalHeaders: headers}, signer)
if err != nil {
Expand Down
34 changes: 27 additions & 7 deletions pkg/openid4ci/issuerinitiatedinteraction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ SPDX-License-Identifier: Apache-2.0
package openid4ci_test

import (
"bytes"
_ "embed"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
Expand Down Expand Up @@ -51,6 +53,9 @@ var (

//go:embed testdata/sample_signed_issuer_metadata.jwt
sampleSignedIssuerMetadata string

//go:embed testdata/sample_issuer_metadata.json
sampleIssuerMetadata string
)

type mockIssuerServerHandler struct {
Expand Down Expand Up @@ -190,8 +195,6 @@ func TestNewIssuerInitiatedInteraction(t *testing.T) {
t.Run("Credential format is jwt_vc_json-ld", func(t *testing.T) {
credentialOffer := createSampleCredentialOffer(t, true, true)

credentialOffer.Credentials[0].Format = "jwt_vc_json-ld"

credentialOfferBytes, err := json.Marshal(credentialOffer)
require.NoError(t, err)

Expand Down Expand Up @@ -287,10 +290,10 @@ func TestNewIssuerInitiatedInteraction(t *testing.T) {
require.EqualError(t, err, "no supported grant types found")
require.Nil(t, interaction)
})
t.Run("Unsupported credential type", func(t *testing.T) {
t.Run("Invalid credential configuration id", func(t *testing.T) {
credentialOffer := createSampleCredentialOffer(t, false, true)

credentialOffer.Credentials[0].Format = "UnsupportedType"
credentialOffer.CredentialConfigurationIDs = []string{"invalid_configuration_id"}

credentialOfferBytes, err := json.Marshal(credentialOffer)
require.NoError(t, err)
Expand All @@ -300,9 +303,8 @@ func TestNewIssuerInitiatedInteraction(t *testing.T) {
credentialOfferIssuanceURI := "openid-credential-offer://?credential_offer=" + credentialOfferEscaped

interaction, err := openid4ci.NewIssuerInitiatedInteraction(credentialOfferIssuanceURI, getTestClientConfig(t))
require.EqualError(t, err, "UNSUPPORTED_CREDENTIAL_TYPE_IN_OFFER(OCI0-0002):unsupported "+
"credential type (UnsupportedType) in credential offer at index 0 of credentials object "+
"(must be jwt_vc_json or jwt_vc_json-ld)")
require.EqualError(t, err, "INVALID_CREDENTIAL_CONFIGURATION_ID(OCI0-0022):invalid credential configuration "+
"ID (invalid_configuration_id) in credential offer")
require.Nil(t, interaction)
})
t.Run("Fail to log retrieving credential offer via HTTP GET metrics event", func(t *testing.T) {
Expand Down Expand Up @@ -1964,9 +1966,27 @@ func getTestClientConfig(t *testing.T) *openid4ci.ClientConfig {
DIDResolver: didResolver,
DisableVCProofChecks: true,
NetworkDocumentLoaderHTTPTimeout: &networkDocumentLoaderHTTPTimeout,
HTTPClient: &http.Client{
Transport: &mockTransport{
roundTripFunc: func(req *http.Request) (*http.Response, error) {
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewBufferString(sampleIssuerMetadata)),
}, nil
},
},
},
}
}

type mockTransport struct {
roundTripFunc func(req *http.Request) (*http.Response, error)
}

func (m *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return m.roundTripFunc(req)
}

// makeMockDoc creates a key in the given KMS and returns a mock DID Doc with a verification method.
func makeMockDoc(keyWriter api.KeyWriter) (*did.Doc, error) {
_, pkJWK, err := keyWriter.Create(arieskms.ED25519Type)
Expand Down
14 changes: 4 additions & 10 deletions pkg/openid4ci/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,11 @@ import (
)

// CredentialOffer represents the Credential Offer object as defined in
// https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-11.html#section-4.1.1.
// https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-wg-draft.html#section-4.1.1.
type CredentialOffer struct {
CredentialIssuer string `json:"credential_issuer,omitempty"`
Credentials []Credentials `json:"credentials,omitempty"`
Grants map[string]map[string]interface{} `json:"grants,omitempty"`
}

// Credentials represents the credential format and types in a Credential Offer.
type Credentials struct {
Format string `json:"format,omitempty"`
Types []string `json:"types,omitempty"`
CredentialIssuer string `json:"credential_issuer,omitempty"`
CredentialConfigurationIDs []string `json:"credential_configuration_ids"`
Grants map[string]map[string]interface{} `json:"grants,omitempty"`
}

// AuthorizeResult is the object returned from the Client.Authorize method.
Expand Down
10 changes: 2 additions & 8 deletions pkg/openid4ci/testdata/sample_credential_offer.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
{
"credential_issuer":"example.com",
"credentials":[
{
"format":"jwt_vc_json",
"types":[
"VerifiableCredential",
"VerifiedEmployee"
]
}
"credential_configuration_ids": [
"credential_configuration_id_1"
],
"grants":{
"urn:ietf:params:oauth:grant-type:pre-authorized_code":{
Expand Down
Loading

0 comments on commit 616166d

Please sign in to comment.