Skip to content

Commit

Permalink
feat: Authorization Request
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Sizov <[email protected]>
  • Loading branch information
mishasizov-SK committed Mar 7, 2024
1 parent d7b26bb commit 73b2e0f
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 195 deletions.
47 changes: 22 additions & 25 deletions component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ type Flow struct {
credentialOffer string
credentialType string
oidcCredentialFormat vcsverifiable.OIDCFormat
credentialConfigurationID string
clientID string
scopes []string
redirectURI string
Expand Down Expand Up @@ -206,7 +205,6 @@ func NewFlow(p provider, opts ...Opt) (*Flow, error) {
credentialOffer: o.credentialOffer,
credentialType: o.credentialType,
oidcCredentialFormat: o.oidcCredentialFormat,
credentialConfigurationID: o.credentialConfigurationID,
clientID: o.clientID,
scopes: o.scopes,
redirectURI: o.redirectURI,
Expand Down Expand Up @@ -283,7 +281,7 @@ func (f *Flow) Run(ctx context.Context) (*verifiable.Credential, error) {

var authCode string

authCode, err = f.getAuthorizationCode(oauthClient, issuerState)
authCode, err = f.getAuthorizationCode(oauthClient, credentialOfferResponse, issuerState)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -445,11 +443,14 @@ func (f *Flow) parseCredentialOfferURI(uri string) (*oidc4ci.CredentialOfferResp
return credentialOfferResponse, nil
}

func (f *Flow) getAuthorizationCode(oauthClient *oauth2.Config, issuerState string) (string, error) {
func (f *Flow) getAuthorizationCode(
oauthClient *oauth2.Config,
credentialOfferResponse *oidc4ci.CredentialOfferResponse,
issuerState string,
) (string, error) {
slog.Info("Getting authorization code",
"client_id", oauthClient.ClientID,
"scopes", oauthClient.Scopes,
"credential_configuration_id", f.credentialConfigurationID,
"format", f.oidcCredentialFormat,
"redirect_uri", oauthClient.RedirectURL,
"authorization_endpoint", oauthClient.Endpoint.AuthURL,
Expand Down Expand Up @@ -486,7 +487,7 @@ func (f *Flow) getAuthorizationCode(oauthClient *oauth2.Config, issuerState stri
oauth2.SetAuthURLParam("code_challenge_method", "S256"),
}

authorizationDetailsRequestBody, err := f.getAuthorizationDetailsRequestBody(f.credentialConfigurationID,
authorizationDetailsRequestBody, err := f.getAuthorizationDetailsRequestBody(credentialOfferResponse,
f.credentialType, f.oidcCredentialFormat)
if err != nil {
return "", fmt.Errorf("getAuthorizationDetailsRequestBody: %w", err)
Expand Down Expand Up @@ -951,21 +952,25 @@ func (f *Flow) handleIssuanceAck(
//
// Spec: https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-wg-draft.html#section-5.1.1
func (f *Flow) getAuthorizationDetailsRequestBody(
credentialConfigurationID, credentialType string, oidcCredentialFormat vcsverifiable.OIDCFormat,
credentialOfferResponse *oidc4ci.CredentialOfferResponse,
credentialType string,
oidcCredentialFormat vcsverifiable.OIDCFormat,
) ([]byte, error) {
res := make([]common.AuthorizationDetails, 1) // We do not support multiple authorization details for now.
res := make([]common.AuthorizationDetails, 0)

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 credentialOfferResponse != nil: // Priority 1. Based on credentialConfigurationID.
for _, credentialConfigurationID := range credentialOfferResponse.CredentialConfigurationIDs {
res = append(res, 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{
res = append(res, common.AuthorizationDetails{
CredentialConfigurationId: nil,
CredentialDefinition: &common.CredentialDefinition{
Context: nil, // Not supported for now.
Expand All @@ -978,7 +983,7 @@ func (f *Flow) getAuthorizationDetailsRequestBody(
Format: lo.ToPtr(string(oidcCredentialFormat)),
Locations: nil, // Not supported for now.
Type: "openid_credential",
}
})
default:
// Valid case - neither credentialFormat nor credentialConfigurationID supplied.
return nil, nil
Expand Down Expand Up @@ -1036,7 +1041,6 @@ type options struct {
credentialOffer string
credentialType string
oidcCredentialFormat vcsverifiable.OIDCFormat
credentialConfigurationID string
clientID string
scopes []string
redirectURI string
Expand Down Expand Up @@ -1133,10 +1137,3 @@ 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
}
}
2 changes: 1 addition & 1 deletion pkg/restapi/v1/issuer/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ func (c *Controller) prepareClaimDataAuthorizationRequest(
body *PrepareClaimDataAuthorizationRequest,
) (*PrepareClaimDataAuthorizationResponse, error) {
var (
ad *oidc4ci.AuthorizationDetails
ad []*oidc4ci.AuthorizationDetails
err error
)

Expand Down
3 changes: 1 addition & 2 deletions pkg/restapi/v1/oidc4ci/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,7 @@ func (c *Controller) OidcAuthorize(e echo.Context, params OidcAuthorizeParams) e
return err
}

// only single authorization_details supported for now.
prepareAuthRequestAuthorizationDetails = lo.ToPtr(authorizationDetails[:1])
prepareAuthRequestAuthorizationDetails = lo.ToPtr(authorizationDetails)
}

r, err := c.issuerInteractionClient.PrepareAuthorizationRequest(ctx,
Expand Down
77 changes: 41 additions & 36 deletions pkg/restapi/v1/util/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package util

import (
"errors"
vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable"

"github.com/samber/lo"

Expand All @@ -17,56 +18,60 @@ import (
)

func ValidateAuthorizationDetails(
authorizationDetails []common.AuthorizationDetails) (*oidc4ci.AuthorizationDetails, error) {
authorizationDetails []common.AuthorizationDetails) ([]*oidc4ci.AuthorizationDetails, error) {
if len(authorizationDetails) != 1 {
return nil, resterr.NewOIDCError("invalid_request",
errors.New("only single authorization_details supported"))
}

ad := authorizationDetails[0]
var authorizationDetailsDomain []*oidc4ci.AuthorizationDetails

if ad.Type != "openid_credential" {
return nil, resterr.NewValidationError(resterr.InvalidValue, "authorization_details.type",
errors.New("type should be 'openid_credential'"))
}

oidcCredentialFormat := lo.FromPtr(ad.Format)
credentialConfigurationID := lo.FromPtr(ad.CredentialConfigurationId)
for _, ad := range authorizationDetails {
if ad.Type != "openid_credential" {
return nil, resterr.NewValidationError(resterr.InvalidValue, "authorization_details.type",
errors.New("type should be 'openid_credential'"))
}

mapped := &oidc4ci.AuthorizationDetails{
Type: ad.Type,
Locations: lo.FromPtr(ad.Locations),
CredentialConfigurationID: "",
Format: "",
CredentialDefinition: nil,
}
oidcCredentialFormat := lo.FromPtr(ad.Format)
credentialConfigurationID := lo.FromPtr(ad.CredentialConfigurationId)

switch {
case credentialConfigurationID != "": // Priority 1. Based on credentialConfigurationID.
mapped.CredentialConfigurationID = credentialConfigurationID
case oidcCredentialFormat != "": // Priority 2. Based on credentialFormat.
vcsCredentialFormat, err := common.ValidateVCFormat(common.VCFormat(oidcCredentialFormat))
if err != nil {
return nil, resterr.NewValidationError(resterr.InvalidValue, "authorization_details.format", err)
mapped := &oidc4ci.AuthorizationDetails{
Type: ad.Type,
Locations: lo.FromPtr(ad.Locations),
CredentialConfigurationID: "",
Format: "",
CredentialDefinition: nil,
}

mapped.Format = vcsCredentialFormat
switch {
case credentialConfigurationID != "": // Priority 1. Based on credentialConfigurationID.
mapped.CredentialConfigurationID = credentialConfigurationID
case oidcCredentialFormat != "": // Priority 2. Based on credentialFormat.
_, err := common.ValidateVCFormat(common.VCFormat(oidcCredentialFormat))
if err != nil {
return nil, resterr.NewValidationError(resterr.InvalidValue, "authorization_details.format", err)
}

if ad.CredentialDefinition == nil {
mapped.Format = vcsverifiable.OIDCFormat(oidcCredentialFormat)

if ad.CredentialDefinition == nil {
return nil, resterr.NewValidationError(resterr.InvalidValue,
"authorization_details.credential_definition", errors.New("not supplied"))
}

mapped.CredentialDefinition = &oidc4ci.CredentialDefinition{
Context: lo.FromPtr(ad.CredentialDefinition.Context),
CredentialSubject: lo.FromPtr(ad.CredentialDefinition.CredentialSubject),
Type: ad.CredentialDefinition.Type,
}
default:
return nil, resterr.NewValidationError(resterr.InvalidValue,
"authorization_details.credential_definition", errors.New("not supplied"))
"authorization_details.credential_configuration_id",
errors.New("neither credentialFormat nor credentialConfigurationID supplied"))
}

mapped.CredentialDefinition = &oidc4ci.CredentialDefinition{
Context: lo.FromPtr(ad.CredentialDefinition.Context),
CredentialSubject: lo.FromPtr(ad.CredentialDefinition.CredentialSubject),
Type: ad.CredentialDefinition.Type,
}
default:
return nil, resterr.NewValidationError(resterr.InvalidValue,
"authorization_details.credential_configuration_id",
errors.New("neither credentialFormat nor credentialConfigurationID supplied"))
authorizationDetailsDomain = append(authorizationDetailsDomain, mapped)
}

return mapped, nil
return authorizationDetailsDomain, nil
}
23 changes: 14 additions & 9 deletions pkg/service/oidc4ci/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ type TransactionStore transactionStore

// TransactionData is the transaction data stored in the underlying storage.
type TransactionData struct {
ProfileID profileapi.ID
ProfileVersion profileapi.Version
CredentialFormat vcsverifiable.Format // Format, that represents issued VC format (JWT, LDP).
ProfileID profileapi.ID
ProfileVersion profileapi.Version
//CredentialFormat vcsverifiable.Format // Format, that represents issued VC format (JWT, LDP).
IsPreAuthFlow bool
PreAuthCode string
OrgID string
Expand All @@ -84,7 +84,7 @@ type TransactionData struct {
UserPin string
DID string
WalletInitiatedIssuance bool
CredentialConfiguration map[string]TxCredentialConfiguration
CredentialConfiguration map[string]*TxCredentialConfiguration
}

type TxCredentialConfiguration struct {
Expand All @@ -96,15 +96,18 @@ type TxCredentialConfiguration struct {
CredentialDescription string
CredentialExpiresAt *time.Time
PreAuthCodeExpiresAt *time.Time
AuthorizationDetails *AuthorizationDetails
// AuthorizationDetails may be defined on Authorization Request via using "authorization_details" parameter.
// If "scope" param is used, this field will stay empty.
AuthorizationDetails *AuthorizationDetails
}

// AuthorizationDetails represents the domain model for Authorization Details request.
// This object is used to convey the details about the Credentials the Wallet wants to obtain.
//
// Spec: https://openid.github.io/OpenID4VCI/openid-4-verifiable-credential-issuance-wg-draft.html#section-5.1.1
type AuthorizationDetails struct {
Type string
Format vcsverifiable.Format
Format vcsverifiable.OIDCFormat
Locations []string
CredentialConfigurationID string
CredentialDefinition *CredentialDefinition
Expand Down Expand Up @@ -200,7 +203,7 @@ type PrepareClaimDataAuthorizationRequest struct {
ResponseType string
Scope []string
OpState string
AuthorizationDetails *AuthorizationDetails
AuthorizationDetails []*AuthorizationDetails
}

type PrepareClaimDataAuthorizationResponse struct {
Expand Down Expand Up @@ -301,7 +304,7 @@ type ServiceInterface interface {
req *InitiateIssuanceRequest,
profile *profileapi.Issuer,
) (*InitiateIssuanceResponse, error)
PushAuthorizationDetails(ctx context.Context, opState string, ad *AuthorizationDetails) error
PushAuthorizationDetails(ctx context.Context, opState string, ad []*AuthorizationDetails) error
PrepareClaimDataAuthorizationRequest(
ctx context.Context,
req *PrepareClaimDataAuthorizationRequest,
Expand Down Expand Up @@ -348,7 +351,9 @@ type AckRemote struct {
}

type ExchangeAuthorizationCodeResult struct {
TxID TxID
TxID TxID
// AuthorizationDetails REQUIRED when authorization_details parameter is used to request issuance
// of a certain Credential type in Authorization Request. It MUST NOT be used otherwise.
AuthorizationDetails *AuthorizationDetails
}

Expand Down
Loading

0 comments on commit 73b2e0f

Please sign in to comment.