Skip to content

Commit

Permalink
fix(sdk): missing context for ldp_vc credentials format (#835)
Browse files Browse the repository at this point in the history
Signed-off-by: Volodymyr Kit <[email protected]>
  • Loading branch information
volodymyr-kit-gen authored Nov 25, 2024
1 parent b17ff0b commit 0715131
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 25 deletions.
10 changes: 10 additions & 0 deletions pkg/openid4ci/createauthorizationurlopts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type createAuthorizationURLOpts struct {
scopes []string
issuerState *string
useOAuthDiscoverableClientIDScheme bool
context []string
}

// CreateAuthorizationURLOpt is an option for the CreateAuthorizationURL method.
Expand Down Expand Up @@ -56,6 +57,15 @@ func WithOAuthDiscoverableClientIDScheme() CreateAuthorizationURLOpt {
}
}

// WithCredentialContext is an option for the CreateAuthorizationURL method that specifies an credential context
// See https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#appendix-A.1.2.3
// for more information about authorization request.
func WithCredentialContext(context []string) CreateAuthorizationURLOpt {
return func(opts *createAuthorizationURLOpts) {
opts.context = context
}
}

func processCreateAuthorizationURLOpts(opts []CreateAuthorizationURLOpt) *createAuthorizationURLOpts {
processedOpts := &createAuthorizationURLOpts{}

Expand Down
30 changes: 19 additions & 11 deletions pkg/openid4ci/interaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ type requestedAcknowledgment struct {
ackIDs []string
}

func (i *interaction) createAuthorizationURL(clientID, redirectURI, format string, types []string, issuerState *string,
scopes []string, useOAuthDiscoverableClientIDScheme bool,
func (i *interaction) createAuthorizationURL(clientID, redirectURI, format string, types, credentialContext []string,
issuerState *string, scopes []string, useOAuthDiscoverableClientIDScheme bool,
) (string, error) {
err := i.populateIssuerMetadata("Authorization")
if err != nil {
Expand All @@ -91,7 +91,7 @@ func (i *interaction) createAuthorizationURL(clientID, redirectURI, format strin
return "", err
}

authorizationDetails, err := i.generateAuthorizationDetails(format, types)
authorizationDetails, err := i.generateAuthorizationDetails(format, types, credentialContext)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -134,13 +134,13 @@ func (i *interaction) instantiateCodeVerifier() error {
return nil
}

func (i *interaction) generateAuthorizationDetails(format string, types []string) ([]byte, error) {
func (i *interaction) generateAuthorizationDetails(format string, types, credentialContext []string) ([]byte, error) {
// TODO: Add support for requesting multiple credentials at once (by sending an array).
// Currently we always use the first credential type specified in the offer.
authorizationDetailsDTO := authorizationDetails{
CredentialConfigurationID: "",
CredentialDefinition: &issuer.CredentialDefinition{
Context: nil,
Context: credentialContext,
CredentialSubject: nil,
Type: types,
},
Expand Down Expand Up @@ -304,11 +304,11 @@ func (i *interaction) populateIssuerMetadata(parentEvent string) error {
}

func (i *interaction) requestCredentialWithAuth(jwtSigner api.JWTSigner, credentialFormats []string,
credentialTypes [][]string,
credentialTypes, credentialContexts [][]string,
) ([]*verifiable.Credential, error) {
timeStartRequestCredential := time.Now()

credentialResponses, err := i.getCredentialResponsesWithAuth(jwtSigner, credentialFormats, credentialTypes)
credentialResponses, err := i.getCredentialResponsesWithAuth(jwtSigner, credentialFormats, credentialTypes, credentialContexts)
if err != nil {
return nil, fmt.Errorf("failed to get credential response: %w", err)
}
Expand Down Expand Up @@ -346,7 +346,7 @@ func (i *interaction) requestCredentialWithAuth(jwtSigner api.JWTSigner, credent

// credentialsFormats and credentialTypes need to have the same length.
func (i *interaction) getCredentialResponsesWithAuth(signer api.JWTSigner, credentialFormats []string,
credentialTypes [][]string,
credentialTypes, credentialContexts [][]string,
) ([]CredentialResponse, error) {
proofJWT, err := i.createClaimsProof(i.authTokenResponseNonce, signer)
if err != nil {
Expand All @@ -359,7 +359,7 @@ func (i *interaction) getCredentialResponsesWithAuth(signer api.JWTSigner, crede

for index := range credentialTypes {
request, err := i.createCredentialRequestWithoutAccessToken(proofJWT, credentialFormats[index],
credentialTypes[index])
credentialTypes[index], credentialContexts[index])
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -442,11 +442,19 @@ func createOAuthHTTPClient(
// The returned *http.Request will not have the access token set on it. The caller must ensure that it's set
// before sending the request to the server.
func (i *interaction) createCredentialRequestWithoutAccessToken(proofJWT, credentialFormat string,
credentialTypes []string,
credentialTypes, credentialContext []string,
) (*http.Request, error) {

var credentialContextToSend *[]string

if len(credentialContext) > 0 {
credentialContextToSend = &credentialContext
}

credentialReq := &credentialRequest{
CredentialDefinition: &credentialDefinition{
Type: credentialTypes,
Type: credentialTypes,
Context: credentialContextToSend,
},
Format: credentialFormat,
Proof: proof{
Expand Down
27 changes: 17 additions & 10 deletions pkg/openid4ci/issuerinitiatedinteraction.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type IssuerInitiatedInteraction struct {
credentialTypes [][]string
credentialFormats []string
credentialConfigIDs []string
credentialContexts [][]string
preAuthorizedCodeGrantParams *PreAuthorizedCodeGrantParams
authorizationCodeGrantParams *AuthorizationCodeGrantParams

Expand Down Expand Up @@ -114,7 +115,7 @@ func NewIssuerInitiatedInteraction(
return nil, err
}

credentialTypes, credentialFormats, err := determineCredentialTypesAndFormats(credentialOffer,
credentialTypes, credentialFormats, credentialContexts, err := determineCredentialParameters(credentialOffer,
issuerInteraction.issuerMetadata)
if err != nil {
return nil, err
Expand All @@ -126,6 +127,7 @@ func NewIssuerInitiatedInteraction(
authorizationCodeGrantParams: authorizationCodeGrantParams,
credentialTypes: credentialTypes,
credentialFormats: credentialFormats,
credentialContexts: credentialContexts,
credentialConfigIDs: credentialOffer.CredentialConfigurationIDs,
},
config.MetricsLogger.Log(
Expand Down Expand Up @@ -158,7 +160,7 @@ func (i *IssuerInitiatedInteraction) CreateAuthorizationURL(clientID, redirectUR
}

return i.interaction.createAuthorizationURL(clientID, redirectURI, i.credentialFormats[0], i.credentialTypes[0],
issuerState, processedOpts.scopes, processedOpts.useOAuthDiscoverableClientIDScheme)
i.credentialContexts[0], issuerState, processedOpts.scopes, processedOpts.useOAuthDiscoverableClientIDScheme)
}

// RequestCredentialWithPreAuth requests credential(s) from the issuer. This method can only be used for the
Expand Down Expand Up @@ -208,7 +210,7 @@ func (i *IssuerInitiatedInteraction) RequestCredentialWithAuth(jwtSigner api.JWT
return nil, err
}

return i.interaction.requestCredentialWithAuth(jwtSigner, i.credentialFormats, i.credentialTypes)
return i.interaction.requestCredentialWithAuth(jwtSigner, i.credentialFormats, i.credentialTypes, i.credentialContexts)
}

// IssuerURI returns the issuer's URI from the initiation request. It's useful to store this somewhere in case
Expand Down Expand Up @@ -421,7 +423,7 @@ func (i *IssuerInitiatedInteraction) getCredentialResponsesWithPreAuth(

for index := range i.credentialTypes {
request, err := i.interaction.createCredentialRequestWithoutAccessToken(proofJWT, i.credentialFormats[index],
i.credentialTypes[index])
i.credentialTypes[index], i.credentialContexts[index])
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -693,19 +695,20 @@ func getCredentialOfferJSONFromCredentialOfferURI(credentialOfferURI string,
return responseBytes, nil
}

func determineCredentialTypesAndFormats(
func determineCredentialParameters(
credentialOffer *CredentialOffer,
issuerMetadata *issuer.Metadata,
) ([][]string, []string, error) {
) ([][]string, []string, [][]string, error) {
types := make([][]string, len(credentialOffer.CredentialConfigurationIDs))
formats := make([]string, len(credentialOffer.CredentialConfigurationIDs))
contexts := make([][]string, len(credentialOffer.CredentialConfigurationIDs))

for i := 0; i < len(credentialOffer.CredentialConfigurationIDs); i++ {
for i := range len(credentialOffer.CredentialConfigurationIDs) {
id := credentialOffer.CredentialConfigurationIDs[i]

configuration, ok := issuerMetadata.CredentialConfigurationsSupported[id]
if !ok {
return nil, nil, walleterror.NewValidationError(
return nil, nil, nil, walleterror.NewValidationError(
ErrorModule,
InvalidCredentialConfigurationIDCode,
InvalidCredentialConfigurationIDError,
Expand All @@ -718,7 +721,7 @@ func determineCredentialTypesAndFormats(
if configuration.Format != jwtVCJSONCredentialFormat &&
configuration.Format != jwtVCJSONLDCredentialFormat &&
configuration.Format != ldpVCCredentialFormat {
return nil, nil, walleterror.NewValidationError(
return nil, nil, nil, walleterror.NewValidationError(
ErrorModule,
UnsupportedCredentialTypeInOfferCode,
UnsupportedCredentialTypeInOfferError,
Expand All @@ -729,9 +732,13 @@ func determineCredentialTypesAndFormats(
}

formats[i] = configuration.Format

if configuration.CredentialDefinition != nil && configuration.Format == ldpVCCredentialFormat {
contexts[i] = configuration.CredentialDefinition.Context
}
}

return types, formats, nil
return types, formats, contexts, nil
}

func validateSignerKeyID(jwtSigner api.JWTSigner) error {
Expand Down
11 changes: 7 additions & 4 deletions pkg/openid4ci/walletinitiatedinteraction.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ import (
type WalletInitiatedInteraction struct {
interaction *interaction

credentialFormat string
credentialTypes []string
credentialFormat string
credentialTypes []string
credentialContext []string
}

// NewWalletInitiatedInteraction creates a new OpenID4CI WalletInitiatedInteraction.
Expand Down Expand Up @@ -96,13 +97,15 @@ func (i *WalletInitiatedInteraction) CreateAuthorizationURL(clientID, redirectUR
processedOpts := processCreateAuthorizationURLOpts(opts)

authorizationURL, err := i.interaction.createAuthorizationURL(clientID, redirectURI, credentialFormat,
credentialTypes, processedOpts.issuerState, processedOpts.scopes, processedOpts.useOAuthDiscoverableClientIDScheme)
credentialTypes, processedOpts.context, processedOpts.issuerState, processedOpts.scopes,
processedOpts.useOAuthDiscoverableClientIDScheme)
if err != nil {
return "", err
}

i.credentialFormat = credentialFormat
i.credentialTypes = credentialTypes
i.credentialContext = processedOpts.context

return authorizationURL, nil
}
Expand All @@ -119,7 +122,7 @@ func (i *WalletInitiatedInteraction) RequestCredential(jwtSigner api.JWTSigner,
return nil, err
}

return i.interaction.requestCredentialWithAuth(jwtSigner, []string{i.credentialFormat}, [][]string{i.credentialTypes})
return i.interaction.requestCredentialWithAuth(jwtSigner, []string{i.credentialFormat}, [][]string{i.credentialTypes}, [][]string{i.credentialContext})
}

// DynamicClientRegistrationSupported indicates whether the issuer supports dynamic client registration.
Expand Down

0 comments on commit 0715131

Please sign in to comment.