Skip to content

Commit

Permalink
feat: 5.1.1. Request Issuance of a Certain Credential Type using auth…
Browse files Browse the repository at this point in the history
…orization_details Parameter (#1583)

Signed-off-by: Mykhailo Sizov <mykhailo.sizov@securekey.com>
  • Loading branch information
mishasizov-SK authored Feb 1, 2024
1 parent 7361044 commit f901c65
Show file tree
Hide file tree
Showing 29 changed files with 1,531 additions and 524 deletions.
352 changes: 175 additions & 177 deletions api/spec/openapi.gen.go

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions component/wallet-cli/cmd/oidc4vci_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/trustbloc/vcs/component/wallet-cli/pkg/oidc4vci"
"github.com/trustbloc/vcs/component/wallet-cli/pkg/wallet"
"github.com/trustbloc/vcs/component/wallet-cli/pkg/wellknown"
vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable"
)

const (
Expand All @@ -40,7 +41,7 @@ type oidc4vciCommandFlags struct {
demoIssuerURL string
vcFormat string
credentialType string
credentialFormat string
oidcCredentialFormat vcsverifiable.OIDCFormat
walletDIDIndex int
clientID string
scopes []string
Expand Down Expand Up @@ -86,7 +87,7 @@ func NewOIDC4VCICommand() *cobra.Command {
return fmt.Errorf("--credential-type not set")
}

if flags.credentialFormat == "" {
if flags.oidcCredentialFormat == "" {
return fmt.Errorf("--credential-format not set")
}

Expand Down Expand Up @@ -171,7 +172,7 @@ func NewOIDC4VCICommand() *cobra.Command {

opts := []oidc4vci.Opt{
oidc4vci.WithCredentialType(flags.credentialType),
oidc4vci.WithCredentialFormat(flags.credentialFormat),
oidc4vci.WithOIDCCredentialFormat(flags.oidcCredentialFormat),
oidc4vci.WithClientID(flags.clientID),
oidc4vci.WithTrustRegistryURL(flags.trustRegistryURL),
}
Expand Down Expand Up @@ -251,14 +252,16 @@ func NewOIDC4VCICommand() *cobra.Command {
},
}

var oidcCredentialFormat string

cmd.Flags().StringVar(&flags.serviceFlags.levelDBPath, "leveldb-path", "", "leveldb path")
cmd.Flags().StringVar(&flags.serviceFlags.mongoDBConnectionString, "mongodb-connection-string", "", "mongodb connection string")

cmd.Flags().StringVar(&flags.grantType, "grant-type", "authorization_code", "supported grant types: authorization_code,urn:ietf:params:oauth:grant-type:pre-authorized_code")
cmd.Flags().StringVar(&flags.qrCodePath, "qr-code-path", "", "path to file with qr code")
cmd.Flags().StringVar(&flags.credentialOffer, "credential-offer", "", "openid credential offer")
cmd.Flags().StringVar(&flags.demoIssuerURL, "demo-issuer-url", "", "demo issuer url for downloading qr code automatically")
cmd.Flags().StringVar(&flags.credentialFormat, "credential-format", "ldp_vc", "supported credential formats: ldp_vc,jwt_vc_json-ld")
cmd.Flags().StringVar(&oidcCredentialFormat, "credential-format", "ldp_vc", "supported credential formats: ldp_vc,jwt_vc_json-ld")
cmd.Flags().StringVar(&flags.credentialType, "credential-type", "", "credential type")
cmd.Flags().IntVar(&flags.walletDIDIndex, "wallet-did-index", -1, "index of wallet did, if not set the most recently created DID is used")
cmd.Flags().StringVar(&flags.clientID, "client-id", "", "vcs oauth2 client")
Expand All @@ -274,6 +277,8 @@ func NewOIDC4VCICommand() *cobra.Command {
cmd.Flags().BoolVar(&flags.enableTracing, "enable-tracing", false, "enables http tracing")
cmd.Flags().StringVar(&flags.proxyURL, "proxy-url", "", "proxy url for http client")

flags.oidcCredentialFormat = vcsverifiable.OIDCFormat(oidcCredentialFormat)

return cmd
}

Expand Down
6 changes: 3 additions & 3 deletions component/wallet-cli/pkg/oidc4vci/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ type JWTProofClaims struct {
}

type CredentialRequest struct {
Format string `json:"format,omitempty"`
Types []string `json:"types"`
Proof JWTProof `json:"proof,omitempty"`
Format verifiable.OIDCFormat `json:"format,omitempty"`
Types []string `json:"types"`
Proof JWTProof `json:"proof,omitempty"`
}

type JWTProof struct {
Expand Down
99 changes: 81 additions & 18 deletions component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
Expand Down Expand Up @@ -41,6 +42,7 @@ import (
"github.com/trustbloc/vcs/component/wallet-cli/pkg/trustregistry"
"github.com/trustbloc/vcs/component/wallet-cli/pkg/wallet"
"github.com/trustbloc/vcs/component/wallet-cli/pkg/wellknown"
vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable"
kmssigner "github.com/trustbloc/vcs/pkg/kms/signer"
"github.com/trustbloc/vcs/pkg/restapi/v1/common"
issuerv1 "github.com/trustbloc/vcs/pkg/restapi/v1/issuer"
Expand Down Expand Up @@ -76,7 +78,8 @@ type Flow struct {
flowType FlowType
credentialOffer string
credentialType string
credentialFormat string
oidcCredentialFormat vcsverifiable.OIDCFormat
credentialConfigurationID string
clientID string
scopes []string
redirectURI string
Expand Down Expand Up @@ -201,7 +204,8 @@ func NewFlow(p provider, opts ...Opt) (*Flow, error) {
flowType: o.flowType,
credentialOffer: o.credentialOffer,
credentialType: o.credentialType,
credentialFormat: o.credentialFormat,
oidcCredentialFormat: o.oidcCredentialFormat,
credentialConfigurationID: o.credentialConfigurationID,
clientID: o.clientID,
scopes: o.scopes,
redirectURI: o.redirectURI,
Expand All @@ -220,7 +224,7 @@ func (f *Flow) Run(ctx context.Context) (*verifiable.Credential, error) {
"flow_type", f.flowType,
"credential_offer_uri", f.credentialOffer,
"credential_type", f.credentialType,
"credential_format", f.credentialFormat,
"credential_format", f.oidcCredentialFormat,
)

var (
Expand Down Expand Up @@ -465,23 +469,17 @@ func (f *Flow) getAuthorizationCode(oauthClient *oauth2.Config, issuerState stri

oauthClient.RedirectURL = redirectURI.String()

b, err := json.Marshal(&common.AuthorizationDetails{
Type: "openid_credential",
Types: []string{
"VerifiableCredential",
f.credentialType,
},
Format: lo.ToPtr(f.credentialFormat),
})
authorizationDetailsRequestBody, err := f.getAuthorizationDetailsRequestBody(
f.credentialType, f.credentialConfigurationID, f.oidcCredentialFormat)
if err != nil {
return "", fmt.Errorf("marshal authorization details: %w", err)
return "", fmt.Errorf("getAuthorizationDetailsRequestBody: %w", err)
}

authCodeOptions := []oauth2.AuthCodeOption{
oauth2.SetAuthURLParam("issuer_state", issuerState),
oauth2.SetAuthURLParam("code_challenge", "MLSjJIlPzeRQoN9YiIsSzziqEuBSmS4kDgI3NDjbfF8"),
oauth2.SetAuthURLParam("code_challenge_method", "S256"),
oauth2.SetAuthURLParam("authorization_details", string(b)),
oauth2.SetAuthURLParam("authorization_details", string(authorizationDetailsRequestBody)),
}

if f.enableDiscoverableClientID {
Expand Down Expand Up @@ -714,9 +712,26 @@ func (f *Flow) receiveVC(
return nil, fmt.Errorf("build proof: %w", err)
}

// TODO: take configuration from wellKnown.CredentialsConfigurationSupported[credentialType]
// Take default value as f.oidcCredentialFormat
oidcCredentialFormat := f.oidcCredentialFormat

if f.credentialConfigurationID != "" {
// For cases, when oidcCredentialFormat is not supplied, but credentialConfigurationID option available,
// we can take format from well-known configuration.
// Spec: https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-wg-draft.html#section-5.1.1
format := wellKnown.CredentialConfigurationsSupported.AdditionalProperties[f.credentialConfigurationID].Format
if format == "" {
return nil, fmt.Errorf(
"unable to obtain OIDC credential format from issuer well-known configuration. "+
"Check if `issuer.credentialMetadata.credential_configurations_supported` contains key `%s` "+
"with nested `format` field", f.credentialConfigurationID)
}

oidcCredentialFormat = vcsverifiable.OIDCFormat(format)
}

b, err := json.Marshal(CredentialRequest{
Format: f.credentialFormat,
Format: oidcCredentialFormat,
Types: []string{"VerifiableCredential", f.credentialType},
Proof: JWTProof{
ProofType: "jwt", //TODO: take the value from wellKnown.CredentialsConfigurationSupported[credentialType].ProofTypesSupported
Expand Down Expand Up @@ -879,6 +894,46 @@ func (f *Flow) handleIssuanceAck(
return nil
}

// getAuthorizationDetailsRequestBody returns authorization details request body
// either with credential_configuration_id or format params.
//
// Spec: https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-wg-draft.html#section-5.1.1
func (f *Flow) getAuthorizationDetailsRequestBody(
credentialType, credentialConfigurationID string, oidcCredentialFormat vcsverifiable.OIDCFormat,
) ([]byte, error) {
res := make([]common.AuthorizationDetails, 1) // We do not support multiple authorization details for now.

switch {
case credentialConfigurationID != "": // Priority 1. Based on credentialConfigurationID.
res[0] = common.AuthorizationDetails{
CredentialConfigurationId: &credentialConfigurationID,
CredentialDefinition: nil,
Format: nil,
Locations: nil, // Not supported for now.
Type: "openid_credential",
}
case oidcCredentialFormat != "": // Priority 2. Based on credentialFormat.
res[0] = common.AuthorizationDetails{
CredentialConfigurationId: nil,
CredentialDefinition: &common.CredentialDefinition{
Context: nil, // Not supported for now.
CredentialSubject: nil, // Not supported for now.
Type: []string{
"VerifiableCredential",
credentialType,
},
},
Format: lo.ToPtr(string(oidcCredentialFormat)),
Locations: nil, // Not supported for now.
Type: "openid_credential",
}
default:
return nil, errors.New("neither credentialFormat nor credentialConfigurationID supplied")
}

return json.Marshal(res)
}

func (f *Flow) PerfInfo() *PerfInfo {
return f.perfInfo
}
Expand Down Expand Up @@ -927,7 +982,8 @@ type options struct {
proofBuilder JWTProofBuilder
credentialOffer string
credentialType string
credentialFormat string
oidcCredentialFormat vcsverifiable.OIDCFormat
credentialConfigurationID string
clientID string
scopes []string
redirectURI string
Expand Down Expand Up @@ -966,9 +1022,9 @@ func WithCredentialType(credentialType string) Opt {
}
}

func WithCredentialFormat(credentialFormat string) Opt {
func WithOIDCCredentialFormat(oidcCredentialFormat vcsverifiable.OIDCFormat) Opt {
return func(opts *options) {
opts.credentialFormat = credentialFormat
opts.oidcCredentialFormat = oidcCredentialFormat
}
}

Expand Down Expand Up @@ -1031,3 +1087,10 @@ func WithWalletDIDIndex(idx int) Opt {
opts.walletDIDIndex = idx
}
}

// WithCredentialConfigurationID adds credentialConfigurationID to authorization request.
func WithCredentialConfigurationID(credentialConfigurationID string) Opt {
return func(opts *options) {
opts.credentialConfigurationID = credentialConfigurationID
}
}
35 changes: 28 additions & 7 deletions docs/v1/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,40 @@ components:
type:
type: string
description: String that determines the authorization details type. MUST be set to "openid_credential" for OIDC4VC.
types:
type: array
items:
type: string
description: String array denoting the types of the requested Credential.
format:
type: string
description: String representing a format in which the Credential is requested to be issued. Valid values defined by OIDC4VC are jwt_vc_json-ld and ldp_vc. Issuer can refuse the authorization request if the given credential type and format combo is not supported.
description: REQUIRED when CredentialConfigurationId parameter is not present. String identifying the format of the Credential the Wallet needs. This Credential format identifier determines further claims in the authorization details object needed to identify the Credential type in the requested format. It MUST NOT be present if credential_configuration_id parameter is present.
credential_configuration_id:
type: string
description: REQUIRED when Format parameter is not present. String specifying a unique identifier of the Credential being described in the credential_configurations_supported map in the Credential Issuer Metadata. The referenced object in the credential_configurations_supported map conveys the details, such as the format, for issuance of the requested Credential. It MUST NOT be present if format parameter is present.
credential_definition:
$ref: '#/components/schemas/CredentialDefinition'
locations:
description: An array of strings that allows a client to specify the location of the resource server(s) allowing the Authorization Server to mint audience restricted access tokens.
type: array
items:
type: string
required:
- type
- types
CredentialDefinition:
title: CredentialDefinition object definition.
x-tags:
- issuer
type: object
description: Object containing the detailed description of the credential type.
properties:
'@context':
type: array
items:
type: string
description: 'For ldp_vc only. Array as defined in https://www.w3.org/TR/vc-data-model/#contexts.'
type:
type: array
items:
type: string
description: Array designating the types a certain credential type supports
credentialSubject:
type: object
description: 'An object containing a list of name/value pairs, where each name identifies a claim offered in the Credential. The value can be another such object (nested data structures), or an array of such objects.'
required:
- type
Loading

0 comments on commit f901c65

Please sign in to comment.