Skip to content

Commit

Permalink
feat: use issuer_state for wallet-flow (#1371)
Browse files Browse the repository at this point in the history
Signed-off-by: Stas D <[email protected]>
  • Loading branch information
skynet2 authored Jul 19, 2023
1 parent fee7f49 commit 0c28510
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 57 deletions.
3 changes: 3 additions & 0 deletions component/wallet-cli/cmd/oidc4ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type oidc4ciCommandFlags struct {
RedirectURI string
Login string
Password string
IssuerState string
VCFormat string
VCProvider string
CredentialType string
Expand Down Expand Up @@ -161,6 +162,7 @@ func NewOIDC4CICommand() *cobra.Command {
RedirectURI: flags.RedirectURI,
Login: flags.Login,
Password: flags.Password,
IssuerState: flags.IssuerState,
CredentialType: flags.CredentialType,
CredentialFormat: flags.CredentialFormat,
Pin: flags.Pin,
Expand Down Expand Up @@ -191,6 +193,7 @@ func NewOIDC4CICommand() *cobra.Command {
cmd.Flags().StringVar(&flags.RedirectURI, "redirect-uri", "", "callback where the authorization code should be sent")
cmd.Flags().StringVar(&flags.Login, "login", "", "user login email")
cmd.Flags().StringVar(&flags.Password, "password", "", "user login password")
cmd.Flags().StringVar(&flags.IssuerState, "issuer-state", "", "issuer-state")
cmd.Flags().StringVar(&flags.VCFormat, "vc-format", "jwt_vc_json-ld", "vc format [jwt_vc_json-ld|ldp_vc]")
cmd.Flags().StringVar(&flags.VCProvider, "vc-provider", "vcs", "vc provider")
cmd.Flags().StringVar(&flags.CredentialType, "credential-type", "", "credential type")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type OIDC4CIConfig struct {
Pin string
Login string
Password string
IssuerState string
}

type OauthClientOpt func(config *oauth2.Config)
Expand Down Expand Up @@ -235,7 +236,7 @@ func (s *Service) RunOIDC4CIWalletInitiated(config *OIDC4CIConfig, hooks *Hooks)
log.Println("Starting OIDC4VCI authorized code flow Wallet initiated")
ctx := context.Background()

issuerUrl := oidc4ci.ExtractIssuerURLFromScopes(config.Scope)
issuerUrl := oidc4ci.ExtractIssuerURL(config.IssuerState)
if issuerUrl == "" {
return errors.New(
"undefined scopes supplied. " +
Expand Down Expand Up @@ -300,6 +301,7 @@ func (s *Service) RunOIDC4CIWalletInitiated(config *OIDC4CIConfig, hooks *Hooks)
}

authCodeURL := s.oauthClient.AuthCodeURL(state,
oauth2.SetAuthURLParam("issuer_state", issuerUrl),
oauth2.SetAuthURLParam("code_challenge", "MLSjJIlPzeRQoN9YiIsSzziqEuBSmS4kDgI3NDjbfF8"),
oauth2.SetAuthURLParam("code_challenge_method", "S256"),
oauth2.SetAuthURLParam("authorization_details", string(b)),
Expand Down
5 changes: 5 additions & 0 deletions pkg/restapi/v1/oidc4ci/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,11 @@ func (c *Controller) OidcAuthorize(e echo.Context, params OidcAuthorizeParams) e
return fmt.Errorf("decode claim data authorization response: %w", err)
}

if claimDataAuth.WalletInitiatedFlow != nil {
ses.Extra[sessionOpStateKey] = claimDataAuth.WalletInitiatedFlow.OpState // swap op state
params.IssuerState = &claimDataAuth.WalletInitiatedFlow.OpState
}

if params.State != nil {
ar.(*fosite.AuthorizeRequest).State = *params.State
}
Expand Down
19 changes: 4 additions & 15 deletions pkg/service/oidc4ci/oidc4ci_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,7 @@ func (s *Service) PrepareClaimDataAuthorizationRequest(
walletFlowResp, walletFlowErr := s.prepareClaimDataAuthorizationRequestWalletInitiated(
ctx,
req.Scope,
ExtractIssuerURLFromScopes(req.Scope),
req.OpState,
ExtractIssuerURL(req.OpState),
)
if walletFlowErr != nil && errors.Is(walletFlowErr, ErrInvalidIssuerURL) { // not wallet-initiated flow
return nil, err
Expand Down Expand Up @@ -262,7 +261,6 @@ func (s *Service) prepareClaimDataAuthorizationRequestWalletInitiated(
ctx context.Context,
requestScopes []string,
issuerURL string,
opState string,
) (*PrepareClaimDataAuthorizationResponse, error) {
sp := strings.Split(issuerURL, "/")
if len(sp) < WalletInitFlowClaimExpectedMatchCount {
Expand Down Expand Up @@ -292,15 +290,6 @@ func (s *Service) prepareClaimDataAuthorizationRequestWalletInitiated(
return nil, fmt.Errorf("wallet initiated flow get oidc config: %w", err)
}

var scopes []string

for _, scope := range requestScopes {
if scope == issuerURL {
continue
}
scopes = append(scopes, scope)
}

event := eventPayload{
WebHook: profile.WebHook,
ProfileID: profileID,
Expand All @@ -321,12 +310,12 @@ func (s *Service) prepareClaimDataAuthorizationRequestWalletInitiated(
ProfileVersion: profileVersion,
ClaimEndpoint: profile.OIDCConfig.ClaimsEndpoint,
CredentialTemplateId: profile.CredentialTemplates[0].ID,
OpState: opState,
Scopes: &scopes,
OpState: uuid.NewString(),
Scopes: &requestScopes,
},
ProfileID: profileID,
ProfileVersion: profileVersion,
Scope: scopes,
Scope: requestScopes,
AuthorizationEndpoint: oidcConfig.AuthorizationEndpoint,
}, nil
}
Expand Down
56 changes: 27 additions & 29 deletions pkg/service/oidc4ci/oidc4ci_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,15 +522,15 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
profileSvc.EXPECT().GetProfile(profileapi.ID("issuer"), "bank_issuer1").
Return(nil, errors.New("not found"))

mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1").
Return(nil, oidc4ci.ErrDataNotFound)
resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1",
"scope3",
},
},
Expand All @@ -556,15 +556,15 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {

profileSvc.EXPECT().GetProfile(profileapi.ID("bank_issuer1"), gomock.Any()).
Return(nil, errors.New("not found"))
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0").
Return(nil, oidc4ci.ErrDataNotFound)
resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
"scope3",
},
},
Expand All @@ -590,15 +590,15 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {

profileSvc.EXPECT().GetProfile(profileapi.ID("bank_issuer1"), gomock.Any()).
Return(&profileapi.Issuer{}, nil)
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0").
Return(nil, oidc4ci.ErrDataNotFound)
resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
"scope3",
},
},
Expand Down Expand Up @@ -628,15 +628,15 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
WalletInitiatedAuthFlowSupported: true,
},
}, nil)
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0").
Return(nil, oidc4ci.ErrDataNotFound)
resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
"scope3",
},
},
Expand Down Expand Up @@ -667,15 +667,15 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
ClaimsEndpoint: "sadsadsa",
},
}, nil)
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0").
Return(nil, oidc4ci.ErrDataNotFound)
resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
"scope3",
},
},
Expand Down Expand Up @@ -712,17 +712,17 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
ClaimsEndpoint: "sadsadsa",
},
}, nil)
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0").
Return(nil, oidc4ci.ErrDataNotFound)
wellKnown.EXPECT().GetOIDCConfiguration(gomock.Any(), gomock.Any()).
Return(nil, errors.New("well-known err"))
resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
"scope3",
},
},
Expand Down Expand Up @@ -758,7 +758,8 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
ClaimsEndpoint: "sadsadsa",
},
}, nil)
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0").
Return(nil, oidc4ci.ErrDataNotFound)
wellKnown.EXPECT().GetOIDCConfiguration(gomock.Any(), gomock.Any()).
Return(&oidc4ci.OIDCConfiguration{}, nil)
Expand All @@ -772,11 +773,10 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
})
resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v1.0",
"scope3",
},
},
Expand All @@ -800,7 +800,8 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
})
assert.NoError(t, err)

