Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for custom oid4vp URL scheme #1735

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 119 additions & 119 deletions api/spec/openapi.gen.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion component/wallet-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ To trace HTTP requests between `wallet-cli` and `vcs`, use the `--enable-tracing
Use the `oidc4vp` command to present Verifiable Credential(s) to the Verifier:
```bash
--attestation-url string attestation url, i.e. https://<host>/vcs/wallet/attestation
--authorization-request-uri string authorization request uri, starts with 'openid-vc://?request_uri=' prefix
--authorization-request-uri string authorization request uri, starts with 'openid-vc://?request_uri=' prefix if default URL schema is used
--disable-domain-matching disables domain matching for issuer and verifier when presenting credentials (only for did:web)
--enable-linked-domain-verification enables linked domain verification
--enable-tracing enables http tracing
Expand Down
9 changes: 6 additions & 3 deletions component/wallet-cli/cmd/oidc4vp_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,15 @@ func NewOIDC4VPCommand() *cobra.Command {
return fmt.Errorf("either --qr-code-path or --authorization-request-uri flag must be set")
}

requestURI := strings.TrimPrefix(authorizationRequest, "openid-vc://?request_uri=")
requestURI := strings.SplitN(authorizationRequest, "?request_uri=", 2)
if len(requestURI) != 2 {
return fmt.Errorf("invalid authorizationRequest format: %s", authorizationRequest)
}

var flow *oidc4vp.Flow

opts := []oidc4vp.Opt{
oidc4vp.WithRequestURI(requestURI),
oidc4vp.WithRequestURI(requestURI[1]),
oidc4vp.WithWalletDIDIndex(walletDIDIndex),
}

