Skip to content

Commit

Permalink
chore: add new fields to create auth credential endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
martinsaporiti committed Dec 19, 2024
1 parent 5cc03a5 commit ea318ed
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 49 deletions.
34 changes: 24 additions & 10 deletions api/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -444,14 +444,7 @@ paths:
content:
application/json:
schema:
type: object
required:
- keyID
properties:
keyID:
type: string
x-omitempty: false
example: ZGlkOnBvbHlnb25pZDpwb2x5Z29uOmFtb3k6MnFRNjhKa1JjZjN5cXBYanRqVVQ3WjdVeW1TV0hzYll
$ref: '#/components/schemas/CreateAuthCredentialRequest'
responses:
'201':
description: Key added successfully
Expand All @@ -461,6 +454,7 @@ paths:
type: object
required:
- id
- credentialStatusType
properties:
id:
type: string
Expand All @@ -470,7 +464,6 @@ paths:
name: uuid
path: github.com/google/uuid
example: 8edd8112-c415-11ed-b036-debe37e1cbd6

'400':
$ref: '#/components/responses/400'
'500':
Expand Down Expand Up @@ -2761,7 +2754,28 @@ components:
meta:
$ref: '#/components/schemas/PaginatedMetadata'


CreateAuthCredentialRequest:
type: object
required: [keyID, credentialStatusType]
properties:
keyID:
type: string
x-omitempty: false
example: ZGlkOnBvbHlnb25pZDpwb2x5Z29uOmFtb3k6MnFRNjhKa1JjZjN5cXBYanRqVVQ3WjdVeW1TV0hzYll
expiration:
type: integer
format: int64
version:
type: integer
format: uint32
revNonce:
type: integer
format: uint64
credentialStatusType:
type: string
x-omitempty: true
example: "Iden3ReverseSparseMerkleTreeProof"
enum: [ Iden3commRevocationStatusV1.0, Iden3ReverseSparseMerkleTreeProof, Iden3OnchainSparseMerkleTreeProof2023 ]

parameters:
credentialStatusType:
Expand Down
32 changes: 23 additions & 9 deletions internal/api/api.gen.go

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

30 changes: 29 additions & 1 deletion internal/api/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"math/big"
"slices"
"strings"
"time"

core "github.com/iden3/go-iden3-core/v2"
"github.com/iden3/go-iden3-core/v2/w3c"
Expand Down Expand Up @@ -318,6 +319,7 @@ func (s *Server) GetIdentityDetails(ctx context.Context, request GetIdentityDeta

// CreateAuthCredential is the controller to create an auth credential
func (s *Server) CreateAuthCredential(ctx context.Context, request CreateAuthCredentialRequestObject) (CreateAuthCredentialResponseObject, error) {
did := request.Identifier.w3cDID
decodedKeyID, err := b64.StdEncoding.DecodeString(request.Body.KeyID)
if err != nil {
log.Error(ctx, "decoding base64 key id", "err", err)
Expand All @@ -328,7 +330,33 @@ func (s *Server) CreateAuthCredential(ctx context.Context, request CreateAuthCre
}, nil
}

autCredentialID, err := s.identityService.CreateAuthCredential(ctx, request.Identifier.w3cDID, string(decodedKeyID))
var credentialStatusType *verifiable.CredentialStatusType
credentialStatusType, err = validateStatusType(common.ToPointer(string(request.Body.CredentialStatusType)))
if err != nil {
return CreateAuthCredential400JSONResponse{N400JSONResponse{Message: err.Error()}}, nil
}

resolverPrefix, err := common.ResolverPrefix(did)
if err != nil {
return CreateAuthCredential400JSONResponse{N400JSONResponse{Message: "error parsing did"}}, nil
}

rhsSettings, err := s.networkResolver.GetRhsSettings(ctx, resolverPrefix)
if err != nil {
return CreateAuthCredential400JSONResponse{N400JSONResponse{Message: "error getting reverse hash service settings"}}, nil
}

if !s.networkResolver.IsCredentialStatusTypeSupported(rhsSettings.Mode, *credentialStatusType) {
log.Warn(ctx, "unsupported credential status type", "req", request)
return CreateAuthCredential400JSONResponse{N400JSONResponse{Message: fmt.Sprintf("Credential Status Type '%s' is not supported by the issuer", *credentialStatusType)}}, nil
}

var expiration *time.Time
if request.Body.Expiration != nil {
expiration = common.ToPointer(time.Unix(*request.Body.Expiration, 0))
}

autCredentialID, err := s.identityService.CreateAuthCredential(ctx, request.Identifier.w3cDID, string(decodedKeyID), request.Body.RevNonce, expiration, request.Body.Version, *credentialStatusType)
if err != nil {
log.Error(ctx, "creating auth credential", "err", err)
if errors.Is(err, services.ErrDuplicatedHash) {
Expand Down
141 changes: 139 additions & 2 deletions internal/api/identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
"testing"
"time"

"github.com/google/uuid"
core "github.com/iden3/go-iden3-core/v2"
"github.com/iden3/go-iden3-core/v2/w3c"
"github.com/iden3/go-schema-processor/v2/verifiable"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -18,6 +20,7 @@ import (
"github.com/polygonid/sh-id-platform/internal/core/domain"
"github.com/polygonid/sh-id-platform/internal/core/ports"
"github.com/polygonid/sh-id-platform/internal/db/tests"
"github.com/polygonid/sh-id-platform/internal/kms"
"github.com/polygonid/sh-id-platform/internal/repositories"
)

Expand Down Expand Up @@ -95,8 +98,7 @@ func TestServer_CreateIdentity(t *testing.T) {
Method string `json:"method"`
Network string `json:"network"`
Type CreateIdentityRequestDidMetadataType `json:"type"`
}{Blockchain: blockchain, Method: method, Network: network, Type: BJJ}, DisplayName: common.ToPointer("blockchain display name"),
CredentialStatusType: &authBJJCredentialStatus,
}{Blockchain: blockchain, Method: method, Network: network, Type: BJJ}, DisplayName: common.ToPointer("blockchain display name"), CredentialStatusType: &authBJJCredentialStatus,
},
expected: expected{
httpCode: 201,
Expand Down Expand Up @@ -482,3 +484,138 @@ func TestServer_UpdateIdentity(t *testing.T) {
})
}
}

