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

feat: add did document support for alsoKnownAs #3277

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions pkg/doc/did/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ func ParseDocumentResolution(data []byte) (*DocResolution, error) {
type Doc struct {
Context []string
ID string
AlsoKnownAs []string
VerificationMethod []VerificationMethod
Service []Service
Authentication []Verification
Expand Down Expand Up @@ -428,6 +429,7 @@ func NewReferencedVerification(vm *VerificationMethod, r VerificationRelationshi
type rawDoc struct {
Context interface{} `json:"@context,omitempty"`
ID string `json:"id,omitempty"`
AlsoKnownAs []interface{} `json:"alsoKnownAs,omitempty"`
VerificationMethod []map[string]interface{} `json:"verificationMethod,omitempty"`
PublicKey []map[string]interface{} `json:"publicKey,omitempty"`
Service []map[string]interface{} `json:"service,omitempty"`
Expand Down Expand Up @@ -489,9 +491,10 @@ func ParseDocument(data []byte) (*Doc, error) {
}

doc := &Doc{
ID: raw.ID,
Created: raw.Created,
Updated: raw.Updated,
ID: raw.ID,
AlsoKnownAs: stringArray(raw.AlsoKnownAs),
Created: raw.Created,
Updated: raw.Updated,
}

context, baseURI := parseContext(raw.Context)
Expand Down Expand Up @@ -1114,6 +1117,8 @@ func (doc *Doc) JSONBytes() ([]byte, error) {
context = doc.Context[0]
}

aka := populateRawAlsoKnownAs(doc.AlsoKnownAs)

vm, err := populateRawVM(context, doc.ID, doc.processingMeta.baseURI, doc.VerificationMethod)
if err != nil {
return nil, fmt.Errorf("JSON unmarshalling of Verification Method failed: %w", err)
Expand Down Expand Up @@ -1148,7 +1153,7 @@ func (doc *Doc) JSONBytes() ([]byte, error) {
}

raw := &rawDoc{
Context: doc.Context, ID: doc.ID, VerificationMethod: vm,
Context: doc.Context, ID: doc.ID, AlsoKnownAs: aka, VerificationMethod: vm,
Authentication: auths, AssertionMethod: assertionMethods, CapabilityDelegation: capabilityDelegations,
CapabilityInvocation: capabilityInvocations, KeyAgreement: keyAgreements,
Service: populateRawServices(doc.Service, doc.ID, doc.processingMeta.baseURI), Created: doc.Created,
Expand Down Expand Up @@ -1395,6 +1400,16 @@ func populateRawServices(services []Service, didID, baseURI string) []map[string
return rawServices
}

func populateRawAlsoKnownAs(aka []string) []interface{} {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add unit tests to validate aka value as per schema

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest you cover a few different IRI formats, such as:

did:example:123, https://social.example/username, urn:uuid:1231

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Baha-sk Unit tests added to doc_test.go

rawAka := make([]interface{}, len(aka))

for i, v := range aka {
rawAka[i] = v
}

return rawAka
}

func populateRawVM(context, didID, baseURI string, pks []VerificationMethod) ([]map[string]interface{}, error) {
var rawVM []map[string]interface{}

Expand Down
124 changes: 113 additions & 11 deletions pkg/doc/did/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ func TestValidWithDocBase(t *testing.T) {
// test doc id
require.Equal(t, doc.ID, "did:example:123456789abcdefghi")

// test alsoKnownAs
require.Equal(t, 1, len(doc.AlsoKnownAs))
require.Equal(t, "did:example:123", doc.AlsoKnownAs[0])

hexDecodeValue, err := hex.DecodeString("02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71")
block, _ := pem.Decode([]byte(pemPK))
require.NotNil(t, block)
Expand Down Expand Up @@ -175,6 +179,8 @@ func TestDocResolution(t *testing.T) {
require.Equal(t, 1, len(d.Context))
require.Equal(t, "https://w3id.org/did-resolution/v1", d.Context[0])
require.Equal(t, "did:example:21tDAKCERh95uGgKbJNHYp", d.DIDDocument.ID)
require.Equal(t, 1, len(d.DIDDocument.AlsoKnownAs))
require.Equal(t, "did:example:123", d.DIDDocument.AlsoKnownAs[0])
require.Equal(t, true, d.DocumentMetadata.Method.Published)
require.Equal(t, "did:ex:123333", d.DocumentMetadata.CanonicalID)

Expand All @@ -187,6 +193,8 @@ func TestDocResolution(t *testing.T) {
require.Equal(t, 1, len(d.Context))
require.Equal(t, "https://w3id.org/did-resolution/v1", d.Context[0])
require.Equal(t, "did:example:21tDAKCERh95uGgKbJNHYp", d.DIDDocument.ID)
require.Equal(t, 1, len(d.DIDDocument.AlsoKnownAs))
require.Equal(t, "did:example:123", d.DIDDocument.AlsoKnownAs[0])
require.Equal(t, true, d.DocumentMetadata.Method.Published)
require.Equal(t, "did:ex:123333", d.DocumentMetadata.CanonicalID)
})
Expand All @@ -209,6 +217,10 @@ func TestValid(t *testing.T) {
// test doc id
require.Equal(t, doc.ID, "did:example:21tDAKCERh95uGgKbJNHYp")

// test alsoKnownAs
require.Equal(t, 1, len(doc.AlsoKnownAs))
require.Equal(t, "did:example:123", doc.AlsoKnownAs[0])

hexDecodeValue, err := hex.DecodeString("02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71")
block, _ := pem.Decode([]byte(pemPK))
require.NotNil(t, block)
Expand Down Expand Up @@ -561,6 +573,96 @@ func TestValidateDidDocID(t *testing.T) {
})
}

func TestValidateDidDocAlsoKnownAs(t *testing.T) {
t.Run("test did doc with duplicate alsoKnownAs entries", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{"did:example:123", "did:example:123"}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.Error(t, err)
require.Contains(t, err.Error(), "must be unique")
}
})

t.Run("test did doc with non-uri alsoKnownAs string", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{"not.a.valid.uri"}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.Error(t, err)
require.Contains(t, err.Error(), "Does not match format 'uri'")
}
})

t.Run("test did doc with non-string alsoKnownAs entry", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{0}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.Error(t, err)
require.Contains(t, err.Error(), "Invalid type. Expected: string")
}
})

t.Run("test did doc with empty alsoKnownAs", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = nil
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.NoError(t, err)
}
})