Expand Down Expand Up @@ -177,7 +180,7 @@ func createFlags(cmd *cobra.Command, flags *oidc4vpCommandFlags) {
cmd.Flags().StringVar(&flags.serviceFlags.mongoDBConnectionString, "mongodb-connection-string", "", "mongodb connection string")

cmd.Flags().StringVar(&flags.qrCodePath, "qr-code-path", "", "path to file with qr code")
cmd.Flags().StringVar(&flags.authorizationRequestURI, "authorization-request-uri", "", "authorization request uri, starts with 'openid-vc://?request_uri=' prefix")
cmd.Flags().StringVar(&flags.authorizationRequestURI, "authorization-request-uri", "", "authorization request uri, starts with 'openid-vc://?request_uri=' prefix if default URL schema is used")
cmd.Flags().BoolVar(&flags.enableLinkedDomainVerification, "enable-linked-domain-verification", false, "enables linked domain verification")
cmd.Flags().BoolVar(&flags.disableDomainMatching, "disable-domain-matching", false, "disables domain matching for issuer and verifier when presenting credentials (only for did:web)")
cmd.Flags().IntVar(&flags.walletDIDIndex, "wallet-did-index", -1, "index of wallet did, if not set the most recently created DID is used")
Expand Down
4 changes: 3 additions & 1 deletion docs/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1268,11 +1268,13 @@ components:
type: string
scopes:
type: array
description: List of custom scopes that defines additional claims requested from Holder to Verifier.
items:
type: string
description: List of custom scopes that defines additional claims requested from Holder to Verifier.
presentationDefinitionFilters:
$ref: '#/components/schemas/PresentationDefinitionFilters'
customURLScheme:
type: string
PresentationDefinitionFilters:
title: PresentationDefinitionFilters
x-tags:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func (w *Wrapper) InitiateOidcInteraction(
presentationDefinition *presexch.PresentationDefinition,
purpose string,
customScopes []string,
customURLScheme string,
profile *profileapi.Verifier) (*oidc4vp.InteractionInfo, error) {
ctx, span := w.tracer.Start(ctx, "oidc4vp.InitiateOidcInteraction")
defer span.End()
Expand All @@ -45,7 +46,8 @@ func (w *Wrapper) InitiateOidcInteraction(
span.SetAttributes(attribute.String("purpose", purpose))
span.SetAttributes(attribute.StringSlice("custom_copes", customScopes))

resp, err := w.svc.InitiateOidcInteraction(ctx, presentationDefinition, purpose, customScopes, profile)
resp, err := w.svc.InitiateOidcInteraction(ctx,
presentationDefinition, purpose, customScopes, customURLScheme, profile)
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ func TestWrapper_InitiateOidcInteraction(t *testing.T) {
ctrl := gomock.NewController(t)

svc := NewMockService(ctrl)
svc.EXPECT().InitiateOidcInteraction(gomock.Any(), &presexch.PresentationDefinition{}, "purpose", []string{"additionalScope"}, &profileapi.Verifier{}).Times(1)
svc.EXPECT().InitiateOidcInteraction(gomock.Any(), &presexch.PresentationDefinition{}, "purpose", []string{"additionalScope"}, "", &profileapi.Verifier{}).Times(1)

w := Wrap(svc, trace.NewNoopTracerProvider().Tracer(""))

_, err := w.InitiateOidcInteraction(context.Background(), &presexch.PresentationDefinition{}, "purpose", []string{"additionalScope"}, &profileapi.Verifier{})
_, err := w.InitiateOidcInteraction(context.Background(), &presexch.PresentationDefinition{}, "purpose", []string{"additionalScope"}, "", &profileapi.Verifier{})
require.NoError(t, err)
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/restapi/v1/verifier/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ func (c *Controller) initiateOidcInteraction(
}

result, err := c.oidc4VPService.InitiateOidcInteraction(
ctx, pd, lo.FromPtr(data.Purpose), lo.FromPtr(data.Scopes), profile)
ctx, pd, lo.FromPtr(data.Purpose), lo.FromPtr(data.Scopes), lo.FromPtr(data.CustomURLScheme), profile)
if err != nil {
return nil, resterr.NewSystemError(resterr.VerifierOIDC4vpSvcComponent, "InitiateOidcInteraction", err)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/restapi/v1/verifier/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2039,8 +2039,8 @@ func TestController_InitiateOidcInteraction(t *testing.T) {
mockProfileSvc := NewMockProfileService(gomock.NewController(t))

oidc4VPSvc := NewMockOIDC4VPService(gomock.NewController(t))
oidc4VPSvc.EXPECT().InitiateOidcInteraction(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes().Return(&oidc4vp.InteractionInfo{}, nil)
oidc4VPSvc.EXPECT().InitiateOidcInteraction(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(),
gomock.Any()).AnyTimes().Return(&oidc4vp.InteractionInfo{}, nil)

t.Run("Success", func(t *testing.T) {
mockProfileSvc.EXPECT().GetProfile(gomock.Any(), gomock.Any()).Times(1).Return(&profileapi.Verifier{
Expand Down Expand Up @@ -2090,7 +2090,7 @@ func TestController_initiateOidcInteraction(t *testing.T) {

oidc4VPSvc := NewMockOIDC4VPService(gomock.NewController(t))
oidc4VPSvc.EXPECT().InitiateOidcInteraction(
gomock.Any(), gomock.Any(), gomock.Any(), []string{"test_scope"}, gomock.Any()).
gomock.Any(), gomock.Any(), gomock.Any(), []string{"test_scope"}, "", gomock.Any()).
AnyTimes().Return(&oidc4vp.InteractionInfo{}, nil)

t.Run("Success", func(t *testing.T) {
Expand Down Expand Up @@ -2321,7 +2321,7 @@ func TestController_initiateOidcInteraction(t *testing.T) {
t.Run("oidc4VPService.InitiateOidcInteraction failed", func(t *testing.T) {
oidc4VPSvc := NewMockOIDC4VPService(gomock.NewController(t))
oidc4VPSvc.EXPECT().
InitiateOidcInteraction(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
InitiateOidcInteraction(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes().Return(nil, errors.New("fail"))

controller := NewController(&Config{
Expand Down
1 change: 1 addition & 0 deletions pkg/restapi/v1/verifier/openapi.gen.go

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

1 change: 1 addition & 0 deletions pkg/service/oidc4vp/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type ServiceInterface interface {
presentationDefinition *presexch.PresentationDefinition,
purpose string,
customScopes []string,
customURLScheme string,
profile *profileapi.Verifier,
) (*InteractionInfo, error)
VerifyOIDCVerifiablePresentation(ctx context.Context, txID TxID, authResponse *AuthorizationResponseParsed) error
Expand Down
10 changes: 9 additions & 1 deletion pkg/service/oidc4vp/oidc4vp_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const (
vpTokenIDTokenResponseType = "vp_token id_token" //nolint:gosec
directPostResponseMode = "direct_post"
didClientIDScheme = "did"
defaultURLScheme = "openid-vc://"
)

const (
Expand Down Expand Up @@ -229,6 +230,7 @@ func (s *Service) InitiateOidcInteraction(
presentationDefinition *presexch.PresentationDefinition,
purpose string,
customScopes []string,
customURLScheme string,
profile *profileapi.Verifier,
) (*InteractionInfo, error) {
logger.Debugc(ctx, "InitiateOidcInteraction begin")
Expand Down Expand Up @@ -273,7 +275,13 @@ func (s *Service) InitiateOidcInteraction(

logger.Debugc(ctx, "InitiateOidcInteraction request object published")

authorizationRequest := "openid-vc://?request_uri=" + requestURI
urlScheme := defaultURLScheme

if customURLScheme != "" {
urlScheme = customURLScheme
}

authorizationRequest := fmt.Sprintf("%s?request_uri=%s", urlScheme, requestURI)

if errSendEvent := s.sendOIDCInteractionInitiatedEvent(ctx, tx, profile, authorizationRequest); errSendEvent != nil {
return nil, errSendEvent
Expand Down
23 changes: 19 additions & 4 deletions pkg/service/oidc4vp/oidc4vp_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -140,10 +141,21 @@ func TestService_InitiateOidcInteraction(t *testing.T) {
t.Run("Success", func(t *testing.T) {
info, err := s.InitiateOidcInteraction(context.TODO(), &presexch.PresentationDefinition{
ID: "test",
}, "test", []string{customScope}, correctProfile)
}, "test", []string{customScope}, "", correctProfile)

require.NoError(t, err)
require.NotNil(t, info)
require.True(t, strings.HasPrefix(info.AuthorizationRequest, "openid-vc://"))
})

t.Run("Success with custom URL scheme", func(t *testing.T) {
info, err := s.InitiateOidcInteraction(context.TODO(), &presexch.PresentationDefinition{
ID: "test",
}, "test", []string{customScope}, "openid4vp://", correctProfile)

require.NoError(t, err)
require.NotNil(t, info)
require.True(t, strings.HasPrefix(info.AuthorizationRequest, "openid4vp://"))
})

t.Run("No signature did", func(t *testing.T) {
Expand All @@ -152,7 +164,7 @@ func TestService_InitiateOidcInteraction(t *testing.T) {
incorrectProfile.SigningDID = nil

info, err := s.InitiateOidcInteraction(
context.TODO(), &presexch.PresentationDefinition{}, "test", []string{customScope}, incorrectProfile)
context.TODO(), &presexch.PresentationDefinition{}, "test", []string{customScope}, "", incorrectProfile)

require.Error(t, err)
require.Nil(t, info)
Expand All @@ -179,6 +191,7 @@ func TestService_InitiateOidcInteraction(t *testing.T) {
&presexch.PresentationDefinition{},
"test",
[]string{customScope},
"",
correctProfile,
)

Expand All @@ -205,6 +218,7 @@ func TestService_InitiateOidcInteraction(t *testing.T) {
&presexch.PresentationDefinition{},
"test",
[]string{customScope},
"",
correctProfile,
)

Expand All @@ -230,6 +244,7 @@ func TestService_InitiateOidcInteraction(t *testing.T) {
&presexch.PresentationDefinition{},
"test",
[]string{customScope},
"",
correctProfile,
)

Expand All @@ -243,7 +258,7 @@ func TestService_InitiateOidcInteraction(t *testing.T) {
incorrectProfile.SigningDID.KMSKeyID = "invalid"

info, err := s.InitiateOidcInteraction(
context.TODO(), &presexch.PresentationDefinition{}, "test", []string{customScope}, incorrectProfile)
context.TODO(), &presexch.PresentationDefinition{}, "test", []string{customScope}, "", incorrectProfile)

require.Error(t, err)
require.Nil(t, info)
Expand All @@ -255,7 +270,7 @@ func TestService_InitiateOidcInteraction(t *testing.T) {
incorrectProfile.OIDCConfig.KeyType = "invalid"

info, err := s.InitiateOidcInteraction(
context.TODO(), &presexch.PresentationDefinition{}, "test", []string{customScope}, incorrectProfile)
context.TODO(), &presexch.PresentationDefinition{}, "test", []string{customScope}, "", incorrectProfile)

require.Error(t, err)
require.Nil(t, info)
Expand Down
7 changes: 5 additions & 2 deletions test/bdd/pkg/v1/oidc4vc/oidc4vp.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,10 +273,13 @@ func (s *Steps) runOIDC4VPFlowWithOpts(profileVersionedID, pdID, fields string,
return fmt.Errorf("init oidc4vp interaction: %w", err)
}

requestURI := strings.TrimPrefix(initiateInteractionResult.AuthorizationRequest, "openid-vc://?request_uri=")
requestURI := strings.SplitN(initiateInteractionResult.AuthorizationRequest, "?request_uri=", 2)
if len(requestURI) != 2 {
return fmt.Errorf("invalid AuthorizationRequest format: %s", initiateInteractionResult.AuthorizationRequest)
}

flow, err := oidc4vp.NewFlow(s.oidc4vpProvider,
oidc4vp.WithRequestURI(requestURI),
oidc4vp.WithRequestURI(requestURI[1]),
oidc4vp.WithDomainMatchingDisabled(),
oidc4vp.WithSchemaValidationDisabled(),
)
Expand Down
7 changes: 6 additions & 1 deletion test/stress/pkg/stress/stress_test_case.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,13 @@ func (c *TestCase) Invoke() (string, interface{}, error) {
return credID, nil, fmt.Errorf("cred id [%v]; fetch authorization request: %w", credID, err)
}

requestURI := strings.SplitN(authorizationRequest, "?request_uri=", 2)
if len(requestURI) != 2 {
return "", nil, fmt.Errorf("invalid authorizationRequest format: %s", authorizationRequest)
}

vpFlow, err = oidc4vp.NewFlow(c.oidc4vpProvider,
oidc4vp.WithRequestURI(strings.TrimPrefix(authorizationRequest, "openid-vc://?request_uri=")),
oidc4vp.WithRequestURI(requestURI[1]),
oidc4vp.WithDomainMatchingDisabled(),
oidc4vp.WithSchemaValidationDisabled(),
)
Expand Down
Loading