Skip to content

Commit

Permalink
feat: update api according to ID1 spec (#1824)
Browse files Browse the repository at this point in the history
* feat: update api according to ID1 spec

* fix: coverage

* fix: lint

* fix: ack

* fix: notification as required

* feat: vp flow

* fix: lint
  • Loading branch information
skynet2 authored Dec 19, 2024
1 parent 33a03e3 commit c3888ab
Show file tree
Hide file tree
Showing 11 changed files with 301 additions and 368 deletions.
4 changes: 2 additions & 2 deletions .codecov.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ coverage:
status:
project:
default:
target: 85%
target: 70%
patch:
default:
target: 85%
target: 75%
only_pulls: true

ignore:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ the Issuer and Verifier role defined in the [W3C VC Specification](https://www.w
The TrustBloc VCS implements following specifications.
- W3C [Verifiable Credential Data Model (VCDM)](https://www.w3.org/TR/vc-data-model/)
- W3C [Decentralized Identifier (DID)](https://www.w3.org/TR/did-core/)
- OIDF [OpenID for Verifiable Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html)
- OIDF [OpenID for Verifiable Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID1.html)
- Pre Authorization Code flow
- Authorization Code low
- OIDF [OpenID for Verifiable Presentation](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html)
- OIDF [OpenID for Verifiable Presentation](https://openid.net/specs/openid-4-verifiable-presentations-1_0-ID2.html)
- DIF [Presentation Exchange](https://identity.foundation/presentation-exchange/)
- DIF [Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration/)

Expand Down
419 changes: 209 additions & 210 deletions api/spec/openapi.gen.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions component/wallet-cli/pkg/oidc4vci/oidc4vci_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -1172,13 +1172,13 @@ func (f *Flow) handleIssuanceAck(
slog.Debug("Sending wallet notification", "notification_id", notificationID, "endpoint", notificationEndpoint)

ackRequest := oidc4civ1.AckRequest{
Event: lo.ToPtr("credential_accepted"),
Event: "credential_accepted",
EventDescription: nil,
IssuerIdentifier: wellKnown.CredentialIssuer,
InteractionDetails: lo.ToPtr(map[string]interface{}{
"notification_id": notificationID,
}),
NotificationId: lo.ToPtr(notificationID),
NotificationId: notificationID,
}

b, err := json.Marshal(ackRequest)
Expand Down
68 changes: 16 additions & 52 deletions docs/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ paths:
$ref: '#/components/schemas/WellKnownOpenIDIssuerConfiguration'
operationId: openid-credential-issuer-config
description: Returns openid-config.
deprecated: true
tags:
- issuer
'/oidc/idp/{profileID}/{profileVersion}/.well-known/openid-credential-issuer':
Expand Down Expand Up @@ -2273,19 +2272,16 @@ components:
description: Model for Prepare Credential response.
properties:
credential:
deprecated: true
description: Deprecated. Credential value.
description: Credential value. According to draft 13 spec.
oneOf:
- type: string
- type: object
format:
deprecated: true
type: string
description: Deprecated. Format of issued credential.
description: Format of issued credential. Internal use only.
oidc_format:
deprecated: true
type: string
description: Deprecated. OIDC credential format
description: OIDC credential format. Internal use only.
credentials:
type: array
description: Contains an array of one or more issued Credentials.
Expand All @@ -2296,13 +2292,12 @@ components:
description: String identifying an issued Credential that the Wallet includes in the acknowledgement request.
retry:
type: boolean
deprecated: true
description: Deprecated. TRUE if claim data is not yet available in the issuer OP server. This will indicate VCS OIDC to issue acceptance_token instead of credential response (Deferred Credential flow).
description: Boolean value indicating whether the Wallet should retry the issuance request. Internal Use. Used for deferred issuance.
required:
- credential
- format
- oidc_format
- retry
- oidc_format
- notification_id
- credentials
CredentialRequest:
Expand Down Expand Up @@ -2385,43 +2380,21 @@ components:
type: object
description: Ack request.
properties:
credentials:
description: Deprecated. Ack request credentials data.
deprecated: true
type: array
items:
$ref: '#/components/schemas/AckRequestItem'
notification_id:
type: string
description: Ack ID.
description: Ack ID. According to draft 13 spec.
issuer_identifier:
type: string
description: Optional issuer identifier.
description: Optional issuer identifier. Proprietary.
event:
type: string
description: Type of the notification event.
description: Type of the notification event. According to draft 13 spec.
event_description:
type: string
description: 'Human-readable ASCII text providing additional information, used to assist the Credential Issuer developer in understanding the event that occurred.'
description: 'Human-readable ASCII text providing additional information, used to assist the Credential Issuer developer in understanding the event that occurred. According to draft 13 spec.'
interaction_details:
type: object
AckRequestItem:
deprecated: true
type: object
description: Deprecated. AckRequestItem
properties:
notification_id:
type: string
description: Deprecated. Ack ID.
event:
type: string
description: Deprecated. Type of the notification event.
issuer_identifier:
type: string
description: Deprecated. Optional issuer identifier.
event_description:
type: string
description: 'Deprecated. Human-readable ASCII text providing additional information, used to assist the Credential Issuer developer in understanding the event that occurred.'
description: Proprietary interaction details.
required:
- notification_id
- event
Expand All @@ -2444,38 +2417,29 @@ components:
type: object
description: Model for OIDC Credential response.
properties:
format:
deprecated: true
type: string
description: Deprecated. JSON string denoting the format of the issued Credential.
credential:
deprecated: true
description: Deprecated. Credential value.
description: Credential value. According to draft 13 spec.
oneOf:
- type: string
- type: object
credentials:
type: array
description: Contains an array of one or more issued Credentials.
description: Contains an array of one or more issued Credentials. According to editor-draft spec.
items:
$ref: "./common.yaml#/components/schemas/CredentialResponseCredentialObject"
acceptance_token:
deprecated: true
transaction_id:
type: string
description: Deprecated. A JSON string containing a token subsequently used to obtain a Credential. MUST be present when credential is not returned.
description: A JSON string containing a token subsequently used to obtain a Credential. MUST be present when credential is not returned.
c_nonce:
deprecated: true
type: string
description: Deprecated. JSON string containing a nonce to be used to create a proof of possession of key material when requesting a Credential.
description: JSON string containing a nonce to be used to create a proof of possession of key material when requesting a Credential. According to draft 13 spec.
c_nonce_expires_in:
deprecated: true
type: integer
description: Deprecated. JSON integer denoting the lifetime in seconds of the c_nonce.
description: JSON integer denoting the lifetime in seconds of the c_nonce. According to draft 13 spec.
notification_id:
type: string
description: String identifying one or more Credentials issued in one Credential Response.
required:
- format
- credential
- credentials
- notification_id
Expand Down
8 changes: 4 additions & 4 deletions pkg/restapi/v1/issuer/openapi.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 9 additions & 32 deletions pkg/restapi/v1/oidc4ci/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,9 +626,6 @@ func mustGenerateNonce() string {
func (c *Controller) OidcAcknowledgement(e echo.Context) error {
req := e.Request()

// ctx, span := c.tracer.Start(req.Context(), "OidcAcknowledgement")
// defer span.End()

var body AckRequest
if err := e.Bind(&body); err != nil {
return err
Expand All @@ -652,34 +649,15 @@ func (c *Controller) OidcAcknowledgement(e echo.Context) error {

var finalErr error

// todo: according to the spec those 2 fields are required, so condition is redundant.
// Should be removed during endpoint update to the latest spec.
if body.NotificationId != nil && body.Event != nil {
if err := c.ackService.Ack(ctx, oidc4ci.AckRemote{
TxID: issuecredential.TxID(lo.FromPtr(body.NotificationId)),
Event: lo.FromPtr(body.Event),
HashedToken: hashedToken,
EventDescription: lo.FromPtr(body.EventDescription),
IssuerIdentifier: lo.FromPtr(body.IssuerIdentifier),
InteractionDetails: interactionDetails,
}); err != nil {
finalErr = errors.Join(finalErr, err)
}
}

// todo: backward compatability code.
// Should be removed during endpoint update to the latest spec.
for _, r := range lo.FromPtr(body.Credentials) {
if err := c.ackService.Ack(ctx, oidc4ci.AckRemote{
TxID: issuecredential.TxID(r.NotificationId),
Event: r.Event,
HashedToken: hashedToken,
EventDescription: lo.FromPtr(r.EventDescription),
IssuerIdentifier: lo.FromPtr(r.IssuerIdentifier),
InteractionDetails: interactionDetails,
}); err != nil {
finalErr = errors.Join(finalErr, err)
}
if err := c.ackService.Ack(ctx, oidc4ci.AckRemote{
TxID: issuecredential.TxID(body.NotificationId),
Event: body.Event,
HashedToken: hashedToken,
EventDescription: lo.FromPtr(body.EventDescription),
IssuerIdentifier: lo.FromPtr(body.IssuerIdentifier),
InteractionDetails: interactionDetails,
}); err != nil {
finalErr = errors.Join(finalErr, err)
}

if finalErr != nil {
Expand Down Expand Up @@ -920,7 +898,6 @@ func (c *Controller) OidcCredential(e echo.Context) error { //nolint:funlen

credentialResp := &CredentialResponse{
Credential: prepareCredentialResult.Credential,
Format: prepareCredentialResult.OidcFormat,
CNonce: lo.ToPtr(nonce),
CNonceExpiresIn: lo.ToPtr(int(cNonceTTL.Seconds())),
NotificationId: prepareCredentialResult.NotificationId,
Expand Down
55 changes: 38 additions & 17 deletions pkg/restapi/v1/oidc4ci/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2989,11 +2989,9 @@ func TestController_OidcBatchCredential(t *testing.T) {
require.NoError(t, json.Unmarshal(decrypted, &resp))

assert.Equal(t, resp, &oidc4ci.CredentialResponse{
AcceptanceToken: nil,
CNonce: nil,
CNonceExpiresIn: nil,
Credential: "credential1 in jwt format",
Format: "",
NotificationId: "notificationId",
})

Expand Down Expand Up @@ -4600,15 +4598,24 @@ func TestController_Ack(t *testing.T) {
return nil
})

req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer([]byte(`{
"credentials" : [{"notification_id" : "tx_id", "event" : "credential_accepted", "event_description" : "err_txt"}],
"interaction_details": {"userId": "userId", "transactionId": "transactionId"}
}`)))
b, err := json.Marshal(oidc4ci.AckRequest{
NotificationId: "tx_id",
Event: "credential_accepted",
EventDescription: lo.ToPtr("err_txt"),
InteractionDetails: lo.ToPtr(map[string]any{
"userId": "userId",
"transactionId": "transactionId",
}),
})
require.NoError(t, err)

req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(b))

req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Set("Authorization", "Bearer xxxx")
rec := httptest.NewRecorder()

err := controller.OidcAcknowledgement(echo.New().NewContext(req, rec))
err = controller.OidcAcknowledgement(echo.New().NewContext(req, rec))
assert.NoError(t, err)
assert.Equal(t, http.StatusNoContent, rec.Code)
})
Expand Down Expand Up @@ -4669,20 +4676,25 @@ func TestController_Ack(t *testing.T) {
ackMock.EXPECT().Ack(gomock.Any(), gomock.Any()).
Return(errors.New("some error"))

req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer([]byte(`{
"credentials" : [{"ack_id" : "tx_id", "status" : "status", "error_description" : "err_txt"}]
}`)))
b, err := json.Marshal(oidc4ci.AckRequest{
NotificationId: "tx_id",
Event: "credential_accepted",
EventDescription: lo.ToPtr("err_txt"),
})
require.NoError(t, err)

req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(b))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Set("Authorization", "Bearer xxxx")

rec := httptest.NewRecorder()

err := controller.OidcAcknowledgement(echo.New().NewContext(req, rec))
err = controller.OidcAcknowledgement(echo.New().NewContext(req, rec))
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, rec.Code)

var bd oidc4ci.AckErrorResponse
b, _ := io.ReadAll(rec.Body)
b, _ = io.ReadAll(rec.Body)

assert.NoError(t, json.Unmarshal(b, &bd))
assert.Equal(t, "some error", bd.Error)
Expand Down Expand Up @@ -4756,20 +4768,29 @@ func TestController_Ack(t *testing.T) {
ackMock.EXPECT().Ack(gomock.Any(), gomock.Any()).
Return(oidc4cisrv.ErrAckExpired)

req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer([]byte(`{
"credentials" : [{"ack_id" : "tx_id", "status" : "status", "error_description" : "err_txt"}]
}`)))
b, err := json.Marshal(oidc4ci.AckRequest{
NotificationId: "tx_id",
Event: "credential_accepted",
EventDescription: lo.ToPtr("err_txt"),
InteractionDetails: lo.ToPtr(map[string]any{
"userId": "userId",
"transactionId": "transactionId",
}),
})
require.NoError(t, err)
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(b))

req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
req.Header.Set("Authorization", "Bearer xxxx")

rec := httptest.NewRecorder()

err := controller.OidcAcknowledgement(echo.New().NewContext(req, rec))
err = controller.OidcAcknowledgement(echo.New().NewContext(req, rec))
assert.NoError(t, err)
assert.Equal(t, http.StatusBadRequest, rec.Code)

var bd oidc4ci.AckErrorResponse
b, _ := io.ReadAll(rec.Body)
b, _ = io.ReadAll(rec.Body)

assert.NoError(t, json.Unmarshal(b, &bd))
assert.Equal(t, "expired_ack_id", bd.Error)
Expand Down
Loading

0 comments on commit c3888ab

Please sign in to comment.