Skip to content

Commit

Permalink
feat: Credential Issuer Metadata Parameters
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Sizov <[email protected]>
  • Loading branch information
mishasizov-SK committed Jan 23, 2024
1 parent 05660b2 commit f9d32e8
Show file tree
Hide file tree
Showing 13 changed files with 728 additions and 331 deletions.
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ ALPINE_IMAGE ?=alpine
OPENSSL_IMAGE ?=frapsoft/openssl
GOPROXY ?= https://proxy.golang.org


BUILD_DATE=$(shell date +'%Y%m%d%H%M%S' -d @$(shell git show -s --format=%ct))
VC_REST_VERSION ?= $(subst v,,"$(shell git name-rev --tags --name-only $(shell git rev-parse HEAD))+$(BUILD_DATE)")
ifneq (,$(findstring undefined,"$(VC_REST_VERSION)"))
Expand Down
325 changes: 170 additions & 155 deletions api/spec/openapi.gen.go

Large diffs are not rendered by default.

26 changes: 14 additions & 12 deletions component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@ func (f *Flow) Run(ctx context.Context) (*verifiable.Credential, error) {

f.perfInfo.GetIssuerCredentialsOIDCConfig = time.Since(start)

requireWalletAttestation := openIDConfig.TokenEndpointAuthMethodsSupported != nil &&
lo.Contains(openIDConfig.TokenEndpointAuthMethodsSupported, attestJWTClientAuthType)
tokenEndpointAuthMethodsSupported := lo.FromPtr(openIDConfig.TokenEndpointAuthMethodsSupported)
requireWalletAttestation := lo.Contains(tokenEndpointAuthMethodsSupported, attestJWTClientAuthType)

if f.trustRegistryURL != "" {
if credentialOfferResponse == nil || len(credentialOfferResponse.Credentials) == 0 {
Expand Down Expand Up @@ -290,7 +290,7 @@ func (f *Flow) Run(ctx context.Context) (*verifiable.Credential, error) {
"",
credentialType,
credentialFormat,
lo.Contains(openIDConfig.TokenEndpointAuthMethodsSupported, attestJWTClientAuthType),
lo.Contains(tokenEndpointAuthMethodsSupported, attestJWTClientAuthType),
); err != nil {
return nil, fmt.Errorf("validate issuer: %w", err)
}
Expand All @@ -305,8 +305,8 @@ func (f *Flow) Run(ctx context.Context) (*verifiable.Credential, error) {
ClientID: f.clientID,
Scopes: f.scopes,
Endpoint: oauth2.Endpoint{
AuthURL: openIDConfig.AuthorizationEndpoint,
TokenURL: openIDConfig.TokenEndpoint,
AuthURL: lo.FromPtr(openIDConfig.AuthorizationEndpoint),
TokenURL: lo.FromPtr(openIDConfig.TokenEndpoint),
AuthStyle: oauth2.AuthStyleInHeader,
},
}
Expand Down Expand Up @@ -363,7 +363,7 @@ func (f *Flow) Run(ctx context.Context) (*verifiable.Credential, error) {

var resp *http.Response

if resp, err = f.httpClient.PostForm(openIDConfig.TokenEndpoint, tokenValues); err != nil {
if resp, err = f.httpClient.PostForm(lo.FromPtr(openIDConfig.TokenEndpoint), tokenValues); err != nil {
return nil, err
}

Expand Down Expand Up @@ -686,7 +686,7 @@ func (f *Flow) receiveVC(
wellKnown *issuerv1.WellKnownOpenIDIssuerConfiguration,
credentialIssuer string,
) (*verifiable.Credential, error) {
credentialEndpoint := wellKnown.CredentialEndpoint
credentialEndpoint := lo.FromPtr(wellKnown.CredentialEndpoint)

start := time.Now()
defer func() {
Expand Down Expand Up @@ -714,11 +714,12 @@ func (f *Flow) receiveVC(
return nil, fmt.Errorf("build proof: %w", err)
}

// TODO: take configuration from wellKnown.CredentialsSupported[credentialType]
b, err := json.Marshal(CredentialRequest{
Format: f.credentialFormat,
Types: []string{"VerifiableCredential", f.credentialType},
Proof: JWTProof{
ProofType: "jwt",
ProofType: "jwt", //TODO: take the value from wellKnown.CredentialsSupported[credentialType].ProofTypesSupported
JWT: jws,
},
})
Expand Down Expand Up @@ -825,7 +826,8 @@ func (f *Flow) handleIssuanceAck(
return nil
}

if wellKnown.CredentialAckEndpoint == "" || lo.FromPtr(credResponse.AckID) == "" {
credentialAckEndpoint := lo.FromPtr(wellKnown.CredentialAckEndpoint)
if credentialAckEndpoint == "" || lo.FromPtr(credResponse.AckID) == "" {
return nil
}

Expand All @@ -836,7 +838,7 @@ func (f *Flow) handleIssuanceAck(

slog.Info("Sending wallet ACK",
"ack_id", credResponse.AckID,
"endpoint", wellKnown.CredentialAckEndpoint,
"endpoint", credentialAckEndpoint,
)

b, err := json.Marshal(oidc4civ1.AckRequest{
Expand All @@ -845,15 +847,15 @@ func (f *Flow) handleIssuanceAck(
AckId: *credResponse.AckID,
ErrorDescription: nil,
Status: "success",
IssuerIdentifier: &wellKnown.CredentialIssuer,
IssuerIdentifier: wellKnown.CredentialIssuer,
},
},
})
if err != nil {
return err
}

req, err := http.NewRequest(http.MethodPost, wellKnown.CredentialAckEndpoint, bytes.NewBuffer(b))
req, err := http.NewRequest(http.MethodPost, credentialAckEndpoint, bytes.NewBuffer(b))
if err != nil {
return fmt.Errorf("ack credential request: %w", err)
}
Expand Down
26 changes: 18 additions & 8 deletions component/wallet-cli/pkg/wellknown/wellknown.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package wellknown
import (
"encoding/json"
"fmt"
"github.com/samber/lo"
"io"
"log/slog"
"net/http"
Expand Down Expand Up @@ -48,22 +49,31 @@ func (s *Service) GetWellKnownOpenIDConfiguration(
return nil, fmt.Errorf("get issuer well-known: status code %d", resp.StatusCode)
}

var oidcConfig issuerv1.WellKnownOpenIDIssuerConfiguration
var oidcConfigUnsigned issuerv1.WellKnownOpenIDIssuerConfiguration

wellKnownOpenIDIssuerConfigurationPayload, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read issuer configuration payload body: %w", err)
}

if jwt.IsJWS(string(wellKnownOpenIDIssuerConfigurationPayload)) {
wellKnownOpenIDIssuerConfigurationPayload, err =
getWellKnownOpenIDConfigurationJWTPayload(
string(wellKnownOpenIDIssuerConfigurationPayload), s.VDRRegistry)
if err != nil {
return nil, err
}
if err = json.Unmarshal(wellKnownOpenIDIssuerConfigurationPayload, &oidcConfigUnsigned); err != nil {
return nil, fmt.Errorf("decode issuer well-known: %w", err)
}

signedMetadata := lo.FromPtr(oidcConfigUnsigned.SignedMetadata)

if !jwt.IsJWS(signedMetadata) {
return &oidcConfigUnsigned, nil
}

wellKnownOpenIDIssuerConfigurationPayload, err = getWellKnownOpenIDConfigurationJWTPayload(
signedMetadata, s.VDRRegistry)
if err != nil {
return nil, err
}

var oidcConfig issuerv1.WellKnownOpenIDIssuerConfiguration

if err = json.Unmarshal(wellKnownOpenIDIssuerConfigurationPayload, &oidcConfig); err != nil {
return nil, fmt.Errorf("decode issuer well-known: %w", err)
}
Expand Down
169 changes: 127 additions & 42 deletions docs/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -900,20 +900,62 @@ components:
- issuer
type: object
properties:
credential_issuer:
type: string
description: The Credential Issuer's identifier.
authorization_endpoint:
type: string
description: URL of the OP's OAuth 2.0 Authorization Endpoint.
credential_endpoint:
type: string
description: 'URL of the Credential Issuer''s Credential Endpoint. This URL MUST use the https scheme and MAY contain port, path and query parameter components.'
batch_credential_endpoint:
type: string
description: 'URL of the Credential Issuer''s Batch Credential Endpoint. This URL MUST use the https scheme and MAY contain port, path and query parameter components. If omitted, the Credential Issuer does not support the Batch Credential Endpoint.'
deferred_credential_endpoint:
type: string
description: "URL of the Credential Issuer's Deferred Credential Endpoint. This URL MUST use the https scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not support the Deferred Credential Endpoint."
notification_endpoint:
type: string
description: "URL of the Credential Issuer's Notification Endpoint. This URL MUST use the https scheme and MAY contain port, path, and query parameter components. If omitted, the Credential Issuer does not support the Notification Endpoint."
credential_response_encryption:
$ref: '#/components/schemas/CredentialResponseEncryption'
credential_identifiers_supported:
type: boolean
description: "Boolean value specifying whether the Credential Issuer supports returning credential_identifiers parameter in the authorization_details Token Response parameter, with true indicating support. If omitted, the default value is false."
signed_metadata:
type: string
description: "String that is a signed JWT. This JWT contains Credential Issuer metadata parameters as claims."
display:
type: array
description: 'An array of objects, where each object contains display properties of a Credential Issuer for a certain language.'
items:
$ref: '#/components/schemas/CredentialDisplay'
credential_configurations_supported:
type: object
additionalProperties:
$ref: '#/components/schemas/CredentialConfigurationsSupported'
description: "An object that describes specifics of the Credential that the Credential Issuer supports issuance of. This object contains a list of name/value pairs, where each name is a unique identifier of the supported credential being described."

credential_ack_endpoint:
type: string
description: URL of the acknowledgement endpoint.
registration_endpoint:
type: string
description: URL of the OP's Dynamic Client Registration Endpoint.
token_endpoint:
type: string
description: URL of the OP's OAuth 2.0 Token Endpoint.
token_endpoint_auth_methods_supported:
type: array
items:
type: string
description: JSON array containing a list of client authentication methods supported by this token endpoint. Default is "none".
response_types_supported:
type: array
description: JSON array containing a list of the OAuth 2.0 response_type values that this OP supports.
items:
type: string
registration_endpoint:
type: string
description: URL of the OP's Dynamic Client Registration Endpoint.
scopes_supported:
type: array
items:
Expand All @@ -927,44 +969,6 @@ components:
pre-authorized_grant_anonymous_access_supported:
type: boolean
description: JSON Boolean indicating whether the issuer accepts a Token Request with a Pre-Authorized Code but without a client id. The default is false.
credential_issuer:
type: string
description: The Credential Issuer's identifier.
credential_endpoint:
type: string
description: 'URL of the Credential Issuer''s Credential Endpoint. This URL MUST use the https scheme and MAY contain port, path and query parameter components.'
batch_credential_endpoint:
type: string
description: 'URL of the Credential Issuer''s Batch Credential Endpoint. This URL MUST use the https scheme and MAY contain port, path and query parameter components. If omitted, the Credential Issuer does not support the Batch Credential Endpoint.'
credentials_supported:
type: array
description: 'A JSON array containing a list of JSON objects, each of them representing metadata about a separate credential type that the Credential Issuer can issue.'
display:
type: array
description: 'An array of objects, where each object contains display properties of a Credential Issuer for a certain language.'
items:
$ref: '#/components/schemas/CredentialDisplay'
token_endpoint_auth_methods_supported:
type: array
items:
type: string
description: JSON array containing a list of client authentication methods supported by this token endpoint. Default is "none".
credential_ack_endpoint:
type: string
description: URL of the acknowledgement endpoint.
required:
- authorization_endpoint
- token_endpoint
- response_types_supported
- scopes_supported
- grant_types_supported
- wallet_initiated_auth_flow_supported
- pre-authorized_grant_anonymous_access_supported
- credential_issuer
- credential_endpoint
- credentials_supported
- token_endpoint_auth_methods_supported
- credential_ack_endpoint
description: WellKnownOpenIDIssuerConfiguration represents the OIDC Configuration response for cases when VCS serves as IDP.
CredentialIssuanceHistoryData:
title: CredentialIssuanceHistory response
Expand Down Expand Up @@ -1037,10 +1041,13 @@ components:
- issuer
type: object
properties:
url:
uri:
type: string
description: String value that contains a URI where the Wallet can obtain the logo of the Credential Issuer.
alt_text:
type: string
required:
- uri
CredentialIssuer:
title: CredentialIssuer
x-tag:
Expand Down Expand Up @@ -1929,4 +1936,82 @@ components:
required:
- format
- credential
CredentialConfigurationsSupported:
title: CredentialConfigurationsSupported object definition.
x-tags:
- issuer
type: object
description: An object that describes specifics of the Credential that the Credential Issuer supports issuance of.
properties:
format:
type: string
description: A JSON string identifying the format of this credential, i.e., jwt_vc_json or ldp_vc. Depending on the format value, the object contains further elements defining the type and (optionally) particular claims the credential MAY contain and information about how to display the credential.
scope:
type: string
description: A JSON string identifying the scope value that this Credential Issuer supports for this particular credential.
cryptographic_binding_methods_supported:
type: array
items:
type: string
description: Array of case sensitive strings that identify how the Credential is bound to the identifier of the End-User who possesses the Credential.
cryptographic_suites_supported:
type: array
items:
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'
proof_types:
type: array
items:
type: string
description: A JSON array of case sensitive strings, each representing proof_type that the Credential Issuer supports. If omitted, the default value is jwt.
display:
type: array
description: An array of objects, where each object contains the display properties of the supported credential for a certain language.
items:
$ref: '#/components/schemas/CredentialDisplay'
required:
- format
CredentialConfigurationsSupportedDefinition:
title: CredentialSupported.CredentialDefinition object definition.
x-tags:
- issuer
type: object
description: Object containing the detailed description of the credential type.
properties:
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
CredentialResponseEncryption:
title: CredentialResponseEncryption object definition.
x-tags:
- issuer
type: object
description: Object containing information about whether the Credential Issuer supports encryption of the Credential and Batch Credential Response on top of TLS
properties:
alg_values_supported:
type: array
items:
type: string
description: Array containing a list of the JWE [RFC7516] encryption algorithms (alg values) [RFC7518] supported by the Credential and Batch Credential Endpoint to encode the Credential or Batch Credential Response in a JWT [RFC7519].
enc_values_supported:
type: array
items:
type: string
description: Array containing a list of the JWE [RFC7516] encryption algorithms (enc values) [RFC7518] supported by the Credential and Batch Credential Endpoint to encode the Credential or Batch Credential Response in a JWT [RFC7519].
encryption_required:
type: boolean
description: Boolean value specifying whether the Credential Issuer requires the additional encryption on top of TLS for the Credential Response. If the value is true, the Credential Issuer requires encryption for every Credential Response and therefore the Wallet MUST provide encryption keys in the Credential Request. If the value is false, the Wallet MAY chose whether it provides encryption keys or not.
required:
- alg_values_supported
- enc_values_supported
- encryption_required
securitySchemes: {}
12 changes: 10 additions & 2 deletions pkg/profile/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,16 @@ type CredentialDisplay struct {
}

type CredentialMetaData struct {
CredentialsSupported []map[string]interface{} `json:"credentials_supported"`
Display []*CredentialDisplay `json:"display"`
CredentialsSupported []*CredentialsSupported `json:"credentials_supported"`
Display []*CredentialDisplay `json:"display"`
}

type CredentialsSupported struct {
ID string `json:"id"`
Format string `json:"format"`
Types []string `json:"types"`
CredentialSubject map[string]interface{} `json:"credentialSubject"`
Display []CredentialDisplay `json:"display"`
}

// OIDCConfig represents issuer's OIDC configuration.
Expand Down
Loading

0 comments on commit f9d32e8

Please sign in to comment.