mockTransactionStore.EXPECT().FindByOpState(gomock.Any(), "random-op-state").
mockTransactionStore.EXPECT().FindByOpState(gomock.Any(),
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v111.0").
Return(nil, oidc4ci.ErrDataNotFound)
wellKnown.EXPECT().GetOIDCConfiguration(gomock.Any(), "https://awesome.local").
Return(&oidc4ci.OIDCConfiguration{}, nil)
Expand Down Expand Up @@ -829,11 +830,10 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {

resp, err := svc.PrepareClaimDataAuthorizationRequest(context.TODO(),
&oidc4ci.PrepareClaimDataAuthorizationRequest{
OpState: "random-op-state",
OpState: "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v111.0",
Scope: []string{
"scope1",
"scope2",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v111.0",
"scope3",
},
},
Expand All @@ -851,7 +851,8 @@ func TestPrepareClaimDataAuthorizationForWalletFlow(t *testing.T) {
}, resp.Scope)
assert.Equal(t, resp.Scope, *resp.WalletInitiatedFlow.Scopes)
assert.Equal(t, "https://awesome.claims.local", resp.WalletInitiatedFlow.ClaimEndpoint)
assert.Equal(t, "random-op-state", resp.WalletInitiatedFlow.OpState)
assert.NotEqual(t, "https://api-gateway.trustbloc.local:5566/issuer/bank_issuer1/v111.0",
resp.WalletInitiatedFlow.OpState)
assert.Equal(t, "bank_issuer1", resp.WalletInitiatedFlow.ProfileId)
assert.Equal(t, "v111.0", resp.WalletInitiatedFlow.ProfileVersion)
assert.Equal(t, "some-template", resp.WalletInitiatedFlow.CredentialTemplateId)
Expand Down Expand Up @@ -1798,8 +1799,5 @@ func TestSelectProperFormat(t *testing.T) {
}

func TestExtractNoScope(t *testing.T) {
assert.Equal(t, "", oidc4ci.ExtractIssuerURLFromScopes([]string{
"scope1",
"scope2",
}))
assert.Equal(t, "", oidc4ci.ExtractIssuerURL("scope1"))
}
8 changes: 3 additions & 5 deletions pkg/service/oidc4ci/regex.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ const (
WalletInitFlowClaimExpectedMatchCount = 2
)

func ExtractIssuerURLFromScopes(scopes []string) string {
for _, scope := range scopes {
if strings.HasPrefix(scope, "http://") || strings.HasPrefix(scope, "https://") {
return scope
}
func ExtractIssuerURL(input string) string {
if strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://") {
return input
}

return ""
Expand Down
5 changes: 1 addition & 4 deletions test/bdd/fixtures/oauth-clients/clients.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
"response_types": ["code"],
"scopes": [
"openid",
"profile",
"https://api-gateway.trustbloc.local:5566/issuer/bank_issuer/v1.0",
"https://api-gateway.trustbloc.local:5566/issuer/i_myprofile_cmtr_p256_ldp/v1.0",
"https://api-gateway.trustbloc.local:5566/issuer/i_myprofile_ud_es256k_jwt/v1.0"
"profile"
],
"token_endpoint_auth_method": "none"
}
Expand Down
5 changes: 2 additions & 3 deletions test/bdd/pkg/v1/oidc4vc/oidc4ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,16 +301,15 @@ func (s *Steps) runOIDC4CIAuth() error {
}

func (s *Steps) runOIDC4CIAuthWalletInitiatedFlow() error {
walletInitiatedFlowScope := fmt.Sprintf(vcsIssuerURL, s.issuerProfile.ID, s.issuerProfile.Version)

err := s.walletRunner.RunOIDC4CIWalletInitiated(&walletrunner.OIDC4CIConfig{
ClientID: "oidc4vc_client",
Scope: []string{"openid", "profile", walletInitiatedFlowScope},
Scope: []string{"openid", "profile"},
RedirectURI: "http://127.0.0.1/callback",
CredentialType: s.issuedCredentialType,
CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string),
Login: "bdd-test",
Password: "bdd-test-pass",
IssuerState: fmt.Sprintf(vcsIssuerURL, s.issuerProfile.ID, s.issuerProfile.Version),
}, nil)
if err != nil {
return fmt.Errorf("s.walletRunner.RunOIDC4CIWalletInitiated: %w", err)
Expand Down

0 comments on commit 0c28510

Please sign in to comment.