func TestServer_CreateAuthCredential(t *testing.T) {
const (
method = "polygonid"
blockchain = "polygon"
network = "amoy"
BJJ = "BJJ"
)
ctx := context.Background()

server := newTestServer(t, nil)
handler := getHandler(ctx, server)

iden, err := server.Services.identity.Create(ctx, "http://polygon-test", &ports.DIDCreationOptions{Method: method, Blockchain: blockchain, Network: network, KeyType: BJJ})
require.NoError(t, err)
did := iden.Identifier

issuerDID, err := w3c.ParseDID(did)
require.NoError(t, err)

t.Run("no auth header", func(t *testing.T) {
rr := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/v2/identities/%s/create-auth-credential", did), tests.JSONBody(t, nil))
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusUnauthorized, rr.Code)
})

t.Run("should create an auth credential with default values", func(t *testing.T) {
key, err := server.keyService.Create(ctx, issuerDID, kms.KeyTypeBabyJubJub, uuid.New().String())
require.NoError(t, err)

body := CreateAuthCredentialRequest{
KeyID: key.ID,
CredentialStatusType: CreateAuthCredentialRequestCredentialStatusType(verifiable.Iden3commRevocationStatusV1),
}
rr := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/v2/identities/%s/create-auth-credential", did), tests.JSONBody(t, body))
req.SetBasicAuth(authOk())
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusCreated, rr.Code)
var response CreateAuthCredential201JSONResponse
assert.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response))
assert.NotNil(t, response.Id)
})

t.Run("should get an error - duplicated auth credential", func(t *testing.T) {
key, err := server.keyService.Create(ctx, issuerDID, kms.KeyTypeBabyJubJub, uuid.New().String())
require.NoError(t, err)

body := CreateAuthCredentialRequest{
KeyID: key.ID,
CredentialStatusType: CreateAuthCredentialRequestCredentialStatusType(verifiable.Iden3commRevocationStatusV1),
}
rr := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/v2/identities/%s/create-auth-credential", did), tests.JSONBody(t, body))
req.SetBasicAuth(authOk())
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusCreated, rr.Code)

body = CreateAuthCredentialRequest{
KeyID: key.ID,
CredentialStatusType: CreateAuthCredentialRequestCredentialStatusType(verifiable.Iden3commRevocationStatusV1),
}
rr = httptest.NewRecorder()
req, err = http.NewRequest("POST", fmt.Sprintf("/v2/identities/%s/create-auth-credential", did), tests.JSONBody(t, body))
req.SetBasicAuth(authOk())
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusBadRequest, rr.Code)
var response CreateAuthCredential400JSONResponse
assert.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response))
assert.Equal(t, "hash already exists. This means an auth credential was already created with this key", response.Message)
})

