Skip to content

Commit

Permalink
Merge pull request #1716 from skynet2/compose-no-template
Browse files Browse the repository at this point in the history
feat: compose no template
  • Loading branch information
fqutishat committed May 2, 2024
2 parents c37a16a + efc1cb6 commit 77d04a8
Show file tree
Hide file tree
Showing 12 changed files with 548 additions and 221 deletions.
17 changes: 8 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
runs-on: ubuntu-20.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v2
with:
Expand All @@ -43,24 +43,23 @@ jobs:
runs-on: ubuntu-20.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: '${{ env.GO_VERSION }}'
- name: Run unit tests
run: make unit-test
- name: Upload coverage to Codecov
run: |
bash <(curl https://codecov.io/bash)
env:
CODECOV_UPLOAD_TOKEN: ${{ secrets.CODECOV_UPLOAD_TOKEN }}
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_UPLOAD_TOKEN }}
verbose: true

BDDTest:
runs-on: ubuntu-20.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v2
with:
Expand Down Expand Up @@ -93,7 +92,7 @@ jobs:
with:
go-version: '1.21'

- uses: actions/checkout@v2
- uses: actions/checkout@v4
with: { fetch-depth: 0 }

- name: Get current published version
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Setup Go 1.21
uses: actions/setup-go@v2
Expand Down
386 changes: 193 additions & 193 deletions api/spec/openapi.gen.go

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions docs/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2254,9 +2254,6 @@ components:
type: object
description: Raw Complete credential for sign and customization
nullable: false
credential_template_id:
type: string
description: 'Template of the credential to be issued while successfully concluding this interaction. REQUIRED, if the profile is configured to use multiple credential templates.'
credential_expires_at:
type: string
format: date-time
Expand Down
3 changes: 1 addition & 2 deletions pkg/restapi/v1/issuer/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,7 @@ func (c *Controller) InitiateCredentialComposeIssuance(e echo.Context, profileID
OverrideIssuer: compose.CredentialOverrideIssuer,
OverrideSubjectDid: compose.CredentialOverrideSubjectDid,
},
CredentialExpiresAt: compose.CredentialExpiresAt,
CredentialTemplateId: compose.CredentialTemplateId,
CredentialExpiresAt: compose.CredentialExpiresAt,
})
}

Expand Down
3 changes: 0 additions & 3 deletions pkg/restapi/v1/issuer/openapi.gen.go

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

41 changes: 34 additions & 7 deletions pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,21 @@ func (s *Service) newTxCredentialConf(
return nil, err
}

