Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
chore: Integration test for structured claim option (#3488)
Browse files Browse the repository at this point in the history
Added integration test for structured claim option.

Closes #3487

Signed-off-by: Sandra Vrtikapa <[email protected]>

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask authored Jan 20, 2023
1 parent 25c3bd6 commit 1c4baf4
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 9 deletions.
185 changes: 177 additions & 8 deletions pkg/doc/sdjwt/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ SPDX-License-Identifier: Apache-2.0
package sdjwt

import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"encoding/json"
"fmt"
"testing"
"time"
Expand Down Expand Up @@ -43,11 +45,17 @@ func TestSDJWTFlow(t *testing.T) {
"last_name": "Smith",
}

t.Run("success", func(t *testing.T) {
t.Run("success - simple claims (flat option)", func(t *testing.T) {
// Issuer will issue SD-JWT for specified claims.
token, err := issuer.New(testIssuer, claims, nil, signer)
r.NoError(err)

var simpleClaimsFlatOption map[string]interface{}
err = token.DecodeClaims(&simpleClaimsFlatOption)
r.NoError(err)

printObject(t, "Simple Claims:", simpleClaimsFlatOption)

// TODO: Should we have one call instead of two (designed based on JWT)
combinedFormatForIssuance, err := token.Serialize(false)
r.NoError(err)
Expand All @@ -62,9 +70,10 @@ func TestSDJWTFlow(t *testing.T) {
// expected disclosures given_name and last_name
r.Equal(2, len(claims))

selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name"}, claims)

// Holder will disclose only sub-set of claims to verifier.
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance,
[]string{claims[0].Disclosure})
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures)
r.NoError(err)

fmt.Println(fmt.Sprintf("holder SD-JWT: %s", combinedFormatForPresentation))
Expand All @@ -73,11 +82,12 @@ func TestSDJWTFlow(t *testing.T) {
verifiedClaims, err := verifier.Parse(combinedFormatForPresentation,
verifier.WithSignatureVerifier(signatureVerifier))
r.NoError(err)
r.NotNil(verifiedClaims)

printObject(t, "Verified Claims", verifiedClaims)

// expected claims iss, exp, iat, nbf, given_name; last_name was not disclosed
r.Equal(5, len(verifiedClaims))

fmt.Println(fmt.Sprintf("verified claims: %+v", verifiedClaims))
})

t.Run("success - with holder binding", func(t *testing.T) {
Expand Down Expand Up @@ -110,9 +120,10 @@ func TestSDJWTFlow(t *testing.T) {
const testAudience = "https://test.com/verifier"
const testNonce = "nonce"

selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name"}, claims)

// Holder will disclose only sub-set of claims to verifier and add holder binding.
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance,
[]string{claims[0].Disclosure},
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures,
holder.WithHolderBinding(&holder.BindingInfo{
Payload: holder.BindingPayload{
Nonce: testNonce,
Expand All @@ -133,9 +144,167 @@ func TestSDJWTFlow(t *testing.T) {
verifier.WithExpectedNonceForHolderBinding(testNonce))
r.NoError(err)

fmt.Println(fmt.Sprintf("verified claims: %+v", verifiedClaims))
printObject(t, "Verified Claims", verifiedClaims)

// expected claims cnf, iss, exp, iat, nbf, given_name; last_name was not disclosed
r.Equal(6, len(verifiedClaims))
})

t.Run("success - complex claims object with structured claims option", func(t *testing.T) {
complexClaims := createComplexClaims()

// Issuer will issue SD-JWT for specified claims. We will use structured(nested) claims in this test.
token, err := issuer.New(testIssuer, complexClaims, nil, signer,
issuer.WithStructuredClaims(true))
r.NoError(err)

var structuredClaims map[string]interface{}
err = token.DecodeClaims(&structuredClaims)
r.NoError(err)

printObject(t, "Complex Claims(Structured Option) :", structuredClaims)

combinedFormatForIssuance, err := token.Serialize(false)
r.NoError(err)

fmt.Println(fmt.Sprintf("issuer SD-JWT: %s", combinedFormatForIssuance))

// Holder will parse combined format for issuance and hold on to that
// combined format for issuance and the claims that can be selected.
claims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier))
r.NoError(err)

printObject(t, "Holder Claims", claims)

r.Equal(10, len(claims))

selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name", "email", "street_address"}, claims)

// Holder will disclose only sub-set of claims to verifier.
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures)
r.NoError(err)

fmt.Println(fmt.Sprintf("holder SD-JWT: %s", combinedFormatForPresentation))

// Verifier will validate combined format for presentation and create verified claims.
verifiedClaims, err := verifier.Parse(combinedFormatForPresentation,
verifier.WithSignatureVerifier(signatureVerifier))
r.NoError(err)

// expected claims iss, exp, iat, nbf, given_name, email, street_address
r.Equal(7, len(verifiedClaims))

printObject(t, "Verified Claims", verifiedClaims)
})

t.Run("success - complex claims object with flat claims option", func(t *testing.T) {
complexClaims := createComplexClaims()

// Issuer will issue SD-JWT for specified claims. We will use structured(nested) claims in this test.
token, err := issuer.New(testIssuer, complexClaims, nil, signer)
r.NoError(err)

var flatClaims map[string]interface{}
err = token.DecodeClaims(&flatClaims)
r.NoError(err)

printObject(t, "Complex Claims (Flat Option)", flatClaims)

combinedFormatForIssuance, err := token.Serialize(false)
r.NoError(err)

fmt.Println(fmt.Sprintf("issuer SD-JWT: %s", combinedFormatForIssuance))

// Holder will parse combined format for issuance and hold on to that
// combined format for issuance and the claims that can be selected.
claims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier))
r.NoError(err)

printObject(t, "Holder Claims", claims)

r.Equal(7, len(claims))

selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name", "email", "address"}, claims)

// Holder will disclose only sub-set of claims to verifier.
combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures)
r.NoError(err)

fmt.Println(fmt.Sprintf("holder SD-JWT: %s", combinedFormatForPresentation))

// Verifier will validate combined format for presentation and create verified claims.
verifiedClaims, err := verifier.Parse(combinedFormatForPresentation,
verifier.WithSignatureVerifier(signatureVerifier))
r.NoError(err)

// expected claims iss, exp, iat, nbf, given_name, email, street_address
r.Equal(7, len(verifiedClaims))

printObject(t, "Verified Claims", verifiedClaims)
})
}