t.Run("should get an error - credential status type not supported", func(t *testing.T) {
key, err := server.keyService.Create(ctx, issuerDID, kms.KeyTypeBabyJubJub, uuid.New().String())
require.NoError(t, err)

body := CreateAuthCredentialRequest{
KeyID: key.ID,
CredentialStatusType: CreateAuthCredentialRequestCredentialStatusType(verifiable.Iden3OnchainSparseMerkleTreeProof2023),
}
rr := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/v2/identities/%s/create-auth-credential", did), tests.JSONBody(t, body))
req.SetBasicAuth(authOk())
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusBadRequest, rr.Code)
var response CreateAuthCredential400JSONResponse
assert.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response))
assert.Equal(t, "Credential Status Type 'Iden3OnchainSparseMerkleTreeProof2023' is not supported by the issuer", response.Message)
})

t.Run("should create an auth credential", func(t *testing.T) {
key, err := server.keyService.Create(ctx, issuerDID, kms.KeyTypeBabyJubJub, uuid.New().String())
require.NoError(t, err)

revNonce, err := common.RandInt64()
require.NoError(t, err)
body := CreateAuthCredentialRequest{
KeyID: key.ID,
CredentialStatusType: CreateAuthCredentialRequestCredentialStatusType(verifiable.Iden3commRevocationStatusV1),
RevNonce: &revNonce,
Version: common.ToPointer[uint32](1),
Expiration: common.ToPointer[int64](1829174400),
}
rr := httptest.NewRecorder()
req, err := http.NewRequest("POST", fmt.Sprintf("/v2/identities/%s/create-auth-credential", did), tests.JSONBody(t, body))
req.SetBasicAuth(authOk())
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusCreated, rr.Code)
var response CreateAuthCredential201JSONResponse
assert.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response))
assert.NotNil(t, response.Id)

rr = httptest.NewRecorder()
req, err = http.NewRequest("GET", fmt.Sprintf("/v2/identities/%s/credentials/%s", did, response.Id), tests.JSONBody(t, nil))
req.SetBasicAuth(authOk())
require.NoError(t, err)
handler.ServeHTTP(rr, req)
require.Equal(t, http.StatusOK, rr.Code)
var response2 GetCredential200JSONResponse
assert.NoError(t, json.Unmarshal(rr.Body.Bytes(), &response2))
assert.NotNil(t, response2.Id)
status, ok := response2.Vc.CredentialStatus.(map[string]any)
require.True(t, ok)
assert.Equal(t, float64(revNonce), status["revocationNonce"])
assert.Equal(t, string(verifiable.Iden3commRevocationStatusV1), status["type"])
assert.Equal(t, *response2.Vc.Expiration, time.Unix(1829174400, 0))
})
}
3 changes: 2 additions & 1 deletion internal/api/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/iden3/go-iden3-core/v2/w3c"
"github.com/iden3/go-schema-processor/v2/verifiable"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -457,7 +458,7 @@ func TestServer_DeleteKey(t *testing.T) {
keyIDForAuthCoreClaimIDASByteArr, err := b64.StdEncoding.DecodeString(keyIDForAuthCoreClaimID.ID)
require.NoError(t, err)

_, err = server.Services.identity.CreateAuthCredential(ctx, did, string(keyIDForAuthCoreClaimIDASByteArr))
_, err = server.Services.identity.CreateAuthCredential(ctx, did, string(keyIDForAuthCoreClaimIDASByteArr), nil, nil, nil, verifiable.Iden3commRevocationStatusV1)
require.NoError(t, err)

handler := getHandler(ctx, server)
Expand Down
3 changes: 2 additions & 1 deletion internal/core/ports/identity_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ports

import (
"context"
"time"

"github.com/google/uuid"
core "github.com/iden3/go-iden3-core/v2"
Expand Down Expand Up @@ -58,5 +59,5 @@ type IdentityService interface {
GetFailedState(ctx context.Context, identifier w3c.DID) (*domain.IdentityState, error)
PublishGenesisStateToRHS(ctx context.Context, did *w3c.DID) error
UpdateIdentityDisplayName(ctx context.Context, did w3c.DID, displayName string) error
CreateAuthCredential(ctx context.Context, did *w3c.DID, keyID string) (uuid.UUID, error)
CreateAuthCredential(ctx context.Context, did *w3c.DID, keyID string, revNonce *uint64, expiration *time.Time, version *uint32, credentialStatusType verifiable.CredentialStatusType) (uuid.UUID, error)
}
Loading

0 comments on commit ea318ed

Please sign in to comment.