t.Run("test did doc with zero-length alsoKnownAs", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
for _, d := range docs {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.NoError(t, err)
}
})

t.Run("test did doc with valid alsoKnownAs entries", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
valid := []interface{}{
"did:example:123",
"https://social.example/username",
"urn:uuid:1231",
}
for _, d := range docs {
for _, aka := range valid {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(d), &raw))
raw.AlsoKnownAs = []interface{}{aka}
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.NoError(t, err)
}
}
})
}

func TestValidateDidDocPublicKey(t *testing.T) {
t.Run("test did doc with empty public key", func(t *testing.T) {
docs := []string{validDoc, validDocV011}
Expand Down Expand Up @@ -1498,20 +1600,20 @@ func TestDIDSchemas(t *testing.T) {
didStr: `{
"@context": "https://w3id.org/did/v0.11",
"id": "did:w123:world",
"assertionMethod": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"assertionMethod": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"authentication": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN", "",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"authentication": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN", "",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"capabilityDelegation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"capabilityDelegation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"capabilityInvocation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"capabilityInvocation": ["did:w123:world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
"did:w123:world#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A",
"did:w123:world#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"did:w123:world#NjQ6Y_ZMj6IUK_XkgCDwtKHlNTUTVjEYOWZtxhp1n-E"],
"keyAgreement": [{
"id": "did:w123:world#zC5iai1sL93gQxn8LKh1i42fTbpfar65dVx4NYznYfG3Y5",
Expand Down
24 changes: 24 additions & 0 deletions pkg/doc/did/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ const (
"id": {
"type": "string"
},
"alsoKnownAs": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

careful with the uri format.... test cases need to exist to cover this... make sure you are covering all of:

https://www.w3.org/TR/did-core/#also-known-as

},
"uniqueItems": true
},
"publicKey": {
"type": "array",
"items": {
Expand Down Expand Up @@ -219,6 +227,14 @@ const (
"id": {
"type": "string"
},
"alsoKnownAs": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"uniqueItems": true
},
"publicKey": {
"type": "array",
"items": {
Expand Down Expand Up @@ -406,6 +422,14 @@ const (
"id": {
"type": "string"
},
"alsoKnownAs": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"uniqueItems": true
},
"publicKey": {
"type": "array",
"items": {
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc.jsonld
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
"alsoKnownAs": [
"did:example:123"
],
"verificationMethod": [
{
"id": "did:example:123456789abcdefghi#keys-1",
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc_resolution.jsonld
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"https://w3id.org/did/v1"
],
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
"alsoKnownAs": [
"did:example:123"
],
"verificationMethod": [
{
"id": "did:example:123456789abcdefghi#keys-1",
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc_v0.11.jsonld
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"@context": ["https://w3id.org/did/v0.11"],
"id": "did:example:21tDAKCERh95uGgKbJNHYp",
"alsoKnownAs": [
"did:example:123"
],
"publicKey": [
{
"id": "did:example:123456789abcdefghi#keys-1",
Expand Down
3 changes: 3 additions & 0 deletions pkg/doc/did/testdata/valid_doc_with_base.jsonld
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"@context": ["https://www.w3.org/ns/did/v1",
{ "@base": "did:example:123456789abcdefghi"}],
"id": "did:example:123456789abcdefghi",
"alsoKnownAs": [
"did:example:123"
],
"verificationMethod": [
{
"id": "#keys-1",
Expand Down