Skip to content

Commit

Permalink
feat: profile specific transient data configuration.
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Sizov <[email protected]>
  • Loading branch information
mishasizov-SK committed Apr 4, 2024
1 parent 2f4a18a commit b7f44f8
Show file tree
Hide file tree
Showing 50 changed files with 896 additions and 520 deletions.
377 changes: 189 additions & 188 deletions api/spec/openapi.gen.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cmd/vc-rest/startcmd/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ const (
"OIDC4CI issuance auth state store " + "(TTL configurable via " + oidc4ciAuthStateTTLEnvKey + "), " +
"OIDC4VP transaction mapping " + "(TTL configurable via " + oidc4vpNonceTTLEnvKey + "), " +
"OIDC4VP transaction data " + "(TTL configurable via " + oidc4vpTransactionDataTTLEnvKey + "), " +
"notification data " + "(TTL configurable via " + oidc4ciAckDataTTLEnvKey + "), " +
"encrypted claim data of OIDC4VP presentation transaction. " + "(TTL configurable via " + oidc4vpReceivedClaimsDataTTLEnvKey + "). " +
"Possible values are \"redis\" or \"mongo\". Default is \"mongo\". " +
commonEnvVarUsageText + transientDataStoreTypeFlagEnvKey
Expand Down
4 changes: 2 additions & 2 deletions cmd/vc-rest/startcmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -1178,14 +1178,14 @@ func getOIDC4CITransactionStore(

func getAckStore(
redisClient *redis.Client,
oidc4ciTransactionDataTTL int32,
oidc4ciAckDataTTL int32,
) *ackstore.Store {
if redisClient == nil {
logger.Warn("Redis client is not configured. Acknowledgement store will not be used")
return nil
}

return ackstore.New(redisClient, oidc4ciTransactionDataTTL)
return ackstore.New(redisClient, oidc4ciAckDataTTL)
}

func createRequestObjectStore(
Expand Down
4 changes: 4 additions & 0 deletions docs/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1412,12 +1412,16 @@ components:
tx_id:
type: string
description: Transaction ID to correlate upcoming authorization response.
profile_auth_state_ttl:
type: integer
description: Profile specific Auth state TTL.
wallet_initiated_flow:
$ref: ./common.yaml#/components/schemas/WalletInitiatedFlowData
required:
- authorization_request
- authorization_endpoint
- tx_id
- profile_auth_state_ttl
OAuthParameters:
title: OAuthParameters
x-tags:
Expand Down
17 changes: 17 additions & 0 deletions pkg/profile/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ type Issuer struct {
WebHook string `json:"webHook,omitempty"`
CredentialMetaData *CredentialMetaData `json:"credentialMetadata"`
Checks IssuanceChecks `json:"checks"`
DataConfig IssuerDataConfig `json:"dataConfig"`
}

// IssuerDataConfig stores profile specific transient data configuration.
type IssuerDataConfig struct {
ClaimDataTTL int32
OIDC4CITransactionDataTTL int32
OIDC4CIAuthStateTTL int32
OIDC4CIAckDataTTL int32
}

type CredentialMetaData struct {
Expand Down Expand Up @@ -207,6 +216,14 @@ type Verifier struct {
SigningDID *SigningDID `json:"signingDID,omitempty"`
PresentationDefinitions []*presexch.PresentationDefinition `json:"presentationDefinitions,omitempty"`
WebHook string `json:"webHook,omitempty"`
DataConfig VerifierDataConfig `json:"dataConfig"`
}

// VerifierDataConfig stores profile specific transient data configuration.
type VerifierDataConfig struct {
OIDC4VPNonceStoreDataTTL int32
OIDC4VPTransactionDataTTL int32
OIDC4VPReceivedClaimsDataTTL int32
}

// OIDC4VPConfig store config for verifier did that used to sign request object in oidc4vp process.
Expand Down
1 change: 1 addition & 0 deletions pkg/restapi/v1/issuer/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ func (c *Controller) prepareClaimDataAuthorizationRequest(
AuthorizationEndpoint: resp.AuthorizationEndpoint,
PushedAuthorizationRequestEndpoint: lo.ToPtr(resp.PushedAuthorizationRequestEndpoint),
TxId: string(resp.TxID),
ProfileAuthStateTtl: int(profile.DataConfig.OIDC4CIAuthStateTTL),
}, nil
}

Expand Down
12 changes: 11 additions & 1 deletion pkg/restapi/v1/issuer/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1383,16 +1383,26 @@ func TestController_PrepareAuthorizationRequest(t *testing.T) {
mockProfileService := NewMockProfileService(gomock.NewController(t))
mockProfileService.EXPECT().GetProfile(profileID, profileVersion).Return(&profileapi.Issuer{
OIDCConfig: &profileapi.OIDCConfig{},
DataConfig: profileapi.IssuerDataConfig{OIDC4CIAuthStateTTL: 10},
}, nil)

c := &Controller{
oidc4ciService: mockOIDC4CIService,
profileSvc: mockProfileService,
}

recorder := httptest.NewRecorder()

req := `{"response_type":"code","op_state":"123","scope":["scope1", "scope2"]}`
ctx := echoContext(withRequestBody([]byte(req)))
ctx := echoContext(withRecorder(recorder), withRequestBody([]byte(req)))
assert.NoError(t, c.PrepareAuthorizationRequest(ctx))

var prepareClaimDataAuthorizationResponse PrepareClaimDataAuthorizationResponse

err := json.NewDecoder(recorder.Body).Decode(&prepareClaimDataAuthorizationResponse)
assert.NoError(t, err)

assert.Equal(t, prepareClaimDataAuthorizationResponse.ProfileAuthStateTtl, 10)
})

t.Run("read body error", func(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 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.

3 changes: 2 additions & 1 deletion pkg/restapi/v1/oidc4ci/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ var logger = log.New("oidc4ci")
type StateStore interface {
SaveAuthorizeState(
ctx context.Context,
profileAuthStateTTL int32,
opState string,
state *oidc4ci.AuthorizeState,
params ...func(insertOptions *oidc4ci.InsertOptions),
) error

GetAuthorizeState(ctx context.Context, opState string) (*oidc4ci.AuthorizeState, error)
Expand Down Expand Up @@ -352,6 +352,7 @@ func (c *Controller) OidcAuthorize(e echo.Context, params OidcAuthorizeParams) e

if err = c.stateStore.SaveAuthorizeState(
ctx,
int32(claimDataAuth.ProfileAuthStateTtl),
lo.FromPtr(params.IssuerState),
&oidc4ci.AuthorizeState{
RedirectURI: ar.GetRedirectURI(),
Expand Down
2 changes: 1 addition & 1 deletion pkg/restapi/v1/oidc4ci/controller_e2e_flows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,9 +541,9 @@ func (s *memoryStateStore) GetAuthorizeState(

func (s *memoryStateStore) SaveAuthorizeState(
_ context.Context,
_ int32,
opState string,
state *oidc4cisrv.AuthorizeState,
_ ...func(insertOptions *oidc4cisrv.InsertOptions),
) error {
s.mu.RLock()
defer s.mu.RUnlock()
Expand Down
15 changes: 8 additions & 7 deletions pkg/restapi/v1/oidc4ci/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ func TestController_OidcAuthorize(t *testing.T) {

b, err := json.Marshal(&issuer.PrepareClaimDataAuthorizationResponse{
AuthorizationRequest: issuer.OAuthParameters{},
ProfileAuthStateTtl: 10,
})
require.NoError(t, err)

Expand All @@ -321,7 +322,7 @@ func TestController_OidcAuthorize(t *testing.T) {
}, nil
})

mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), *params.IssuerState, gomock.Any()).
mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), int32(10), *params.IssuerState, gomock.Any()).
Return(nil)
},
check: func(t *testing.T, rec *httptest.ResponseRecorder, err error) {
Expand Down Expand Up @@ -384,7 +385,7 @@ func TestController_OidcAuthorize(t *testing.T) {
}, nil
})

mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), gomock.Any(), gomock.Any()).
mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), int32(0), gomock.Any(), gomock.Any()).
Return(nil)
},
check: func(t *testing.T, rec *httptest.ResponseRecorder, err error) {
Expand Down Expand Up @@ -445,7 +446,7 @@ func TestController_OidcAuthorize(t *testing.T) {
}, nil
})

mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), *params.IssuerState, gomock.Any()).
mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), int32(0), *params.IssuerState, gomock.Any()).
Return(nil)
},
check: func(t *testing.T, rec *httptest.ResponseRecorder, err error) {
Expand Down Expand Up @@ -512,7 +513,7 @@ func TestController_OidcAuthorize(t *testing.T) {
}, nil
})

mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), "generated-op-state", gomock.Any()).
mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), int32(0), "generated-op-state", gomock.Any()).
Return(nil)
},
check: func(t *testing.T, rec *httptest.ResponseRecorder, err error) {
Expand Down Expand Up @@ -578,7 +579,7 @@ func TestController_OidcAuthorize(t *testing.T) {
},
)

mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), *params.IssuerState, gomock.Any()).Return(nil)
mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), int32(0), *params.IssuerState, gomock.Any()).Return(nil)
},
check: func(t *testing.T, rec *httptest.ResponseRecorder, err error) {
require.NoError(t, err)
Expand Down Expand Up @@ -638,7 +639,7 @@ func TestController_OidcAuthorize(t *testing.T) {
},
)

mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), *params.IssuerState, gomock.Any()).
mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), int32(0), *params.IssuerState, gomock.Any()).
Return(nil)
},
check: func(t *testing.T, rec *httptest.ResponseRecorder, err error) {
Expand Down Expand Up @@ -872,7 +873,7 @@ func TestController_OidcAuthorize(t *testing.T) {
}, nil
})

mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), *params.IssuerState, gomock.Any()).Return(
mockStateStore.EXPECT().SaveAuthorizeState(gomock.Any(), int32(0), *params.IssuerState, gomock.Any()).Return(
errors.New("save state error"))
},
check: func(t *testing.T, rec *httptest.ResponseRecorder, err error) {
Expand Down
4 changes: 0 additions & 4 deletions pkg/service/oidc4ci/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,10 +258,6 @@ type PrepareCredentialResultData struct {
NotificationID *string
}

type InsertOptions struct {
TTL time.Duration
}

type AuthorizeState struct {
RedirectURI *url.URL `json:"redirect_uri"`
RespondMode string `json:"respond_mode"`
Expand Down
7 changes: 6 additions & 1 deletion pkg/service/oidc4ci/oidc4ci_acknowledgement.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ func (s *AckService) CreateAck(
return nil, nil //nolint:nilnil
}

id, err := s.cfg.AckStore.Create(ctx, ack)
profile, err := s.cfg.ProfileSvc.GetProfile(ack.ProfileID, ack.ProfileVersion)
if err != nil {
return nil, err
}

id, err := s.cfg.AckStore.Create(ctx, profile.DataConfig.OIDC4CIAckDataTTL, ack)
if err != nil {
return nil, err
}
Expand Down
55 changes: 49 additions & 6 deletions pkg/service/oidc4ci/oidc4ci_acknowledgement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,70 @@ func TestCreateAck(t *testing.T) {
})

t.Run("success", func(t *testing.T) {
profileSvc := NewMockProfileService(gomock.NewController(t))
profileSvc.EXPECT().GetProfile("some_issuer", "v1.0").
Return(&profile.Issuer{
DataConfig: profile.IssuerDataConfig{OIDC4CIAckDataTTL: 10},
}, nil)

store := NewMockAckStore(gomock.NewController(t))
srv := oidc4ci.NewAckService(&oidc4ci.AckServiceConfig{
AckStore: store,
ProfileSvc: profileSvc,
AckStore: store,
})

item := &oidc4ci.Ack{}
store.EXPECT().Create(gomock.Any(), item).Return("id", nil)
item := &oidc4ci.Ack{
ProfileID: "some_issuer",
ProfileVersion: "v1.0",
}

store.EXPECT().Create(gomock.Any(), int32(10), item).Return("id", nil)
id, err := srv.CreateAck(context.TODO(), item)

assert.NoError(t, err)
assert.Equal(t, "id", *id)
})

t.Run("profile srv err", func(t *testing.T) {
profileSvc := NewMockProfileService(gomock.NewController(t))
profileSvc.EXPECT().GetProfile("some_issuer", "v1.0").
Return(nil, errors.New("some error"))

store := NewMockAckStore(gomock.NewController(t))
srv := oidc4ci.NewAckService(&oidc4ci.AckServiceConfig{
AckStore: store,
ProfileSvc: profileSvc,
})

item := &oidc4ci.Ack{
ProfileID: "some_issuer",
ProfileVersion: "v1.0",
}

id, err := srv.CreateAck(context.TODO(), item)

assert.Nil(t, id)
assert.ErrorContains(t, err, "some error")
})

t.Run("store err", func(t *testing.T) {
profileSvc := NewMockProfileService(gomock.NewController(t))
profileSvc.EXPECT().GetProfile("some_issuer", "v1.0").
Return(&profile.Issuer{
DataConfig: profile.IssuerDataConfig{OIDC4CIAckDataTTL: 10},
}, nil)

store := NewMockAckStore(gomock.NewController(t))
srv := oidc4ci.NewAckService(&oidc4ci.AckServiceConfig{
AckStore: store,
ProfileSvc: profileSvc,
AckStore: store,
})

item := &oidc4ci.Ack{}
store.EXPECT().Create(gomock.Any(), item).Return("", errors.New("some err"))
item := &oidc4ci.Ack{
ProfileID: "some_issuer",
ProfileVersion: "v1.0",
}
store.EXPECT().Create(gomock.Any(), int32(10), item).Return("", errors.New("some err"))
id, err := srv.CreateAck(context.TODO(), item)

assert.Nil(t, id)
Expand Down
6 changes: 3 additions & 3 deletions pkg/service/oidc4ci/oidc4ci_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ type pinGenerator interface {
type transactionStore interface {
Create(
ctx context.Context,
profileTransactionDataTTL int32,
data *TransactionData,
params ...func(insertOptions *InsertOptions),
) (*Transaction, error)

Get(
Expand All @@ -75,7 +75,7 @@ type transactionStore interface {
}

type claimDataStore interface {
Create(ctx context.Context, data *ClaimData) (string, error)
Create(ctx context.Context, profileTTLSec int32, data *ClaimData) (string, error)
GetAndDelete(ctx context.Context, id string) (*ClaimData, error)
}

Expand Down Expand Up @@ -127,7 +127,7 @@ type trustRegistry interface {
}

type ackStore interface {
Create(ctx context.Context, data *Ack) (string, error)
Create(ctx context.Context, profileAckDataTTL int32, data *Ack) (string, error)
Get(ctx context.Context, id string) (*Ack, error)
Delete(ctx context.Context, id string) error
}
Expand Down
6 changes: 4 additions & 2 deletions pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (s *Service) InitiateIssuance( // nolint:funlen,gocyclo,gocognit
txData.UserPin = s.pinGenerator.Generate(uuid.NewString())
}

tx, err := s.store.Create(ctx, txData)
tx, err := s.store.Create(ctx, profile.DataConfig.OIDC4CITransactionDataTTL, txData)
if err != nil {
return nil, resterr.NewSystemError(resterr.TransactionStoreComponent, "create",
fmt.Errorf("store tx: %w", err))
Expand Down Expand Up @@ -218,6 +218,7 @@ func (s *Service) newTxCredentialConf(
if isPreAuthFlow {
err = s.applyPreAuthFlowModifications(
ctx,
profile.DataConfig.ClaimDataTTL,
credentialConfiguration,
credentialTemplate,
txCredentialConfiguration,
Expand All @@ -232,6 +233,7 @@ func (s *Service) newTxCredentialConf(

func (s *Service) applyPreAuthFlowModifications(
ctx context.Context,
profileClaimDataTTLSec int32,
req InitiateIssuanceCredentialConfiguration,
credentialTemplate *profileapi.CredentialTemplate,
txCredentialConfiguration *TxCredentialConfiguration,
Expand Down Expand Up @@ -271,7 +273,7 @@ func (s *Service) applyPreAuthFlowModifications(
return errEncrypt
}

claimDataID, claimDataErr := s.claimDataStore.Create(ctx, claimDataEncrypted)
claimDataID, claimDataErr := s.claimDataStore.Create(ctx, profileClaimDataTTLSec, claimDataEncrypted)
if claimDataErr != nil {
return resterr.NewSystemError(resterr.ClaimDataStoreComponent, "create",
fmt.Errorf("store claim data: %w", claimDataErr))
Expand Down
Loading

0 comments on commit b7f44f8

Please sign in to comment.