credentialTemplate, err := findCredentialTemplate(credentialConfiguration.CredentialTemplateID, profile)
if err != nil {
return nil, err
var targetCredentialTemplate *profileapi.CredentialTemplate

if credentialConfiguration.CredentialTemplateID == "" &&
credentialConfiguration.ComposeCredential != nil &&
credentialConfiguration.ComposeCredential.Credential != nil {
targetCredentialTemplate = s.buildVirtualTemplate(&credentialConfiguration)
} else {
targetCredentialTemplate, err = findCredentialTemplate(credentialConfiguration.CredentialTemplateID, profile)
if err != nil {
return nil, err
}
}

credentialConfigurationID, _, err := findCredentialConfigurationID(
credentialTemplate.ID, credentialTemplate.Type, profile)
targetCredentialTemplate.ID, targetCredentialTemplate.Type, profile)
if err != nil {
return nil, err
}
Expand All @@ -198,13 +206,13 @@ func (s *Service) newTxCredentialConf(

txCredentialConfiguration := &TxCredentialConfiguration{
ID: uuid.NewString(),
CredentialTemplate: credentialTemplate,
CredentialTemplate: targetCredentialTemplate,
OIDCCredentialFormat: metaCredentialConfiguration.Format,
ClaimEndpoint: credentialConfiguration.ClaimEndpoint,
CredentialName: credentialConfiguration.CredentialName,
CredentialDescription: credentialConfiguration.CredentialDescription,
CredentialExpiresAt: lo.ToPtr(
s.GetCredentialsExpirationTime(credentialConfiguration.CredentialExpiresAt, credentialTemplate)),
s.GetCredentialsExpirationTime(credentialConfiguration.CredentialExpiresAt, targetCredentialTemplate)),
CredentialConfigurationID: credentialConfigurationID,
ClaimDataID: "",
PreAuthCodeExpiresAt: nil,
Expand All @@ -216,7 +224,7 @@ func (s *Service) newTxCredentialConf(
ctx,
profile.DataConfig.ClaimDataTTL,
credentialConfiguration,
credentialTemplate,
targetCredentialTemplate,
txCredentialConfiguration,
)
if err != nil {
Expand All @@ -227,6 +235,25 @@ func (s *Service) newTxCredentialConf(
return txCredentialConfiguration, nil
}

func (s *Service) buildVirtualTemplate(req *InitiateIssuanceCredentialConfiguration) *profileapi.CredentialTemplate {
result := &profileapi.CredentialTemplate{
ID: fmt.Sprintf("virtual_%s", uuid.NewString()),
}

if req.ComposeCredential.Credential != nil {
types := (*req.ComposeCredential.Credential)["type"]

if v, ok := types.([]interface{}); ok && len(v) > 0 {
targetType, targetOk := v[len(v)-1].(string)
if targetOk {
result.Type = targetType
}
}
}

return result
}

func (s *Service) applyPreAuthFlowModifications(
ctx context.Context,
profileClaimDataTTLSec int32,
Expand Down
120 changes: 120 additions & 0 deletions pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const (
//go:embed testdata/issuer_profile.json
var profileJSON []byte

//go:embed testdata/issuer_profile_without_template.json
var profileWithoutTemplateJSON []byte

type mocks struct {
transactionStore *MockTransactionStore
wellKnownService *MockWellKnownService
Expand Down Expand Up @@ -815,6 +818,123 @@ func TestService_InitiateIssuance(t *testing.T) {
resp.InitiateIssuanceURL)
},
},
{
name: "Success Compose feature with no template",
setup: func(mocks *mocks) {
initialOpState := "eyJhbGciOiJSU0Et"
expectedCode := "super-secret-pre-auth-code"
claimData := degreeClaims

var tempProfile *profileapi.Issuer
require.NoError(t, json.Unmarshal(profileWithoutTemplateJSON, &tempProfile)) // hack profile ref
profile = tempProfile

mocks.transactionStore.EXPECT().Create(gomock.Any(), int32(0), gomock.Any()).
DoAndReturn(func(
ctx context.Context,
profileTransactionDataTTL int32,
data *oidc4ci.TransactionData,
) (*oidc4ci.Transaction, error) {
assert.NotEqual(t, data.OpState, initialOpState)
assert.Equal(t, data.OpState, data.PreAuthCode)
assert.Empty(t, data.UserPin)
assert.Equal(t, true, data.IsPreAuthFlow)

configuration := data.CredentialConfiguration[0]

assert.NotEmpty(t, configuration.ClaimDataID)
assert.EqualValues(t, oidc4ci.ClaimDataTypeVC, configuration.ClaimDataType)

assert.EqualValues(t, "some-template",
configuration.CredentialComposeConfiguration.IDTemplate)

assert.True(t, configuration.CredentialComposeConfiguration.OverrideIssuer)

return &oidc4ci.Transaction{
ID: "txID",
TransactionData: oidc4ci.TransactionData{
ProfileID: profile.ID,
PreAuthCode: expectedCode,
IsPreAuthFlow: true,
CredentialConfiguration: []*oidc4ci.TxCredentialConfiguration{
{
OIDCCredentialFormat: verifiable.JwtVCJsonLD,
CredentialTemplate: &profileapi.CredentialTemplate{
ID: "templateID",
},
CredentialConfigurationID: "PermanentResidentCardIdentifier",
},
},
},
}, nil
})

chunks := &dataprotect.EncryptedData{
Encrypted: []byte{0x1, 0x2, 0x3},
EncryptedNonce: []byte{0x0, 0x2},
}

mocks.crypto.EXPECT().Encrypt(gomock.Any(), gomock.Any()).
Return(chunks, nil)

mocks.claimDataStore.EXPECT().Create(gomock.Any(), int32(0), gomock.Any()).Return("claimDataID", nil)

mocks.eventService.EXPECT().Publish(gomock.Any(), spi.IssuerEventTopic, gomock.Any()).
DoAndReturn(func(ctx context.Context, topic string, messages ...*spi.Event) error {
assert.Len(t, messages, 1)
assert.Equal(t, messages[0].Type, spi.IssuerOIDCInteractionInitiated)

return nil
})

mocks.wellKnownService.EXPECT().GetOIDCConfiguration(gomock.Any(), issuerWellKnownURL).Return(
&oidc4ci.IssuerIDPOIDCConfiguration{}, nil)

mocks.wellKnownService.EXPECT().GetOIDCConfiguration(gomock.Any(), walletWellKnownURL).Return(
&oidc4ci.IssuerIDPOIDCConfiguration{}, nil)

targetCred := map[string]interface{}{
"type": []string{
"VerifiableCredential",
"PermanentResidentCard",
},
"context": []string{
"https://www.w3.org/2018/credentials/examples/v1",
},
"credentialSubject": claimData,
}

targetCredBytes, err := json.Marshal(targetCred)
require.NoError(t, err)
assert.NoError(t, json.Unmarshal(targetCredBytes, &targetCred)) // just to ensure type castings

issuanceReq = &oidc4ci.InitiateIssuanceRequest{
ClientInitiateIssuanceURL: "",
ClientWellKnownURL: walletWellKnownURL,
GrantType: oidc4ci.GrantTypePreAuthorizedCode,
ResponseType: "",
Scope: []string{"openid", "profile"},
OpState: initialOpState,
UserPinRequired: false,
WalletInitiatedIssuance: false,
CredentialConfiguration: []oidc4ci.InitiateIssuanceCredentialConfiguration{
{
ComposeCredential: &oidc4ci.InitiateIssuanceComposeCredential{
Credential: &targetCred,
IDTemplate: "some-template",
OverrideIssuer: true,
},
},
},
}
},
check: func(t *testing.T, resp *oidc4ci.InitiateIssuanceResponse, err error) {
require.NoError(t, err)
assert.NotNil(t, resp.Tx)
require.Equal(t, "openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fvcs.pb.example.com%2Foidc%2Fidp%2Ftest_issuer%22%2C%22credential_configuration_ids%22%3A%5B%22PermanentResidentCardIdentifier%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22super-secret-pre-auth-code%22%7D%7D%7D", //nolint
resp.InitiateIssuanceURL)
},
},
{
name: "Success Pre-Auth without PIN and without template and empty state",
setup: func(mocks *mocks) {
Expand Down
55 changes: 55 additions & 0 deletions pkg/service/oidc4ci/testdata/issuer_profile_without_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"id": "test_issuer",
"name": "test_issuer",
"version": "1.1",
"organizationID": "test_org1",
"url": "https://issuer.example.com",
"active": true,
"signingDID": {
"did" : "did:orb:anything"
},
"vcConfig": {
"signingAlgorithm": "JsonWebSignature2020",
"signatureRepresentation": 0,
"keyType": "ECDSASecp256k1DER",
"format": "ldp",
"didMethod": "orb"
},
"oidcConfig": {
"client_id": "test_issuer_client_id",
"client_secret_handle": "test_issuer_client_secret_handle",
"issuer_well_known": "https://issuer.example.com/.well-known/openid-configuration",
"scopes_supported": [
"openid",
"profile"
],
"grant_types_supported": [
"authorization_code",
"urn:ietf:params:oauth:grant-type:pre-authorized_code"
]
},
"credentialTemplates": [],
"credentialMetadata": {
"display": [],
"credential_configurations_supported": {
"PermanentResidentCardIdentifier": {
"format": "jwt_vc_json",
"credential_definition": {
"type": [
"VerifiableCredential",
"PermanentResidentCard"
]
}
},
"UniversityDegreeCredentialIdentifier": {
"format": "jwt_vc_json",
"credential_definition": {
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
}
}
}
}
}
Loading

0 comments on commit 77d04a8

Please sign in to comment.