Repository: TBD54566975/ssi-service
Pull request #121 (2022-10-07 03:23:29)

Credential Verification & DID Resolution
adding support for credential verification, which has a dependency on DID resolution
APIs for both, and tests

codecov-commenter: # [Codecov](https://codecov.io/gh/TBD54566975/ssi-service/pull/121?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) Report
> Merging [#121](https://codecov.io/gh/TBD54566975/ssi-service/pull/121?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (c2d16ef) into [main](https://codecov.io/gh/TBD54566975/ssi-service/commit/857253803c4271c5981c047c6e443857e9ca0061?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (8572538) will **increase** coverage by `0.05%`.
> The diff coverage is `45.45%`.

```diff
@@            Coverage Diff             @@
##             main     #121      +/-   ##
==========================================
+ Coverage   30.52%   30.58%   +0.05%     
==========================================
  Files          18       18              
  Lines        1245     1272      +27     
==========================================
+ Hits          380      389       +9     
- Misses        808      820      +12     
- Partials       57       63       +6     
```


| [Impacted Files](https://codecov.io/gh/TBD54566975/ssi-service/pull/121?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) | Coverage Δ | |
|---|---|---|
| [config/config.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/121/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-Y29uZmlnL2NvbmZpZy5nbw==) | `8.79% <ø> (ø)` | |
| [internal/keyaccess/jwt.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/121/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-aW50ZXJuYWwva2V5YWNjZXNzL2p3dC5nbw==) | `54.34% <45.45%> (-8.73%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975)


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dbdd5b2..6cd5ee5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
       - name: Set up Go
         uses: actions/setup-go@v3
         with:
-          go-version: 1.19
+          go-version: 1.19.2
 
       - name: Install Mage
         run: go install github.com/magefile/mage
@@ -35,7 +35,7 @@ jobs:
     - name: Set up Go
       uses: actions/setup-go@v3
       with:
-        go-version: 1.19
+        go-version: 1.19.2
 
     - name: Install mage
       run: go install github.com/magefile/mage
diff --git a/config/compose.toml b/config/compose.toml
index 332648e..fd2901d 100644
--- a/config/compose.toml
+++ b/config/compose.toml
@@ -30,6 +30,7 @@ password = "default-password"
 [services.did]
 name = "did"
 methods = ["key"]
+resolution_methods = ["key", "web", "pkh", "peer"]
 
 [services.schema]
 name = "schema"
diff --git a/config/config.go b/config/config.go
index 09bd3e2..6cb30c2 100644
--- a/config/config.go
+++ b/config/config.go
@@ -78,7 +78,8 @@ func (k *KeyStoreServiceConfig) IsEmpty() bool {
 
 type DIDServiceConfig struct {
 	*BaseServiceConfig
-	Methods []string `toml:"methods"`
+	Methods           []string `toml:"methods"`
+	ResolutionMethods []string `toml:"resolution_methods"`
 }
 
 func (d *DIDServiceConfig) IsEmpty() bool {
@@ -184,6 +185,7 @@ func LoadConfig(path string) (*SSIServiceConfig, error) {
 			DIDConfig: DIDServiceConfig{
 				BaseServiceConfig: &BaseServiceConfig{Name: "did"},
 				Methods:           []string{"key"},
+				ResolutionMethods: []string{"key", "peer", "web", "pkh"},
 			},
 			SchemaConfig: SchemaServiceConfig{
 				BaseServiceConfig: &BaseServiceConfig{Name: "schema"},
diff --git a/config/config.toml b/config/config.toml
index afd2ff3..f89ab56 100644
--- a/config/config.toml
+++ b/config/config.toml
@@ -28,6 +28,7 @@ password = "default-password"
 [services.did]
 name = "did"
 methods = ["key"]
+resolution_methods = ["key", "web", "pkh", "peer"]
 
 [services.schema]
 name = "schema"
diff --git a/go.mod b/go.mod
index bc899e1..497ce0f 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.19
 
 require (
 	github.com/BurntSushi/toml v1.2.0
-	github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221001002612-ec8641015c21
+	github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221007180051-d48966db0f15
 	github.com/ardanlabs/conf v1.5.0
 	github.com/dimfeld/httptreemux/v5 v5.4.0
 	github.com/go-playground/locales v0.14.0
@@ -21,7 +21,7 @@ require (
 	go.opentelemetry.io/otel/exporters/jaeger v1.10.0
 	go.opentelemetry.io/otel/sdk v1.10.0
 	go.opentelemetry.io/otel/trace v1.10.0
-	golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0
+	golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b
 	gopkg.in/go-playground/validator.v9 v9.31.0
 )
 
@@ -51,7 +51,7 @@ require (
 	github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
-	golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
+	golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
 	golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
diff --git a/go.sum b/go.sum
index 36431af..9cf6e77 100644
--- a/go.sum
+++ b/go.sum
@@ -8,6 +8,12 @@ github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b h1:0IJ
 github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
 github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221001002612-ec8641015c21 h1:tsvfXXVW2rqdPIYJ5uLSUhTXG4C6niB9FmjpgQE/f9w=
 github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221001002612-ec8641015c21/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221006024829-0be992d222cd h1:TfikEZGgOY0nSlJ3GlIBLisMu9zkV0cIXg+dbXX4poI=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221006024829-0be992d222cd/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221007010336-560f7ccb3b93 h1:paLRTB3VnfKfcJgx43faOgVM2ypKFJDrvovq5/BykL0=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221007010336-560f7ccb3b93/go.mod h1:oi28ndJG+ksI/NvGI+x7t6xHiDXYGOYxORTjuVynklQ=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221007180051-d48966db0f15 h1:tg8HA0K2/a7X6K/K5ayToC7Yrout5+v2CZH5sGpX2mc=
+github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20221007180051-d48966db0f15/go.mod h1:oi28ndJG+ksI/NvGI+x7t6xHiDXYGOYxORTjuVynklQ=
 github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c=
 github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -130,6 +136,8 @@ golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5
 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
 golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
+golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -141,6 +149,8 @@ golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3B
 golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
 golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM=
+golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
 golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
diff --git a/internal/credential/verification.go b/internal/credential/verification.go
new file mode 100644
index 0000000..ddc4813
--- /dev/null
+++ b/internal/credential/verification.go
@@ -0,0 +1,188 @@
+package credential
+
+import (
+	"crypto"
+	"fmt"
+
+	credsdk "github.com/TBD54566975/ssi-sdk/credential"
+	"github.com/TBD54566975/ssi-sdk/credential/signing"
+	"github.com/TBD54566975/ssi-sdk/credential/verification"
+	"github.com/TBD54566975/ssi-sdk/cryptosuite"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
+	"github.com/goccy/go-json"
+	"github.com/lestrrat-go/jwx/jwk"
+	"github.com/mr-tron/base58"
+	"github.com/multiformats/go-multibase"
+	"github.com/multiformats/go-varint"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+
+	"github.com/tbd54566975/ssi-service/internal/keyaccess"
+	"github.com/tbd54566975/ssi-service/internal/util"
+)
+
+type CredentialVerifier struct {
+	verifier *verification.CredentialVerifier
+	resolver *didsdk.Resolver
+}
+
+// NewCredentialVerifier creates a new credential verifier which executes both signature and static verification checks.
+// In the future the set of verification checks will be configurable.
+func NewCredentialVerifier(resolver *didsdk.Resolver) (*CredentialVerifier, error) {
+	if resolver == nil {
+		return nil, errors.New("resolver is nil")
+	}
+	// TODO(gabe): consider making this configurable
+	verifiers := verification.KnownVerifiers
+	verifier, err := verification.NewCredentialVerifier(verifiers)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to create static credential verifier")
+	}
+	return &CredentialVerifier{
+		verifier: verifier,
+		resolver: resolver,
+	}, nil
+}
+
+// TODO(gabe) consider moving this verification logic to the sdk https://github.com/TBD54566975/ssi-service/issues/122
+
+// VerifyJWTCredential first parses and checks the signature on the given JWT credential. Next, it runs
+// a set of static verification checks on the credential as per the credential service's configuration.
+func (v CredentialVerifier) VerifyJWTCredential(token string) error {
+	cred, err := signing.ParseVerifiableCredentialFromJWT(token)
+	if err != nil {
+		return util.LoggingErrorMsg(err, "could not parse credential from JWT")
+	}
+	kid, pubKey, err := v.getVerificationInformation(cred.Issuer.(string), "")
+	if err != nil {
+		return util.LoggingErrorMsg(err, "could not get verification from credential")
+	}
+	verifier, err := keyaccess.NewJWKKeyAccessVerifier(kid, pubKey)
+	if err != nil {
+		return util.LoggingErrorMsg(err, "could not create verifier")
+	}
+	if err := verifier.Verify(keyaccess.JWKToken{Token: token}); err != nil {
+		return util.LoggingErrorMsg(err, "could not verify credential's signature")
+	}
+	if err = v.verifier.VerifyCredential(*cred); err != nil {
+		return util.LoggingErrorMsg(err, "static credential verification failed")
+	}
+	return err
+}
+
+// VerifyDataIntegrityCredential first checks the signature on the given data integrity credential. Next, it runs
+// a set of static verification checks on the credential as per the credential service's configuration.
+func (v CredentialVerifier) VerifyDataIntegrityCredential(credential credsdk.VerifiableCredential) error {
+	// TODO(gabe): perhaps this should be a verification method referenced on the proof object, not the issuer
+	kid, pubKey, err := v.getVerificationInformation(credential.Issuer.(string), "")
+	if err != nil {
+		return util.LoggingErrorMsg(err, "could not get verification from credential")
+	}
+	verifier, err := keyaccess.NewDataIntegrityKeyAccess(kid, pubKey)
+	if err != nil {
+		return util.LoggingErrorMsg(err, "could not create verifier")
+	}
+	if err := verifier.Verify(&credential); err != nil {
+		return util.LoggingErrorMsg(err, "could not verify credential's signature")
+	}
+	if err = v.verifier.VerifyCredential(credential); err != nil {
+		return util.LoggingErrorMsg(err, "static credential verification failed")
+	}
+	return err
+}
+
+// getVerificationInformation resolves a DID and provides a kid and public key needed for credential verification
+// it is possible that a DID has multiple verification methods, in which case a kid must be provided, otherwise
+// resolution will fail.
+func (v CredentialVerifier) getVerificationInformation(did, maybeKID string) (kid string, pubKey crypto.PublicKey, err error) {
+	resolved, err := v.resolver.Resolve(did)
+	if err != nil {
+		return "", nil, errors.Wrapf(err, "failed to resolve did: %s", did)
+	}
+	if resolved.DIDDocument.IsEmpty() {
+		return "", nil, errors.Errorf("did doc: %s is empty", did)
+	}
+	verificationMethods := resolved.DIDDocument.VerificationMethod
+	if len(verificationMethods) == 0 {
+		return "", nil, errors.Errorf("did doc: %s has no verification methods", did)
+	}
+
+	// handle the case where a kid is provided && there are multiple verification methods
+	if len(verificationMethods) > 1 {
+		if kid == "" {
+			return "", nil, errors.Errorf("kid is required for did: %s, which has multiple verification methods", did)
+		}
+		for _, method := range verificationMethods {
+			if method.ID == kid {
+				kid = did
+				pubKey, err = extractKeyFromVerificationMethod(verificationMethods[0])
+				return
+			}
+		}
+	}
+	// TODO(gabe): some DIDs, like did:key have KIDs that aren't used, so we need to know when to use a kid vs the DID
+	kid = did
+	pubKey, err = extractKeyFromVerificationMethod(verificationMethods[0])
+	return
+}
+
+func extractKeyFromVerificationMethod(method didsdk.VerificationMethod) (pubKey crypto.PublicKey, err error) {
+	if method.PublicKeyMultibase != "" {
+		pubKeyBytes, multiBaseErr := multibaseToPubKeyBytes(method.PublicKeyMultibase)
+		if multiBaseErr != nil {
+			err = multiBaseErr
+			return
+		}
+		pubKey, err = cryptosuite.PubKeyBytesToTypedKey(pubKeyBytes, method.Type)
+		return
+	} else if method.PublicKeyBase58 != "" {
+		pubKeyDecoded, b58Err := base58.Decode(method.PublicKeyBase58)
+		if b58Err != nil {
+			err = b58Err
+			return
+		}
+		pubKey, err = cryptosuite.PubKeyBytesToTypedKey(pubKeyDecoded, method.Type)
+		return
+	} else if method.PublicKeyJWK != nil {
+		jwkBytes, jwkErr := json.Marshal(method.PublicKeyJWK)
+		if err != nil {
+			err = jwkErr
+			return
+		}
+		pubKey, err = jwk.ParseKey(jwkBytes)
+		return
+	}
+	err = errors.New("no public key found in verification method")
+	return
+}
+
+// multibaseToPubKey converts a multibase encoded public key to public key bytes for known multibase encodings
+func multibaseToPubKeyBytes(mb string) ([]byte, error) {
+	if mb == "" {
+		err := fmt.Errorf("could not decode value: %s", mb)
+		logrus.WithError(err).Error()
+		return nil, err
+	}
+
+	encoding, decoded, err := multibase.Decode(mb)
+	if err != nil {
+		logrus.WithError(err).Error("could not decode did:key")
+		return nil, err
+	}
+	if encoding != didsdk.Base58BTCMultiBase {
+		err := fmt.Errorf("expected %d encoding but found %d", didsdk.Base58BTCMultiBase, encoding)
+		logrus.WithError(err).Error()
+		return nil, err
+	}
+
+	// n = # bytes for the int, which we expect to be two from our multicodec
+	_, n, err := varint.FromUvarint(decoded)
+	if err != nil {
+		return nil, err
+	}
+	if n != 2 {
+		return nil, errors.New("error parsing did:key varint")
+	}
+	pubKeyBytes := decoded[n:]
+	return pubKeyBytes, nil
+}
diff --git a/internal/did/resolver.go b/internal/did/resolver.go
new file mode 100644
index 0000000..8622d3b
--- /dev/null
+++ b/internal/did/resolver.go
@@ -0,0 +1,42 @@
+package did
+
+import (
+	"fmt"
+
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
+	"github.com/pkg/errors"
+)
+
+// BuildResolver builds a DID resolver from a list of methods to support resolution for
+func BuildResolver(methods []string) (*didsdk.Resolver, error) {
+	if len(methods) == 0 {
+		return nil, errors.New("no methods provided")
+	}
+	var resolvers []didsdk.Resolution
+	for _, method := range methods {
+		resolver, err := getKnownResolver(method)
+		if err != nil {
+			return nil, err
+		}
+		resolvers = append(resolvers, resolver)
+	}
+	if len(resolvers) == 0 {
+		return nil, errors.New("no resolvers created")
+	}
+	return didsdk.NewResolver(resolvers...)
+}
+
+// all possible resolvers for the DID service
+func getKnownResolver(method string) (didsdk.Resolution, error) {
+	switch didsdk.Method(method) {
+	case didsdk.KeyMethod:
+		return new(didsdk.KeyResolver), nil
+	case didsdk.WebMethod:
+		return new(didsdk.WebResolver), nil
+	case didsdk.PKHMethod:
+		return new(didsdk.PKHResolver), nil
+	case didsdk.PeerMethod:
+		return new(didsdk.PeerResolver), nil
+	}
+	return nil, fmt.Errorf("unsupported method: %s", method)
+}
diff --git a/internal/keyaccess/jwt.go b/internal/keyaccess/jwt.go
index bdbaa2e..c5a4fa4 100644
--- a/internal/keyaccess/jwt.go
+++ b/internal/keyaccess/jwt.go
@@ -11,11 +11,11 @@ import (
 )
 
 type JWKKeyAccess struct {
-	crypto.JWTSigner
-	crypto.JWTVerifier
+	*crypto.JWTSigner
+	*crypto.JWTVerifier
 }
 
-// NewJWKKeyAccess creates a new JWKKeyAccess object from a key id and private key, generating both
+// NewJWKKeyAccess creates a JWKKeyAccess object from a key id and private key, generating both
 // JWT Signer and Verifier objects.
 func NewJWKKeyAccess(kid string, key gocrypto.PrivateKey) (*JWKKeyAccess, error) {
 	if kid == "" {
@@ -37,8 +37,29 @@ func NewJWKKeyAccess(kid string, key gocrypto.PrivateKey) (*JWKKeyAccess, error)
 		return nil, errors.Wrapf(err, "could not create JWK Key Access object for kid: %s, error creating verifier", kid)
 	}
 	return &JWKKeyAccess{
-		JWTSigner:   *signer,
-		JWTVerifier: *verifier,
+		JWTSigner:   signer,
+		JWTVerifier: verifier,
+	}, nil
+}
+
+// NewJWKKeyAccessVerifier creates JWKKeyAccess object from a key id and public key, generating a JWT Verifier object.
+func NewJWKKeyAccessVerifier(kid string, key gocrypto.PublicKey) (*JWKKeyAccess, error) {
+	if kid == "" {
+		return nil, errors.New("kid cannot be empty")
+	}
+	if key == nil {
+		return nil, errors.New("key cannot be nil")
+	}
+	gotJWK, err := jwk.New(key)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not create jwk from public key")
+	}
+	verifier, err := crypto.NewJWTVerifier(kid, gotJWK)
+	if err != nil {
+		return nil, errors.Wrapf(err, "could not create JWK Key Access object for kid: %s, error creating verifier", kid)
+	}
+	return &JWKKeyAccess{
+		JWTVerifier: verifier,
 	}, nil
 }
 
@@ -48,6 +69,9 @@ type JWKToken struct {
 }
 
 func (ka JWKKeyAccess) Sign(payload map[string]interface{}) (*JWKToken, error) {
+	if ka.JWTSigner == nil {
+		return nil, errors.New("cannot sign with nil signer")
+	}
 	if payload == nil {
 		return nil, errors.New("payload cannot be nil")
 	}
@@ -66,10 +90,13 @@ func (ka JWKKeyAccess) Verify(token JWKToken) error {
 }
 
 func (ka JWKKeyAccess) SignVerifiableCredential(credential credential.VerifiableCredential) (*JWKToken, error) {
+	if ka.JWTSigner == nil {
+		return nil, errors.New("cannot sign with nil signer")
+	}
 	if err := credential.IsValid(); err != nil {
 		return nil, errors.New("cannot sign invalid credential")
 	}
-	tokenBytes, err := signing.SignVerifiableCredentialJWT(ka.JWTSigner, credential)
+	tokenBytes, err := signing.SignVerifiableCredentialJWT(*ka.JWTSigner, credential)
 	if err != nil {
 		return nil, errors.Wrap(err, "could not sign credential")
 	}
@@ -80,14 +107,17 @@ func (ka JWKKeyAccess) VerifyVerifiableCredential(token JWKToken) (*credential.V
 	if token.Token == "" {
 		return nil, errors.New("token cannot be empty")
 	}
-	return signing.VerifyVerifiableCredentialJWT(ka.JWTVerifier, token.Token)
+	return signing.VerifyVerifiableCredentialJWT(*ka.JWTVerifier, token.Token)
 }
 
 func (ka JWKKeyAccess) SignVerifiablePresentation(presentation credential.VerifiablePresentation) (*JWKToken, error) {
+	if ka.JWTSigner == nil {
+		return nil, errors.New("cannot sign with nil signer")
+	}
 	if err := presentation.IsValid(); err != nil {
 		return nil, errors.New("cannot sign invalid presentation")
 	}
-	tokenBytes, err := signing.SignVerifiablePresentationJWT(ka.JWTSigner, presentation)
+	tokenBytes, err := signing.SignVerifiablePresentationJWT(*ka.JWTSigner, presentation)
 	if err != nil {
 		return nil, errors.Wrap(err, "could not sign presentation")
 	}
@@ -98,5 +128,5 @@ func (ka JWKKeyAccess) VerifyVerifiablePresentation(token JWKToken) (*credential
 	if token.Token == "" {
 		return nil, errors.New("token cannot be empty")
 	}
-	return signing.VerifyVerifiablePresentationJWT(ka.JWTVerifier, token.Token)
+	return signing.VerifyVerifiablePresentationJWT(*ka.JWTVerifier, token.Token)
 }
diff --git a/pkg/server/router/credential.go b/pkg/server/router/credential.go
index 824459e..67e7473 100644
--- a/pkg/server/router/credential.go
+++ b/pkg/server/router/credential.go
@@ -69,7 +69,7 @@ type CreateCredentialResponse struct {
 
 // CreateCredential godoc
 // @Summary      Create Credential
-// @Description  Create credential
+// @Description  Create a credential
 // @Tags         CredentialAPI
 // @Accept       json
 // @Produce      json
@@ -114,7 +114,7 @@ type GetCredentialResponse struct {
 // @Success      200  {object}  GetCredentialResponse
 // @Failure      400  {string}  string  "Bad request"
 // @Router       /v1/credentials/{id} [get]
-func (cr CredentialRouter) GetCredential(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
+func (cr CredentialRouter) GetCredential(ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
 	id := framework.GetParam(ctx, IDParam)
 	if id == nil {
 		errMsg := "cannot get credential without ID parameter"
@@ -137,6 +137,59 @@ func (cr CredentialRouter) GetCredential(ctx context.Context, w http.ResponseWri
 	return framework.Respond(ctx, w, resp, http.StatusOK)
 }
 
+type VerifyCredentialRequest struct {
+	DataIntegrityCredential *credsdk.VerifiableCredential `json:"credential,omitempty"`
+	CredentialJWT           *string                       `json:"credentialJwt,omitempty"`
+}
+
+func (vcr VerifyCredentialRequest) IsValid() bool {
+	return (vcr.DataIntegrityCredential != nil && vcr.CredentialJWT == nil) ||
+		(vcr.DataIntegrityCredential == nil && vcr.CredentialJWT != nil)
+}
+
+type VerifyCredentialResponse struct {
+	Verified bool   `json:"verified" json:"verified"`
+	Reason   string `json:"reason,omitempty" json:"reason,omitempty"`
+}
+
+// VerifyCredential godoc
+// @Summary      Verify Credential
+// @Description  Verify a given credential by its id
+// @Tags         CredentialAPI
+// @Accept       json
+// @Produce      json
+// @Param        request  body      VerifyCredentialRequest  true  "request body"
+// @Success      200  {object}  VerifyCredentialResponse
+// @Failure      400  {string}  string  "Bad request"
+// @Router       /v1/credentials/verification [put]
+func (cr CredentialRouter) VerifyCredential(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
+	var request VerifyCredentialRequest
+	if err := framework.Decode(r, &request); err != nil {
+		errMsg := "invalid verify credential request"
+		logrus.WithError(err).Error(errMsg)
+		return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
+	}
+
+	if !request.IsValid() {
+		err := errors.New("request must contain either a Data Integrity Credential or a JWT Credential")
+		logrus.WithError(err).Error()
+		return framework.NewRequestError(err, http.StatusBadRequest)
+	}
+
+	verificationResult, err := cr.service.VerifyCredential(credential.VerifyCredentialRequest{
+		DataIntegrityCredential: request.DataIntegrityCredential,
+		CredentialJWT:           request.CredentialJWT,
+	})
+	if err != nil {
+		errMsg := "could not verify credential"
+		logrus.WithError(err).Error(errMsg)
+		return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError)
+	}
+
+	resp := VerifyCredentialResponse{Verified: verificationResult.Verified, Reason: verificationResult.Reason}
+	return framework.Respond(ctx, w, resp, http.StatusOK)
+}
+
 type GetCredentialsResponse struct {
 	Credentials []credmodel.CredentialContainer `json:"credentials"`
 }
@@ -178,7 +231,7 @@ func (cr CredentialRouter) GetCredentials(ctx context.Context, w http.ResponseWr
 	return err
 }
 
-func (cr CredentialRouter) getCredentialsByIssuer(issuer string, ctx context.Context, w http.ResponseWriter, r *http.Request) error {
+func (cr CredentialRouter) getCredentialsByIssuer(issuer string, ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
 	gotCredentials, err := cr.service.GetCredentialsByIssuer(credential.GetCredentialByIssuerRequest{Issuer: issuer})
 	if err != nil {
 		errMsg := fmt.Sprintf("could not get credentials for issuer: %s", util.SanitizeLog(issuer))
@@ -190,7 +243,7 @@ func (cr CredentialRouter) getCredentialsByIssuer(issuer string, ctx context.Con
 	return framework.Respond(ctx, w, resp, http.StatusOK)
 }
 
-func (cr CredentialRouter) getCredentialsBySubject(subject string, ctx context.Context, w http.ResponseWriter, r *http.Request) error {
+func (cr CredentialRouter) getCredentialsBySubject(subject string, ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
 	gotCredentials, err := cr.service.GetCredentialsBySubject(credential.GetCredentialBySubjectRequest{Subject: subject})
 	if err != nil {
 		errMsg := fmt.Sprintf("could not get credentials for subject: %s", util.SanitizeLog(subject))
@@ -202,7 +255,7 @@ func (cr CredentialRouter) getCredentialsBySubject(subject string, ctx context.C
 	return framework.Respond(ctx, w, resp, http.StatusOK)
 }
 
-func (cr CredentialRouter) getCredentialsBySchema(schema string, ctx context.Context, w http.ResponseWriter, r *http.Request) error {
+func (cr CredentialRouter) getCredentialsBySchema(schema string, ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
 	gotCredentials, err := cr.service.GetCredentialsBySchema(credential.GetCredentialBySchemaRequest{Schema: schema})
 	if err != nil {
 		errMsg := fmt.Sprintf("could not get credentials for schema: %s", util.SanitizeLog(schema))
@@ -225,7 +278,7 @@ func (cr CredentialRouter) getCredentialsBySchema(schema string, ctx context.Con
 // @Failure      400  {string}  string  "Bad request"
 // @Failure      500  {string}  string  "Internal server error"
 // @Router       /v1/credentials/{id} [delete]
-func (cr CredentialRouter) DeleteCredential(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
+func (cr CredentialRouter) DeleteCredential(ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
 	id := framework.GetParam(ctx, IDParam)
 	if id == nil {
 		errMsg := "cannot delete credential without ID parameter"
diff --git a/pkg/server/router/did.go b/pkg/server/router/did.go
index 2bc1702..a59061d 100644
--- a/pkg/server/router/did.go
+++ b/pkg/server/router/did.go
@@ -40,7 +40,7 @@ func NewDIDRouter(s svcframework.Service) (*DIDRouter, error) {
 }
 
 type GetDIDMethodsResponse struct {
-	DIDMethods []did.Method `json:"didMethods,omitempty"`
+	DIDMethods []didsdk.Method `json:"methods,omitempty"`
 }
 
 // GetDIDMethods godoc
@@ -94,7 +94,7 @@ func (dr DIDRouter) CreateDIDByMethod(ctx context.Context, w http.ResponseWriter
 	}
 
 	// TODO(gabe) check if the key type is supported for the method, to tell whether this is a bad req or internal error
-	createDIDRequest := did.CreateDIDRequest{Method: did.Method(*method), KeyType: request.KeyType}
+	createDIDRequest := did.CreateDIDRequest{Method: didsdk.Method(*method), KeyType: request.KeyType}
 	createDIDResponse, err := dr.service.CreateDIDByMethod(createDIDRequest)
 	if err != nil {
 		errMsg := fmt.Sprintf("could not create DID for method<%s> with key type: %s", *method, request.KeyType)
@@ -141,7 +141,7 @@ func (dr DIDRouter) GetDIDByMethod(ctx context.Context, w http.ResponseWriter, _
 
 	// TODO(gabe) check if the method is supported, to tell whether this is a bad req or internal error
 	// TODO(gabe) differentiate between internal errors and not found DIDs
-	getDIDRequest := did.GetDIDRequest{Method: did.Method(*method), ID: *id}
+	getDIDRequest := did.GetDIDRequest{Method: didsdk.Method(*method), ID: *id}
 	gotDID, err := dr.service.GetDIDByMethod(getDIDRequest)
 	if err != nil {
 		errMsg := fmt.Sprintf("could not get DID for method<%s> with id: %s", *method, *id)
@@ -177,7 +177,7 @@ func (dr DIDRouter) GetDIDsByMethod(ctx context.Context, w http.ResponseWriter,
 
 	// TODO(gabe) check if the method is supported, to tell whether this is a bad req or internal error
 	// TODO(gabe) differentiate between internal errors and not found DIDs
-	getDIDsRequest := did.GetDIDsRequest{Method: did.Method(*method)}
+	getDIDsRequest := did.GetDIDsRequest{Method: didsdk.Method(*method)}
 	gotDIDs, err := dr.service.GetDIDsByMethod(getDIDsRequest)
 	if err != nil {
 		errMsg := fmt.Sprintf("could not get DIDs for method: %s", *method)
@@ -188,3 +188,39 @@ func (dr DIDRouter) GetDIDsByMethod(ctx context.Context, w http.ResponseWriter,
 	resp := GetDIDsByMethodResponse{DIDs: gotDIDs.DIDs}
 	return framework.Respond(ctx, w, resp, http.StatusOK)
 }
+
+type ResolveDIDResponse struct {
+	ResolutionMetadata  *didsdk.DIDResolutionMetadata `json:"didResolutionMetadata,omitempty"`
+	DIDDocument         *didsdk.DIDDocument           `json:"didDocument"`
+	DIDDocumentMetadata *didsdk.DIDDocumentMetadata   `json:"didDocumentMetadata,omitempty"`
+}
+
+// ResolveDID godoc
+// @Summary      Resolve a DID
+// @Description  Resolve a DID that may not be stored in this service
+// @Tags         DecentralizedIdentityAPI
+// @Accept       json
+// @Produce      json
+// @Param        id       path      string                    true  "ID"
+// @Success      200      {object}  ResolveDIDResponse
+// @Failure      400      {string}  string  "Bad request"
+// @Router       /v1/dids/resolver/{id} [get]
+func (dr DIDRouter) ResolveDID(ctx context.Context, w http.ResponseWriter, _ *http.Request) error {
+	id := framework.GetParam(ctx, IDParam)
+	if id == nil {
+		errMsg := "get DID request missing id parameter"
+		logrus.Error(errMsg)
+		return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest)
+	}
+
+	resolveDIDRequest := did.ResolveDIDRequest{DID: *id}
+	resolvedDID, err := dr.service.ResolveDID(resolveDIDRequest)
+	if err != nil {
+		errMsg := fmt.Sprintf("could not get DID with id: %s", *id)
+		logrus.WithError(err).Error(errMsg)
+		return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
+	}
+
+	resp := ResolveDIDResponse{ResolutionMetadata: resolvedDID.ResolutionMetadata, DIDDocument: resolvedDID.DIDDocument, DIDDocumentMetadata: resolvedDID.DIDDocumentMetadata}
+	return framework.Respond(ctx, w, resp, http.StatusOK)
+}
diff --git a/pkg/service/credential/credential.go b/pkg/service/credential/credential.go
index 2e6071d..11aab8c 100644
--- a/pkg/service/credential/credential.go
+++ b/pkg/service/credential/credential.go
@@ -5,11 +5,12 @@ import (
 	"time"
 
 	"github.com/TBD54566975/ssi-sdk/credential"
+	didint "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 
 	"github.com/tbd54566975/ssi-service/config"
-	credmodel "github.com/tbd54566975/ssi-service/internal/credential"
+	credint "github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/internal/keyaccess"
 	"github.com/tbd54566975/ssi-service/internal/util"
 	credstorage "github.com/tbd54566975/ssi-service/pkg/service/credential/storage"
@@ -19,8 +20,9 @@ import (
 )
 
 type Service struct {
-	storage credstorage.Storage
-	config  config.CredentialServiceConfig
+	storage  credstorage.Storage
+	config   config.CredentialServiceConfig
+	verifier *credint.CredentialVerifier
 
 	// external dependencies
 	keyStore *keystore.Service
@@ -44,15 +46,20 @@ func (s Service) Config() config.CredentialServiceConfig {
 	return s.config
 }
 
-func NewCredentialService(config config.CredentialServiceConfig, s storage.ServiceStorage, keyStore *keystore.Service) (*Service, error) {
+func NewCredentialService(config config.CredentialServiceConfig, s storage.ServiceStorage, keyStore *keystore.Service, resolver *didint.Resolver) (*Service, error) {
 	credentialStorage, err := credstorage.NewCredentialStorage(s)
 	if err != nil {
 		errMsg := "could not instantiate storage for the credential service"
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
+	verifier, err := credint.NewCredentialVerifier(resolver)
+	if err != nil {
+		return nil, util.LoggingErrorMsg(err, "could not instantiate verifier for the credential service")
+	}
 	return &Service{
 		storage:  credentialStorage,
 		config:   config,
+		verifier: verifier,
 		keyStore: keyStore,
 	}, nil
 }
@@ -130,7 +137,7 @@ func (s Service) CreateCredential(request CreateCredentialRequest) (*CreateCrede
 	}
 
 	// store the credential
-	container := credmodel.CredentialContainer{
+	container := credint.CredentialContainer{
 		ID:            cred.ID,
 		Credential:    cred,
 		CredentialJWT: credJWT,
@@ -168,6 +175,55 @@ func (s Service) signCredentialJWT(issuer string, cred credential.VerifiableCred
 	return &credToken.Token, nil
 }
 
+type VerifyCredentialRequest struct {
+	DataIntegrityCredential *credential.VerifiableCredential `json:"credential,omitempty"`
+	CredentialJWT           *string                          `json:"credentialJwt,omitempty"`
+}
+
+// IsValid checks if the request is valid, meaning there is at least one data integrity OR jwt credential, but not both
+func (vcr VerifyCredentialRequest) IsValid() error {
+	if vcr.DataIntegrityCredential == nil && vcr.CredentialJWT == nil {
+		return errors.New("either a credential or a credential JWT must be provided")
+	}
+	if vcr.DataIntegrityCredential != nil && vcr.CredentialJWT != nil {
+		return errors.New("only one of credential or credential JWT can be provided")
+	}
+	return nil
+}
+
+type VerifyCredentialResponse struct {
+	Verified bool   `json:"verified" json:"verified"`
+	Reason   string `json:"reason,omitempty" json:"reason,omitempty"`
+}
+
+// VerifyCredential does three levels of verification on a credential:
+// 1. Makes sure the credential has a valid signature
+// 2. Makes sure the credential has is not expired
+// 3. Makes sure the credential complies with the VC Data Model
+// 4. If the credential has a schema, makes sure its data complies with the schema
+// LATER: Makes sure the credential has not been revoked, other checks.
+// Note: https://github.com/TBD54566975/ssi-sdk/issues/213
+func (s Service) VerifyCredential(request VerifyCredentialRequest) (*VerifyCredentialResponse, error) {
+
+	logrus.Debugf("verifying credential: %+v", request)
+
+	if err := request.IsValid(); err != nil {
+		return nil, util.LoggingErrorMsg(err, "invalid verify credential request")
+	}
+
+	if request.CredentialJWT != nil {
+		if err := s.verifier.VerifyJWTCredential(*request.CredentialJWT); err != nil {
+			return &VerifyCredentialResponse{Verified: false, Reason: err.Error()}, nil
+		}
+	} else {
+		if err := s.verifier.VerifyDataIntegrityCredential(*request.DataIntegrityCredential); err != nil {
+			return &VerifyCredentialResponse{Verified: false, Reason: err.Error()}, nil
+		}
+	}
+
+	return &VerifyCredentialResponse{Verified: true}, nil
+}
+
 func (s Service) GetCredential(request GetCredentialRequest) (*GetCredentialResponse, error) {
 
 	logrus.Debugf("getting credential: %s", request.ID)
@@ -182,7 +238,7 @@ func (s Service) GetCredential(request GetCredentialRequest) (*GetCredentialResp
 		return nil, util.LoggingNewError(errMsg)
 	}
 	response := GetCredentialResponse{
-		credmodel.CredentialContainer{
+		credint.CredentialContainer{
 			ID:            gotCred.CredentialID,
 			Credential:    gotCred.Credential,
 			CredentialJWT: gotCred.CredentialJWT,
@@ -201,9 +257,9 @@ func (s Service) GetCredentialsByIssuer(request GetCredentialByIssuerRequest) (*
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credmodel.CredentialContainer
+	var creds []credint.CredentialContainer
 	for _, cred := range gotCreds {
-		container := credmodel.CredentialContainer{
+		container := credint.CredentialContainer{
 			ID:            cred.CredentialID,
 			Credential:    cred.Credential,
 			CredentialJWT: cred.CredentialJWT,
@@ -225,9 +281,9 @@ func (s Service) GetCredentialsBySubject(request GetCredentialBySubjectRequest)
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credmodel.CredentialContainer
+	var creds []credint.CredentialContainer
 	for _, cred := range gotCreds {
-		container := credmodel.CredentialContainer{
+		container := credint.CredentialContainer{
 			ID:            cred.CredentialID,
 			Credential:    cred.Credential,
 			CredentialJWT: cred.CredentialJWT,
@@ -248,9 +304,9 @@ func (s Service) GetCredentialsBySchema(request GetCredentialBySchemaRequest) (*
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	var creds []credmodel.CredentialContainer
+	var creds []credint.CredentialContainer
 	for _, cred := range gotCreds {
-		container := credmodel.CredentialContainer{
+		container := credint.CredentialContainer{
 			ID:            cred.CredentialID,
 			Credential:    cred.Credential,
 			CredentialJWT: cred.CredentialJWT,
diff --git a/pkg/service/did/did.go b/pkg/service/did/did.go
index 3571c7d..11daeb9 100644
--- a/pkg/service/did/did.go
+++ b/pkg/service/did/did.go
@@ -3,9 +3,11 @@ package did
 import (
 	"fmt"
 
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/pkg/errors"
 
 	"github.com/tbd54566975/ssi-service/config"
+	"github.com/tbd54566975/ssi-service/internal/did"
 	"github.com/tbd54566975/ssi-service/internal/util"
 	didstorage "github.com/tbd54566975/ssi-service/pkg/service/did/storage"
 	"github.com/tbd54566975/ssi-service/pkg/service/framework"
@@ -13,17 +15,13 @@ import (
 	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
 
-type Method string
-
-const (
-	KeyMethod Method = "key"
-)
-
 type Service struct {
-	// supported DID methods
-	handlers map[Method]MethodHandler
-	storage  didstorage.Storage
 	config   config.DIDServiceConfig
+	storage  didstorage.Storage
+	resolver *didsdk.Resolver
+
+	// supported DID methods
+	handlers map[didsdk.Method]MethodHandler
 
 	// external dependencies
 	keyStore *keystore.Service
@@ -33,7 +31,7 @@ type Service struct {
 type MethodHandler interface {
 	CreateDID(request CreateDIDRequest) (*CreateDIDResponse, error)
 	GetDID(request GetDIDRequest) (*GetDIDResponse, error)
-	GetDIDs(method Method) (*GetDIDsResponse, error)
+	GetDIDs(method didsdk.Method) (*GetDIDsResponse, error)
 }
 
 func NewDIDService(config config.DIDServiceConfig, s storage.ServiceStorage, keyStore *keystore.Service) (*Service, error) {
@@ -41,27 +39,36 @@ func NewDIDService(config config.DIDServiceConfig, s storage.ServiceStorage, key
 	if err != nil {
 		return nil, errors.Wrap(err, "could not instantiate DID storage for the DID service")
 	}
+
+	// instantiate DID resolver
+	resolver, err := did.BuildResolver(config.ResolutionMethods)
+	if err != nil {
+		return nil, errors.Wrap(err, "could not instantiate DID resolver")
+	}
+
 	svc := Service{
 		storage:  didStorage,
-		handlers: make(map[Method]MethodHandler),
+		handlers: make(map[didsdk.Method]MethodHandler),
 		keyStore: keyStore,
+		resolver: resolver,
 	}
 
 	// instantiate all handlers for DID methods
 	for _, m := range config.Methods {
-		if err := svc.instantiateHandlerForMethod(Method(m)); err != nil {
+		if err := svc.instantiateHandlerForMethod(didsdk.Method(m)); err != nil {
 			return nil, errors.Wrap(err, "could not instantiate DID service")
 		}
 	}
+
 	return &svc, nil
 }
 
-func (s *Service) instantiateHandlerForMethod(method Method) error {
+func (s *Service) instantiateHandlerForMethod(method didsdk.Method) error {
 	switch method {
-	case KeyMethod:
+	case didsdk.KeyMethod:
 		handler, err := newKeyDIDHandler(s.storage, s.keyStore)
 		if err != nil {
-			err := fmt.Errorf("could not instnatiate did:%s handler", KeyMethod)
+			err := fmt.Errorf("could not instnatiate did:%s handler", didsdk.KeyMethod)
 			return util.LoggingError(err)
 		}
 		s.handlers[method] = handler
@@ -91,8 +98,27 @@ func (s *Service) Config() config.DIDServiceConfig {
 	return s.config
 }
 
+func (s *Service) GetResolver() *didsdk.Resolver {
+	return s.resolver
+}
+
+func (s *Service) ResolveDID(request ResolveDIDRequest) (*ResolveDIDResponse, error) {
+	if request.DID == "" {
+		return nil, util.LoggingNewError("cannot resolve empty DID")
+	}
+	resolved, err := s.resolver.Resolve(request.DID)
+	if err != nil {
+		return nil, err
+	}
+	return &ResolveDIDResponse{
+		ResolutionMetadata:  &resolved.DIDResolutionMetadata,
+		DIDDocument:         &resolved.DIDDocument,
+		DIDDocumentMetadata: &resolved.DIDDocumentMetadata,
+	}, nil
+}
+
 func (s *Service) GetSupportedMethods() GetSupportedMethodsResponse {
-	var methods []Method
+	var methods []didsdk.Method
 	for method := range s.handlers {
 		methods = append(methods, method)
 	}
@@ -127,7 +153,7 @@ func (s *Service) GetDIDsByMethod(request GetDIDsRequest) (*GetDIDsResponse, err
 	return handler.GetDIDs(method)
 }
 
-func (s *Service) getHandler(method Method) (MethodHandler, error) {
+func (s *Service) getHandler(method didsdk.Method) (MethodHandler, error) {
 	handler, ok := s.handlers[method]
 	if !ok {
 		err := fmt.Errorf("could not get handler for DID method: %s", method)
diff --git a/pkg/service/did/key.go b/pkg/service/did/key.go
index e56de59..15da7fb 100644
--- a/pkg/service/did/key.go
+++ b/pkg/service/did/key.go
@@ -87,7 +87,7 @@ func (h *keyDIDHandler) GetDID(request GetDIDRequest) (*GetDIDResponse, error) {
 	return &GetDIDResponse{DID: gotDID.DID}, nil
 }
 
-func (h *keyDIDHandler) GetDIDs(method Method) (*GetDIDsResponse, error) {
+func (h *keyDIDHandler) GetDIDs(method did.Method) (*GetDIDsResponse, error) {
 
 	logrus.Debugf("getting DIDs for method: %s", method)
 
diff --git a/pkg/service/did/model.go b/pkg/service/did/model.go
index ea49ec1..1bbbda2 100644
--- a/pkg/service/did/model.go
+++ b/pkg/service/did/model.go
@@ -6,12 +6,22 @@ import (
 )
 
 type GetSupportedMethodsResponse struct {
-	Methods []Method `json:"methods"`
+	Methods []didsdk.Method `json:"methods"`
+}
+
+type ResolveDIDRequest struct {
+	DID string `json:"did" validate:"required"`
+}
+
+type ResolveDIDResponse struct {
+	ResolutionMetadata  *didsdk.DIDResolutionMetadata `json:"didResolutionMetadata,omitempty"`
+	DIDDocument         *didsdk.DIDDocument           `json:"didDocument"`
+	DIDDocumentMetadata *didsdk.DIDDocumentMetadata   `json:"didDocumentMetadata,omitempty"`
 }
 
 // CreateDIDRequest is the JSON-serializable request for creating a DID across DID methods
 type CreateDIDRequest struct {
-	Method  Method         `json:"method" validate:"required"`
+	Method  didsdk.Method  `json:"method" validate:"required"`
 	KeyType crypto.KeyType `validate:"required"`
 }
 
@@ -23,8 +33,8 @@ type CreateDIDResponse struct {
 }
 
 type GetDIDRequest struct {
-	Method Method `json:"method" validate:"required"`
-	ID     string `json:"id" validate:"required"`
+	Method didsdk.Method `json:"method" validate:"required"`
+	ID     string        `json:"id" validate:"required"`
 }
 
 // GetDIDResponse is the JSON-serializable response for getting a DID
@@ -33,7 +43,7 @@ type GetDIDResponse struct {
 }
 
 type GetDIDsRequest struct {
-	Method Method `json:"method" validate:"required"`
+	Method didsdk.Method `json:"method" validate:"required"`
 }
 
 // GetDIDsResponse is the JSON-serializable response for getting all DIDs for a given method
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 18724c1..991d174 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -89,7 +89,7 @@ func instantiateServices(config config.ServicesConfig) ([]framework.Service, err
 		return nil, util.LoggingErrorMsg(err, "could not instantiate the schema service")
 	}
 
-	credentialService, err := credential.NewCredentialService(config.CredentialConfig, storageProvider, keyStoreService)
+	credentialService, err := credential.NewCredentialService(config.CredentialConfig, storageProvider, keyStoreService, didService.GetResolver())
 	if err != nil {
 		return nil, util.LoggingErrorMsg(err, "could not instantiate the credential service")
 	}


Test patch:
diff --git a/internal/did/resolver_test.go b/internal/did/resolver_test.go
new file mode 100644
index 0000000..4f6c4a4
--- /dev/null
+++ b/internal/did/resolver_test.go
@@ -0,0 +1,32 @@
+package did
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestResolver(t *testing.T) {
+	// empty resolver
+	_, err := BuildResolver(nil)
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "no methods provided")
+
+	// unsupported method
+	_, err = BuildResolver([]string{"unsupported"})
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "unsupported method: unsupported")
+
+	// valid method
+	resolver, err := BuildResolver([]string{"key"})
+	assert.NoError(t, err)
+	assert.NotEmpty(t, resolver)
+	resolved, err := resolver.Resolve("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp")
+	assert.NoError(t, err)
+	assert.NotEmpty(t, resolved)
+
+	// resolve for a method that is not supported
+	_, err = resolver.Resolve("did:web:example.com")
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "unsupported method: web")
+}
diff --git a/internal/keyaccess/jwt_test.go b/internal/keyaccess/jwt_test.go
index c1a0073..ef5e67e 100644
--- a/internal/keyaccess/jwt_test.go
+++ b/internal/keyaccess/jwt_test.go
@@ -56,6 +56,19 @@ func TestJWKKeyAccessSignVerify(t *testing.T) {
 
 		err = ka.Verify(*token)
 		assert.NoError(tt, err)
+
+		// Create just a verifier and check that it can verify the token
+		verifier, err := NewJWKKeyAccessVerifier(kid, privKey.Public())
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, verifier)
+
+		err = verifier.Verify(*token)
+		assert.NoError(tt, err)
+
+		// Make sure the verifier can't sign
+		_, err = verifier.Sign(data)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "cannot sign with nil signer")
 	})
 
 	t.Run("Sign and Verify - Bad Data", func(tt *testing.T) {
diff --git a/pkg/server/router/credential_test.go b/pkg/server/router/credential_test.go
index 34a9cfe..980cee0 100644
--- a/pkg/server/router/credential_test.go
+++ b/pkg/server/router/credential_test.go
@@ -8,6 +8,7 @@ import (
 
 	credsdk "github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/stretchr/testify/assert"
 
 	"github.com/tbd54566975/ssi-service/config"
@@ -45,7 +46,7 @@ func TestCredentialRouter(t *testing.T) {
 		serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential"}}
 		keyStoreService := testKeyStoreService(tt, bolt)
 		didService := testDIDService(tt, bolt, keyStoreService)
-		credService, err := credential.NewCredentialService(serviceConfig, bolt, keyStoreService)
+		credService, err := credential.NewCredentialService(serviceConfig, bolt, keyStoreService, didService.GetResolver())
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, credService)
 
@@ -55,7 +56,7 @@ func TestCredentialRouter(t *testing.T) {
 
 		// create a credential
 
-		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: did.KeyMethod, KeyType: crypto.Ed25519})
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: didsdk.KeyMethod, KeyType: crypto.Ed25519})
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, issuerDID)
 
diff --git a/pkg/server/router/did_test.go b/pkg/server/router/did_test.go
index 3799ed9..49bacef 100644
--- a/pkg/server/router/did_test.go
+++ b/pkg/server/router/did_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/stretchr/testify/assert"
 
 	"github.com/tbd54566975/ssi-service/config"
@@ -40,7 +41,8 @@ func TestDIDRouter(t *testing.T) {
 		assert.NotEmpty(tt, db)
 
 		keyStoreService := testKeyStoreService(tt, db)
-		serviceConfig := config.DIDServiceConfig{Methods: []string{string(did.KeyMethod)}}
+		methods := []string{didsdk.KeyMethod.String()}
+		serviceConfig := config.DIDServiceConfig{Methods: methods, ResolutionMethods: methods}
 		didService, err := did.NewDIDService(serviceConfig, db, keyStoreService)
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, didService)
@@ -57,15 +59,15 @@ func TestDIDRouter(t *testing.T) {
 		supported := didService.GetSupportedMethods()
 		assert.NotEmpty(tt, supported)
 		assert.Len(tt, supported.Methods, 1)
-		assert.Equal(tt, did.KeyMethod, supported.Methods[0])
+		assert.Equal(tt, didsdk.KeyMethod, supported.Methods[0])
 
 		// bad key type
-		_, err = didService.CreateDIDByMethod(did.CreateDIDRequest{Method: did.KeyMethod, KeyType: "bad"})
+		_, err = didService.CreateDIDByMethod(did.CreateDIDRequest{Method: didsdk.KeyMethod, KeyType: "bad"})
 		assert.Error(tt, err)
 		assert.Contains(tt, err.Error(), "could not create did:key")
 
 		// good key type
-		createDIDResponse, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: did.KeyMethod, KeyType: crypto.Ed25519})
+		createDIDResponse, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: didsdk.KeyMethod, KeyType: crypto.Ed25519})
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createDIDResponse)
 
@@ -73,7 +75,7 @@ func TestDIDRouter(t *testing.T) {
 		assert.Contains(tt, createDIDResponse.DID.ID, "did:key")
 
 		// get it back
-		getDIDResponse, err := didService.GetDIDByMethod(did.GetDIDRequest{Method: did.KeyMethod, ID: createDIDResponse.DID.ID})
+		getDIDResponse, err := didService.GetDIDByMethod(did.GetDIDRequest{Method: didsdk.KeyMethod, ID: createDIDResponse.DID.ID})
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, getDIDResponse)
 
@@ -81,12 +83,12 @@ func TestDIDRouter(t *testing.T) {
 		assert.Equal(tt, createDIDResponse.DID.ID, getDIDResponse.DID.ID)
 
 		// create a second DID
-		createDIDResponse2, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: did.KeyMethod, KeyType: crypto.Ed25519})
+		createDIDResponse2, err := didService.CreateDIDByMethod(did.CreateDIDRequest{Method: didsdk.KeyMethod, KeyType: crypto.Ed25519})
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, createDIDResponse2)
 
 		// get all DIDs back
-		getDIDsResponse, err := didService.GetDIDsByMethod(did.GetDIDsRequest{Method: did.KeyMethod})
+		getDIDsResponse, err := didService.GetDIDsByMethod(did.GetDIDsRequest{Method: didsdk.KeyMethod})
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, getDIDsResponse)
 		assert.Len(tt, getDIDsResponse.DIDs, 2)
diff --git a/pkg/server/router/dwn_test.go b/pkg/server/router/dwn_test.go
index 8f0b7f9..486d9d7 100644
--- a/pkg/server/router/dwn_test.go
+++ b/pkg/server/router/dwn_test.go
@@ -39,7 +39,8 @@ func TestDWNRouter(t *testing.T) {
 
 		serviceConfig := config.DWNServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "dwn"}}
 		keyStore := testKeyStoreService(t, bolt)
-		credentialService := testCredentialService(t, bolt, keyStore)
+		didService := testDIDService(t, bolt, keyStore)
+		credentialService := testCredentialService(t, bolt, keyStore, didService)
 		manifestService := testManifestService(t, bolt, keyStore, credentialService)
 		dwnService, err := dwn.NewDWNService(serviceConfig, bolt, keyStore, manifestService)
 		assert.NoError(tt, err)
diff --git a/pkg/server/router/manifest_test.go b/pkg/server/router/manifest_test.go
index 348f766..6002799 100644
--- a/pkg/server/router/manifest_test.go
+++ b/pkg/server/router/manifest_test.go
@@ -7,6 +7,7 @@ import (
 	"github.com/TBD54566975/ssi-sdk/credential/exchange"
 	manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 
@@ -44,7 +45,8 @@ func TestManifestRouter(t *testing.T) {
 
 		serviceConfig := config.ManifestServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "manifest"}}
 		keyStoreService := testKeyStoreService(tt, bolt)
-		testCredentialService := testCredentialService(tt, bolt, keyStoreService)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		testCredentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestService, err := manifest.NewManifestService(serviceConfig, bolt, keyStoreService, testCredentialService)
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, manifestService)
@@ -54,9 +56,8 @@ func TestManifestRouter(t *testing.T) {
 		assert.Equal(tt, framework.StatusReady, manifestService.Status().Status)
 
 		// create issuer and applicant DIDs
-		didService := testDIDService(tt, bolt, keyStoreService)
 		createDIDRequest := did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		}
 		issuerDID, err := didService.CreateDIDByMethod(createDIDRequest)
diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go
index 4bbf129..89ebd6b 100644
--- a/pkg/server/router/router_test.go
+++ b/pkg/server/router/router_test.go
@@ -1,8 +1,9 @@
 package router
 
 import (
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
+
 	"github.com/tbd54566975/ssi-service/config"
-	"github.com/tbd54566975/ssi-service/pkg/service/did"
 	"github.com/tbd54566975/ssi-service/pkg/service/framework"
 )
 
@@ -22,7 +23,7 @@ func (s *testService) Config() config.ServicesConfig {
 	return config.ServicesConfig{
 		StorageProvider:  "bolt",
 		KeyStoreConfig:   config.KeyStoreServiceConfig{ServiceKeyPassword: "test-password"},
-		DIDConfig:        config.DIDServiceConfig{Methods: []string{string(did.KeyMethod)}},
+		DIDConfig:        config.DIDServiceConfig{Methods: []string{string(didsdk.KeyMethod)}},
 		SchemaConfig:     config.SchemaServiceConfig{},
 		CredentialConfig: config.CredentialServiceConfig{},
 		ManifestConfig:   config.ManifestServiceConfig{},
diff --git a/pkg/server/router/testutils_test.go b/pkg/server/router/testutils_test.go
index 1a052a6..f947971 100644
--- a/pkg/server/router/testutils_test.go
+++ b/pkg/server/router/testutils_test.go
@@ -23,7 +23,13 @@ func testKeyStoreService(t *testing.T, db *storage.BoltDB) *keystore.Service {
 }
 
 func testDIDService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service) *did.Service {
-	serviceConfig := config.DIDServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "did"}, Methods: []string{"key"}}
+	serviceConfig := config.DIDServiceConfig{
+		BaseServiceConfig: &config.BaseServiceConfig{
+			Name: "did",
+		},
+		Methods:           []string{"key"},
+		ResolutionMethods: []string{"key"},
+	}
 	// create a did service
 	didService, err := did.NewDIDService(serviceConfig, db, keyStore)
 	require.NoError(t, err)
@@ -31,10 +37,10 @@ func testDIDService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service
 	return didService
 }
 
-func testCredentialService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service) *credential.Service {
+func testCredentialService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service, did *did.Service) *credential.Service {
 	serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential"}}
 	// create a credential service
-	credentialService, err := credential.NewCredentialService(serviceConfig, db, keyStore)
+	credentialService, err := credential.NewCredentialService(serviceConfig, db, keyStore, did.GetResolver())
 	require.NoError(t, err)
 	require.NotEmpty(t, credentialService)
 	return credentialService
diff --git a/pkg/server/server_credential_test.go b/pkg/server/server_credential_test.go
index eb65387..ef829b5 100644
--- a/pkg/server/server_credential_test.go
+++ b/pkg/server/server_credential_test.go
@@ -9,10 +9,12 @@ import (
 	"time"
 
 	credsdk "github.com/TBD54566975/ssi-sdk/credential"
-	"github.com/TBD54566975/ssi-sdk/credential/signing"
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
+	"github.com/TBD54566975/ssi-sdk/util"
 	"github.com/goccy/go-json"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
 	"github.com/tbd54566975/ssi-service/pkg/service/did"
@@ -22,6 +24,7 @@ import (
 func TestCredentialAPI(t *testing.T) {
 	t.Run("Test Create Credential", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -30,11 +33,11 @@ func TestCredentialAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credService := testCredentialRouter(tt, bolt, keyStoreService, didService)
 
 		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -96,14 +99,13 @@ func TestCredentialAPI(t *testing.T) {
 		assert.NoError(tt, err)
 
 		assert.NotEmpty(tt, resp.CredentialJWT)
-		parsedCred, err := signing.ParseVerifiableCredentialFromJWT(*resp.CredentialJWT)
 		assert.NoError(tt, err)
-		assert.NotEmpty(tt, parsedCred)
-		assert.Equal(tt, parsedCred.Issuer, issuerDID.DID.ID)
+		assert.Equal(tt, resp.Credential.Issuer, issuerDID.DID.ID)
 	})
 
 	t.Run("Test Get Credential By ID", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -112,8 +114,8 @@ func TestCredentialAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credService := testCredentialRouter(tt, bolt, keyStoreService, didService)
 
 		w := httptest.NewRecorder()
 
@@ -136,7 +138,7 @@ func TestCredentialAPI(t *testing.T) {
 		w.Flush()
 
 		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -178,6 +180,7 @@ func TestCredentialAPI(t *testing.T) {
 
 	t.Run("Test Get Credential By Schema", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -186,13 +189,13 @@ func TestCredentialAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credService := testCredentialRouter(tt, bolt, keyStoreService, didService)
 
 		w := httptest.NewRecorder()
 
 		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -238,6 +241,7 @@ func TestCredentialAPI(t *testing.T) {
 
 	t.Run("Test Get Credential By Issuer", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -246,13 +250,13 @@ func TestCredentialAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credService := testCredentialRouter(tt, bolt, keyStoreService, didService)
 
 		w := httptest.NewRecorder()
 
 		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -298,6 +302,7 @@ func TestCredentialAPI(t *testing.T) {
 
 	t.Run("Test Get Credential By Subject", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -306,13 +311,13 @@ func TestCredentialAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credService := testCredentialRouter(tt, bolt, keyStoreService, didService)
 
 		w := httptest.NewRecorder()
 
 		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -359,6 +364,7 @@ func TestCredentialAPI(t *testing.T) {
 
 	t.Run("Test Delete Credential", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -367,11 +373,11 @@ func TestCredentialAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credService := testCredentialRouter(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credService := testCredentialRouter(tt, bolt, keyStoreService, didService)
 
 		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -425,4 +431,76 @@ func TestCredentialAPI(t *testing.T) {
 		assert.Error(tt, err)
 		assert.Contains(tt, err.Error(), fmt.Sprintf("could not get credential with id: %s", credID))
 	})
+
+	t.Run("Test Verifying a Credential", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		keyStoreService := testKeyStoreService(tt, bolt)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		credService := testCredentialRouter(tt, bolt, keyStoreService, didService)
+
+		issuerDID, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
+			Method:  didsdk.KeyMethod,
+			KeyType: crypto.Ed25519,
+		})
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, issuerDID)
+
+		// good request
+		createCredRequest := router.CreateCredentialRequest{
+			Issuer:  issuerDID.DID.ID,
+			Subject: "did:abc:456",
+			Data: map[string]interface{}{
+				"firstName": "Jack",
+				"lastName":  "Dorsey",
+			},
+			Expiry: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+		}
+		requestValue := newRequestValue(tt, createCredRequest)
+		req := httptest.NewRequest(http.MethodPut, "https://ssi-service.com/v1/credentials", requestValue)
+		w := httptest.NewRecorder()
+		err = credService.CreateCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var resp router.CreateCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&resp)
+		assert.NoError(tt, err)
+
+		assert.NotEmpty(tt, resp.CredentialJWT)
+		assert.NoError(tt, err)
+		assert.Equal(tt, resp.Credential.Issuer, issuerDID.DID.ID)
+
+		w.Flush()
+
+		// verify the credential
+		requestValue = newRequestValue(tt, router.VerifyCredentialRequest{CredentialJWT: resp.CredentialJWT})
+		req = httptest.NewRequest(http.MethodPost, "https://ssi-service.com/v1/credentials/verification", requestValue)
+		err = credService.VerifyCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		var verifyResp router.VerifyCredentialResponse
+		err = json.NewDecoder(w.Body).Decode(&verifyResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, verifyResp)
+		assert.True(tt, verifyResp.Verified)
+
+		// bad credential
+		requestValue = newRequestValue(tt, router.VerifyCredentialRequest{CredentialJWT: util.StringPtr("badjwt")})
+		req = httptest.NewRequest(http.MethodPost, "https://ssi-service.com/v1/credentials/verification", requestValue)
+		err = credService.VerifyCredential(newRequestContext(), w, req)
+		assert.NoError(tt, err)
+
+		err = json.NewDecoder(w.Body).Decode(&verifyResp)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, verifyResp)
+		assert.False(tt, verifyResp.Verified)
+		assert.Contains(tt, verifyResp.Reason, "could not parse credential from JWT")
+	})
 }
diff --git a/pkg/server/server_did_test.go b/pkg/server/server_did_test.go
index f5bf3b5..25a5120 100644
--- a/pkg/server/server_did_test.go
+++ b/pkg/server/server_did_test.go
@@ -8,17 +8,19 @@ import (
 	"testing"
 
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/goccy/go-json"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
-	"github.com/tbd54566975/ssi-service/pkg/service/did"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
 
 func TestDIDAPI(t *testing.T) {
 	t.Run("Test Get DID Methods", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -42,11 +44,12 @@ func TestDIDAPI(t *testing.T) {
 		assert.NoError(tt, err)
 
 		assert.Len(tt, resp.DIDMethods, 1)
-		assert.Equal(tt, resp.DIDMethods[0], did.KeyMethod)
+		assert.Equal(tt, resp.DIDMethods[0], didsdk.KeyMethod)
 	})
 
 	t.Run("Test Create DID By Method: Key", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -95,11 +98,12 @@ func TestDIDAPI(t *testing.T) {
 		err = json.NewDecoder(w.Body).Decode(&resp)
 		assert.NoError(tt, err)
 
-		assert.Contains(tt, resp.DID.ID, did.KeyMethod)
+		assert.Contains(tt, resp.DID.ID, didsdk.KeyMethod)
 	})
 
 	t.Run("Test Get DID By Method", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -174,6 +178,7 @@ func TestDIDAPI(t *testing.T) {
 
 	t.Run("Test Get DIDs By Method", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -259,4 +264,56 @@ func TestDIDAPI(t *testing.T) {
 		}
 		assert.Len(tt, knownDIDs, 0)
 	})
+
+	t.Run("Test Resolve DIDs", func(tt *testing.T) {
+		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
+
+		// remove the db file after the test
+		tt.Cleanup(func() {
+			_ = bolt.Close()
+			_ = os.Remove(storage.DBFile)
+		})
+
+		_, keyStore := testKeyStore(tt, bolt)
+		didService := testDIDRouter(tt, bolt, keyStore)
+
+		// bad resolution request
+		req := httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/resolver/bad", nil)
+		w := httptest.NewRecorder()
+
+		badParams := map[string]string{
+			"id": "bad",
+		}
+		err = didService.ResolveDID(newRequestContextWithParams(badParams), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "not a valid did: bad")
+
+		w.Flush()
+
+		// known method, bad did
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/resolver/did:key:abcd", nil)
+		badParams = map[string]string{
+			"id": "did:key:abcd",
+		}
+		err = didService.ResolveDID(newRequestContextWithParams(badParams), w, req)
+		assert.Error(tt, err)
+		assert.Contains(tt, err.Error(), "did:key:abcd: selected encoding not supported")
+
+		w.Flush()
+
+		// known method, good did
+		req = httptest.NewRequest(http.MethodGet, "https://ssi-service.com/v1/dids/resolver/did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", nil)
+		goodParams := map[string]string{
+			"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
+		}
+		err = didService.ResolveDID(newRequestContextWithParams(goodParams), w, req)
+		assert.NoError(tt, err)
+
+		var resolutionResponse router.ResolveDIDResponse
+		err = json.NewDecoder(w.Body).Decode(&resolutionResponse)
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, resolutionResponse.DIDDocument)
+		assert.Equal(tt, "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", resolutionResponse.DIDDocument.ID)
+	})
 }
diff --git a/pkg/server/server_dwn_test.go b/pkg/server/server_dwn_test.go
index 7b7cfde..a307650 100644
--- a/pkg/server/server_dwn_test.go
+++ b/pkg/server/server_dwn_test.go
@@ -7,8 +7,10 @@ import (
 	"testing"
 
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/goccy/go-json"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
 	"github.com/tbd54566975/ssi-service/pkg/service/did"
@@ -18,6 +20,7 @@ import (
 func TestDWNAPI(t *testing.T) {
 	t.Run("Test DWN Publish Manifest", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -26,8 +29,8 @@ func TestDWNAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestRouter, manifestService := testManifest(tt, bolt, keyStoreService, credentialService)
 		dwnService := testDWNRouter(tt, bolt, keyStoreService, manifestService)
 
@@ -35,7 +38,7 @@ func TestDWNAPI(t *testing.T) {
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
diff --git a/pkg/server/server_keystore_test.go b/pkg/server/server_keystore_test.go
index bfb5d64..6357042 100644
--- a/pkg/server/server_keystore_test.go
+++ b/pkg/server/server_keystore_test.go
@@ -11,6 +11,7 @@ import (
 	"github.com/goccy/go-json"
 	"github.com/mr-tron/base58"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
@@ -19,6 +20,7 @@ import (
 func TestKeyStoreAPI(t *testing.T) {
 	t.Run("Test Store Key", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -68,6 +70,7 @@ func TestKeyStoreAPI(t *testing.T) {
 
 	t.Run("Test Get Key Details", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
diff --git a/pkg/server/server_manifest_test.go b/pkg/server/server_manifest_test.go
index c85387c..9f21242 100644
--- a/pkg/server/server_manifest_test.go
+++ b/pkg/server/server_manifest_test.go
@@ -8,8 +8,10 @@ import (
 	"testing"
 
 	"github.com/TBD54566975/ssi-sdk/crypto"
+	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	"github.com/goccy/go-json"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
 	"github.com/tbd54566975/ssi-service/pkg/service/did"
@@ -19,6 +21,7 @@ import (
 func TestManifestAPI(t *testing.T) {
 	t.Run("Test Create Manifest", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -27,8 +30,8 @@ func TestManifestAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 
 		// missing required field: Manifest
@@ -47,7 +50,7 @@ func TestManifestAPI(t *testing.T) {
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -73,6 +76,7 @@ func TestManifestAPI(t *testing.T) {
 
 	t.Run("Test Get Manifest By ID", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -81,8 +85,8 @@ func TestManifestAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 
 		w := httptest.NewRecorder()
@@ -107,7 +111,7 @@ func TestManifestAPI(t *testing.T) {
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -139,6 +143,7 @@ func TestManifestAPI(t *testing.T) {
 
 	t.Run("Test Get Manifests", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -147,15 +152,15 @@ func TestManifestAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 
 		w := httptest.NewRecorder()
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -188,6 +193,7 @@ func TestManifestAPI(t *testing.T) {
 
 	t.Run("Test Delete Manifest", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -196,13 +202,13 @@ func TestManifestAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -253,6 +259,7 @@ func TestManifestAPI(t *testing.T) {
 
 	t.Run("Test Submit Application", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -261,8 +268,8 @@ func TestManifestAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 
@@ -284,7 +291,7 @@ func TestManifestAPI(t *testing.T) {
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -326,6 +333,7 @@ func TestManifestAPI(t *testing.T) {
 
 	t.Run("Test Get Application By ID and Get Applications", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -334,8 +342,8 @@ func TestManifestAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 		w := httptest.NewRecorder()
 
@@ -350,7 +358,7 @@ func TestManifestAPI(t *testing.T) {
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
@@ -429,6 +437,7 @@ func TestManifestAPI(t *testing.T) {
 
 	t.Run("Test Delete Application", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -437,13 +446,13 @@ func TestManifestAPI(t *testing.T) {
 		})
 
 		keyStoreService := testKeyStoreService(tt, bolt)
-		credentialService := testCredentialService(tt, bolt, keyStoreService)
 		didService := testDIDService(tt, bolt, keyStoreService)
+		credentialService := testCredentialService(tt, bolt, keyStoreService, didService)
 		manifestRouter, _ := testManifest(tt, bolt, keyStoreService, credentialService)
 
 		// create an issuer
 		issuerDIDDoc, err := didService.CreateDIDByMethod(did.CreateDIDRequest{
-			Method:  did.KeyMethod,
+			Method:  didsdk.KeyMethod,
 			KeyType: crypto.Ed25519,
 		})
 		assert.NoError(tt, err)
diff --git a/pkg/server/server_schema_test.go b/pkg/server/server_schema_test.go
index deb0d00..60c4adb 100644
--- a/pkg/server/server_schema_test.go
+++ b/pkg/server/server_schema_test.go
@@ -9,6 +9,7 @@ import (
 
 	"github.com/goccy/go-json"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 
 	"github.com/tbd54566975/ssi-service/pkg/server/router"
 	"github.com/tbd54566975/ssi-service/pkg/storage"
@@ -17,6 +18,7 @@ import (
 func TestSchemaAPI(t *testing.T) {
 	t.Run("Test Create Schema", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
@@ -65,6 +67,7 @@ func TestSchemaAPI(t *testing.T) {
 
 	t.Run("Test Get Schemas", func(tt *testing.T) {
 		bolt, err := storage.NewBoltDB()
+		require.NoError(tt, err)
 
 		// remove the db file after the test
 		tt.Cleanup(func() {
diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go
index e42eae7..95a8a21 100644
--- a/pkg/server/server_test.go
+++ b/pkg/server/server_test.go
@@ -217,6 +217,7 @@ func testDIDService(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Servi
 	serviceConfig := config.DIDServiceConfig{
 		BaseServiceConfig: &config.BaseServiceConfig{Name: "test-did"},
 		Methods:           []string{"key"},
+		ResolutionMethods: []string{"key"},
 	}
 
 	// create a did service
@@ -253,18 +254,18 @@ func testSchemaRouter(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Ser
 	return schemaRouter
 }
 
-func testCredentialService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service) *credential.Service {
+func testCredentialService(t *testing.T, db *storage.BoltDB, keyStore *keystore.Service, did *did.Service) *credential.Service {
 	serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential"}}
 
 	// create a credential service
-	credentialService, err := credential.NewCredentialService(serviceConfig, db, keyStore)
+	credentialService, err := credential.NewCredentialService(serviceConfig, db, keyStore, did.GetResolver())
 	require.NoError(t, err)
 	require.NotEmpty(t, credentialService)
 	return credentialService
 }
 
-func testCredentialRouter(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Service) *router.CredentialRouter {
-	credentialService := testCredentialService(t, bolt, keyStore)
+func testCredentialRouter(t *testing.T, bolt *storage.BoltDB, keyStore *keystore.Service, did *did.Service) *router.CredentialRouter {
+	credentialService := testCredentialService(t, bolt, keyStore, did)
 
 	// create router for service
 	credentialRouter, err := router.NewCredentialRouter(credentialService)