Skip to content

Commit

Permalink
Refactor VerifiableCredentials function to include nonce
Browse files Browse the repository at this point in the history
The VerifiableCredentials function has been revised to involve the nonce from token response in IAM authentication process. The nonce, which is used in the JWT proof of possession, was previously not included in the VerifiableCredentials function. Necessary updates were also made in related test cases to pass the appropriate nonce.
  • Loading branch information
rolandgroen committed Apr 9, 2024
1 parent b527b02 commit bdbe5ca
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 17 deletions.
2 changes: 1 addition & 1 deletion auth/api/iam/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,7 @@ func (r Wrapper) CallbackOid4vciCredentialIssuance(ctx context.Context, request
log.Logger().WithError(err).Errorf("error while fetching the access_token from endpoint: %s", tokenEndpoint)
return nil, withCallbackURI(oauthError(oauth.AccessDenied, fmt.Sprintf("error while fetching the access_token from endpoint: %s, error : %s", tokenEndpoint, err.Error())), oid4vciSession.remoteRedirectUri())
}
credentials, err := r.auth.IAMClient().VerifiableCredentials(ctx, credentialEndpoint, response.AccessToken, *holderDid, *issuerDid)
credentials, err := r.auth.IAMClient().VerifiableCredentials(ctx, credentialEndpoint, response.AccessToken, response.CNonce, *holderDid, *issuerDid)
if err != nil {
log.Logger().WithError(err).Errorf("error while fetching the credential from endpoint: %s", credentialEndpoint)
return nil, withCallbackURI(oauthError(oauth.ServerError, fmt.Sprintf("error while fetching the credential from endpoint %s, error : %s", credentialEndpoint, err.Error())), oid4vciSession.remoteRedirectUri())
Expand Down
8 changes: 5 additions & 3 deletions auth/api/iam/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ func TestWrapper_CallbackOid4vciCredentialIssuance(t *testing.T) {
redirectURI := "https://test.test/iam/123/cb"
authServer := "https://auth.server"
tokenEndpoint := authServer + "/token"
cNonce := crypto.GenerateNonce()
credEndpoint := authServer + "/credz"
pkceParams := generatePKCEParams()
code := "code"
Expand All @@ -1088,6 +1089,7 @@ func TestWrapper_CallbackOid4vciCredentialIssuance(t *testing.T) {
tokenResponse := oauth.Oid4vciTokenResponse{
AccessToken: accessToken,
TokenType: "Bearer",
CNonce: &cNonce,
}
credentialResponse := iam.CredentialResponse{
Format: "jwt_vc",
Expand All @@ -1097,7 +1099,7 @@ func TestWrapper_CallbackOid4vciCredentialIssuance(t *testing.T) {
ctx := newTestClient(t)
ctx.client.storageEngine.GetSessionDatabase().GetStore(15*time.Minute, "oid4vci").Put(state, &session)
ctx.iamClient.EXPECT().AccessTokenOid4vci(nil, holderDID.String(), tokenEndpoint, redirectURI, code, &pkceParams.Verifier).Return(&tokenResponse, nil)
ctx.iamClient.EXPECT().VerifiableCredentials(nil, credEndpoint, accessToken, holderDID, issuerDID).Return(&credentialResponse, nil)
ctx.iamClient.EXPECT().VerifiableCredentials(nil, credEndpoint, accessToken, &cNonce, holderDID, issuerDID).Return(&credentialResponse, nil)
ctx.vcVerifier.EXPECT().Verify(*verifiableCredential, true, true, nil)
ctx.wallet.EXPECT().Put(nil, *verifiableCredential)

Expand Down Expand Up @@ -1163,7 +1165,7 @@ func TestWrapper_CallbackOid4vciCredentialIssuance(t *testing.T) {
ctx := newTestClient(t)
ctx.client.storageEngine.GetSessionDatabase().GetStore(15*time.Minute, "oid4vci").Put(state, &session)
ctx.iamClient.EXPECT().AccessTokenOid4vci(nil, holderDID.String(), tokenEndpoint, redirectURI, code, &pkceParams.Verifier).Return(&tokenResponse, nil)
ctx.iamClient.EXPECT().VerifiableCredentials(nil, credEndpoint, accessToken, holderDID, issuerDID).Return(nil, errors.New("FAIL"))
ctx.iamClient.EXPECT().VerifiableCredentials(nil, credEndpoint, accessToken, &cNonce, holderDID, issuerDID).Return(nil, errors.New("FAIL"))

callback, err := ctx.client.CallbackOid4vciCredentialIssuance(nil, CallbackOid4vciCredentialIssuanceRequestObject{
Params: CallbackOid4vciCredentialIssuanceParams{
Expand All @@ -1180,7 +1182,7 @@ func TestWrapper_CallbackOid4vciCredentialIssuance(t *testing.T) {
ctx := newTestClient(t)
ctx.client.storageEngine.GetSessionDatabase().GetStore(15*time.Minute, "oid4vci").Put(state, &session)
ctx.iamClient.EXPECT().AccessTokenOid4vci(nil, holderDID.String(), tokenEndpoint, redirectURI, code, &pkceParams.Verifier).Return(&tokenResponse, nil)
ctx.iamClient.EXPECT().VerifiableCredentials(nil, credEndpoint, accessToken, holderDID, issuerDID).Return(&credentialResponse, nil)
ctx.iamClient.EXPECT().VerifiableCredentials(nil, credEndpoint, accessToken, &cNonce, holderDID, issuerDID).Return(&credentialResponse, nil)
ctx.vcVerifier.EXPECT().Verify(*verifiableCredential, true, true, nil).Return(errors.New("FAIL"))

callback, err := ctx.client.CallbackOid4vciCredentialIssuance(nil, CallbackOid4vciCredentialIssuanceRequestObject{
Expand Down
2 changes: 1 addition & 1 deletion auth/client/iam/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type Client interface {

AccessTokenOid4vci(ctx context.Context, clientId string, tokenEndpoint string, redirectUri string, code string, pkceCodeVerifier *string) (*oauth.Oid4vciTokenResponse, error)

VerifiableCredentials(ctx context.Context, credentialEndpoint string, accessToken string, holderDid did.DID, audienceDid did.DID) (*CredentialResponse, error)
VerifiableCredentials(ctx context.Context, credentialEndpoint string, accessToken string, cNonce *string, holderDid did.DID, audienceDid did.DID) (*CredentialResponse, error)
}

// RequestModifier is a function that modifies the claims/params of a unsigned or signed request (JWT)
Expand Down
8 changes: 4 additions & 4 deletions auth/client/iam/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions auth/client/iam/openid4vp.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func (c *OpenID4VPClient) AccessTokenOid4vci(ctx context.Context, clientId strin
return rsp, nil
}

func (c *OpenID4VPClient) proofJwt(ctx context.Context, holderDid did.DID, audienceDid did.DID) (string, error) {
func (c *OpenID4VPClient) proofJwt(ctx context.Context, holderDid did.DID, audienceDid did.DID, nonce *string) (string, error) {
kid, _, err := c.keyResolver.ResolveKey(holderDid, nil, resolver.NutsSigningKeyType)
if err != nil {
return "", fmt.Errorf("failed to resolve key for did (%s): %w", holderDid.String(), err)
Expand All @@ -318,14 +318,18 @@ func (c *OpenID4VPClient) proofJwt(ctx context.Context, holderDid did.DID, audie
"aud": audienceDid.String(),
"jti": jti.String(),
}
if nonce != nil {
claims["nonce"] = nonce
}
proofJwt, err := c.jwtSigner.SignJWT(ctx, claims, nil, kid.String())
if err != nil {
return "", fmt.Errorf("failed to sign the JWT with kid (%s): %w", kid.String(), err)
}
return proofJwt, nil
}
func (c *OpenID4VPClient) VerifiableCredentials(ctx context.Context, credentialEndpoint string, accessToken string, holderDid did.DID, audienceDid did.DID) (*CredentialResponse, error) {
proofJwt, err := c.proofJwt(ctx, holderDid, audienceDid)
func (c *OpenID4VPClient) VerifiableCredentials(ctx context.Context, credentialEndpoint string, accessToken string, cNonce *string, holderDid did.DID, audienceDid did.DID) (*CredentialResponse, error) {
// The cNonce becomes the nonce in the JWT proof of possession.
proofJwt, err := c.proofJwt(ctx, holderDid, audienceDid, cNonce)
if err != nil {
return nil, err
}
Expand Down
11 changes: 6 additions & 5 deletions auth/client/iam/openid4vp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ func TestIAMClient_AccessTokenOid4vci(t *testing.T) {
func TestIAMClient_VerifiableCredentials(t *testing.T) {
walletDID := did.MustParseDID("did:web:test.test:iam:123")
accessToken := "code"
cNonce := crypto.GenerateNonce()

t.Run("ok", func(t *testing.T) {
keyId := walletDID.URI()
Expand All @@ -651,7 +652,7 @@ func TestIAMClient_VerifiableCredentials(t *testing.T) {
return "signed JWT", nil
})

response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, walletDID, ctx.issuerDID)
response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, &cNonce, walletDID, ctx.issuerDID)

require.NoError(t, err)
require.NotNil(t, response)
Expand All @@ -676,7 +677,7 @@ func TestIAMClient_VerifiableCredentials(t *testing.T) {

ctx.credentials = nil

response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, walletDID, ctx.issuerDID)
response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, &cNonce, walletDID, ctx.issuerDID)

assert.EqualError(t, err, "remote server: failed to retrieve credentials: server returned HTTP 404 (expected: 200)")
assert.Nil(t, response)
Expand Down Expand Up @@ -704,7 +705,7 @@ func TestIAMClient_VerifiableCredentials(t *testing.T) {
return
}

response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, walletDID, ctx.issuerDID)
response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, &cNonce, walletDID, ctx.issuerDID)

assert.Error(t, err)
assert.Nil(t, response)
Expand All @@ -719,7 +720,7 @@ func TestIAMClient_VerifiableCredentials(t *testing.T) {

ctx.credentials = nil

response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, walletDID, ctx.issuerDID)
response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, &cNonce, walletDID, ctx.issuerDID)

assert.EqualError(t, err, "failed to resolve key for did (did:web:test.test:iam:123): "+resolver.ErrKeyNotFound.Error())
assert.Nil(t, response)
Expand All @@ -736,7 +737,7 @@ func TestIAMClient_VerifiableCredentials(t *testing.T) {
return "", errors.New("signature failed")
})

response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, walletDID, ctx.issuerDID)
response, err := ctx.client.VerifiableCredentials(context.Background(), ctx.openIDCredentialIssuerMetadata.CredentialEndpoint, accessToken, &cNonce, walletDID, ctx.issuerDID)

assert.EqualError(t, err, "failed to sign the JWT with kid (did:web:test.test:iam:123#1): signature failed")
assert.Nil(t, response)
Expand Down

0 comments on commit bdbe5ca

Please sign in to comment.