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

Signed-off-by: Mykhailo Sizov <[email protected]>
  • Loading branch information
mishasizov-SK committed Jan 31, 2024
1 parent 7361044 commit 363d0e3
Show file tree
Hide file tree
Showing 26 changed files with 1,448 additions and 521 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
80 changes: 63 additions & 17 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 @@ -716,7 +714,7 @@ func (f *Flow) receiveVC(

// TODO: take configuration from wellKnown.CredentialsConfigurationSupported[credentialType]
b, err := json.Marshal(CredentialRequest{
Format: f.credentialFormat,
Format: f.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 +877,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 +965,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 +1005,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 +1070,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
40 changes: 11 additions & 29 deletions docs/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ paths:
description: An opaque value used by the client to maintain state between the request and callback. The authorization server includes this value when redirecting the user-agent back to the client. The parameter SHOULD be used for preventing cross-site request forgery.
authorization_details:
type: string
description: The authorization_details conveys the details about the credentials the wallet wants to obtain. Multiple authorization_details can be used with type openid_credential to request authorization in case of multiple credentials.
description: Encoded array of authorization_details conveys the details about the credentials the wallet wants to obtain. Multiple authorization_details can be used with type openid_credential to request authorization in case of multiple credentials.
wallet_issuer:
type: string
description: Wallet's OpenID Connect Issuer URL. The Issuer will use the discovery process to determine the wallet's capabilities and endpoints. RECOMMENDED in Dynamic Credential Request.
Expand Down Expand Up @@ -659,7 +659,7 @@ paths:
type: string
in: query
name: authorization_details
description: The authorization_details conveys the details about the credentials the wallet wants to obtain. Multiple authorization_details can be used with type openid_credential to request authorization in case of multiple credentials.
description: Encoded array of the authorization_details conveys the details about the credentials the wallet wants to obtain. Multiple authorization_details can be used with type openid_credential to request authorization in case of multiple credentials.
- schema:
type: string
in: query
Expand Down Expand Up @@ -1309,7 +1309,9 @@ components:
items:
type: string
authorization_details:
$ref: ./common.yaml#/components/schemas/AuthorizationDetails
type: array
items:
$ref: './common.yaml#/components/schemas/AuthorizationDetails'
op_state:
type: string
required:
Expand Down Expand Up @@ -1358,7 +1360,7 @@ components:
code:
type: string
wallet_initiated_flow:
$ref: ./common.yaml#/components/schemas/WalletInitiatedFlowData
$ref: './common.yaml#/components/schemas/WalletInitiatedFlowData'
nullable: true
required:
- op_state
Expand Down Expand Up @@ -1437,7 +1439,7 @@ components:
type: string
description: Transaction ID to correlate upcoming authorization response.
wallet_initiated_flow:
$ref: ./common.yaml#/components/schemas/WalletInitiatedFlowData
$ref: './common.yaml#/components/schemas/WalletInitiatedFlowData'
required:
- authorization_request
- authorization_endpoint
Expand Down Expand Up @@ -1767,7 +1769,9 @@ components:
op_state:
type: string
authorization_details:
$ref: ./common.yaml#/components/schemas/AuthorizationDetails
type: array
items:
$ref: './common.yaml#/components/schemas/AuthorizationDetails'
required:
- op_state
- authorization_details
Expand Down Expand Up @@ -2002,7 +2006,7 @@ components:
type: string
description: Array of case sensitive strings that identify the cryptographic suites that are supported for the cryptographic_binding_methods_supported.
credential_definition:
$ref: '#/components/schemas/CredentialConfigurationsSupportedDefinition'
$ref: './common.yaml#/components/schemas/CredentialDefinition'
order:
type: array
items:
Expand All @@ -2029,28 +2033,6 @@ components:
$ref: '#/components/schemas/CredentialDisplay'
required:
- format
CredentialConfigurationsSupportedDefinition:
title: CredentialConfigurationsSupportedDefinition 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
CredentialResponseEncryptionSupported:
title: CredentialResponseEncryption object definition.
x-tags:
Expand Down
Loading

0 comments on commit 363d0e3

Please sign in to comment.