func createComplexClaims() map[string]interface{} {
claims := map[string]interface{}{
"sub": "john_doe_42",
"given_name": "John",
"family_name": "Doe",
"email": "[email protected]",
"phone_number": "+1-202-555-0101",
"birthdate": "1940-01-01",
"address": map[string]interface{}{
"street_address": "123 Main St",
"locality": "Anytown",
"region": "Anystate",
"country": "US",
},
}

return claims
}

func getDisclosuresFromClaimNames(selectedClaimNames []string, claims []*holder.Claim) []string {
var disclosures []string

for _, c := range claims {
if contains(selectedClaimNames, c.Name) {
disclosures = append(disclosures, c.Disclosure)
}
}

return disclosures
}

func contains(values []string, val string) bool {
for _, v := range values {
if v == val {
return true
}
}

return false
}

func printObject(t *testing.T, name string, obj interface{}) {
t.Helper()

objBytes, err := json.Marshal(obj)
require.NoError(t, err)

prettyJSON, err := prettyPrint(objBytes)
require.NoError(t, err)

fmt.Println(name + ":")
fmt.Println(prettyJSON)
}

func prettyPrint(msg []byte) (string, error) {
var prettyJSON bytes.Buffer

err := json.Indent(&prettyJSON, msg, "", "\t")
if err != nil {
return "", err
}

return prettyJSON.String(), nil
}
2 changes: 1 addition & 1 deletion pkg/doc/sdjwt/verifier/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func TestParse(t *testing.T) {
require.Equal(t, 5, len(claims))
})

t.Run("success - VCS sample", func(t *testing.T) {
t.Run("success - VC sample", func(t *testing.T) {
token, err := afjwt.Parse(vcSDJWT, afjwt.WithSignatureVerifier(&holder.NoopSignatureVerifier{}))
r.NoError(err)

Expand Down

0 comments on commit 1c4baf4

Please sign in to comment.