Skip to content

Commit

Permalink
feat: encrypted credential response
Browse files Browse the repository at this point in the history
Signed-off-by: Andrii Holovko <andriy.holovko@gmail.com>
  • Loading branch information
aholovko committed Jan 24, 2024
1 parent 576d6cb commit 07722e3
Show file tree
Hide file tree
Showing 6 changed files with 477 additions and 198 deletions.
342 changes: 172 additions & 170 deletions api/spec/openapi.gen.go

Large diffs are not rendered by default.

69 changes: 47 additions & 22 deletions docs/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,9 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CredentialResponse'
application/jwt:
schema:
type: string
operationId: oidc-credential
description: Issues credentials in exchange for an authorization token.
requestBody:
Expand All @@ -812,7 +815,7 @@ paths:
- oidc4ci
responses:
'204':
description: Ok
description: Ok
'400':
description: Error
content:
Expand All @@ -826,7 +829,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/AckRequest'
parameters: [ ]
parameters: []
components:
schemas:
HealthCheckResponse:
Expand Down Expand Up @@ -919,7 +922,7 @@ components:
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'
$ref: '#/components/schemas/CredentialResponseEncryptionSupported'
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."
Expand Down Expand Up @@ -1794,8 +1797,8 @@ components:
type: string
description: The "aud" claim received from the client.
hashed_token:
type: string
description: Hashed token received from the client.
type: string
description: Hashed token received from the client.
required:
- tx_id
- types
Expand All @@ -1819,8 +1822,8 @@ components:
type: string
description: OIDC credential format
ack_id:
type: string
description: String identifying an issued Credential that the Wallet includes in the acknowledgement request.
type: string
description: String identifying an issued Credential that the Wallet includes in the acknowledgement request.
retry:
type: boolean
description: 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).
Expand All @@ -1838,16 +1841,38 @@ components:
properties:
types:
type: array
description: Array of types of the credential being issued.
items:
type: string
description: Array of types of the credential being issued.
format:
type: string
description: Format of the credential being issued.
proof:
$ref: '#/components/schemas/JWTProof'
credential_response_encryption:
$ref: '#/components/schemas/CredentialResponseEncryption'
required:
- types
CredentialResponseEncryption:
title: CredentialResponseEncryption
x-tags:
- oidc4ci
type: object
description: Object containing information for encrypting the Credential Response.
properties:
jwk:
type: string
description: Object containing a single public key as a JWK used for encrypting the Credential Response.
alg:
type: string
description: JWE alg algorithm for encrypting the Credential Response.
enc:
type: string
description: JWE enc algorithm for encrypting the Credential Response.
required:
- jwk
- alg
- enc
JWTProof:
title: JWTProof
x-tags:
Expand All @@ -1873,7 +1898,7 @@ components:
credentials:
type: array
items:
$ref: '#/components/schemas/AcpRequestItem'
$ref: '#/components/schemas/AcpRequestItem'
required:
- credentials
AcpRequestItem:
Expand Down Expand Up @@ -1955,17 +1980,17 @@ components:
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.
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'
$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.
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.
Expand All @@ -1974,7 +1999,7 @@ components:
required:
- format
CredentialConfigurationsSupportedDefinition:
title: CredentialSupported.CredentialDefinition object definition.
title: CredentialConfigurationsSupportedDefinition object definition.
x-tags:
- issuer
type: object
Expand All @@ -1986,11 +2011,11 @@ components:
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.
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:
CredentialResponseEncryptionSupported:
title: CredentialResponseEncryption object definition.
x-tags:
- issuer
Expand Down
4 changes: 2 additions & 2 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.

65 changes: 62 additions & 3 deletions pkg/restapi/v1/oidc4ci/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ func (c *Controller) OidcAcknowledgement(e echo.Context) error {
}

// OidcCredential handles OIDC credential request (POST /oidc/credential).
func (c *Controller) OidcCredential(e echo.Context) error {
func (c *Controller) OidcCredential(e echo.Context) error { //nolint:funlen
req := e.Request()

ctx, span := c.tracer.Start(req.Context(), "OidcCredential")
Expand Down Expand Up @@ -715,13 +715,35 @@ func (c *Controller) OidcCredential(e echo.Context) error {
session.Extra[cNonceKey] = nonce
session.Extra[cNonceExpiresAtKey] = time.Now().Add(cNonceTTL).Unix()

return apiUtil.WriteOutput(e)(CredentialResponse{
credentialResponse := &CredentialResponse{
Credential: result.Credential,
Format: result.OidcFormat,
CNonce: lo.ToPtr(nonce),
CNonceExpiresIn: lo.ToPtr(int(cNonceTTL.Seconds())),
AckId: result.AckId,
}, nil)
}

if credentialRequest.CredentialResponseEncryption != nil {
var encryptedResponse string

if encryptedResponse, err = encryptCredentialResponse(
credentialResponse,
credentialRequest.CredentialResponseEncryption,
); err != nil {
return fmt.Errorf("encrypt credential response: %w", err)
}

e.Response().Header().Set("Content-Type", "application/jwt")
e.Response().WriteHeader(http.StatusOK)

if _, err = e.Response().Write([]byte(encryptedResponse)); err != nil {
return err

Check warning on line 740 in pkg/restapi/v1/oidc4ci/controller.go

View check run for this annotation

Codecov / codecov/patch

pkg/restapi/v1/oidc4ci/controller.go#L740

Added line #L740 was not covered by tests
}

return nil
}

return apiUtil.WriteOutput(e)(credentialResponse, nil)
}

func validateCredentialRequest(e echo.Context, req *CredentialRequest) error {
Expand All @@ -745,6 +767,43 @@ func validateCredentialRequest(e echo.Context, req *CredentialRequest) error {
return nil
}

func encryptCredentialResponse(resp *CredentialResponse, enc *CredentialResponseEncryption) (string, error) {
var jwk gojose.JSONWebKey

if err := json.Unmarshal([]byte(enc.Jwk), &jwk); err != nil {
return "", fmt.Errorf("unmarshal jwk: %w", err)
}

encrypter, err := gojose.NewEncrypter(
gojose.ContentEncryption(enc.Enc),
gojose.Recipient{
Algorithm: gojose.KeyAlgorithm(enc.Alg),
Key: &jwk,
},
nil,
)
if err != nil {
return "", fmt.Errorf("create encrypter: %w", err)
}

b, err := json.Marshal(resp)
if err != nil {
return "", fmt.Errorf("marshal credential response: %w", err)

Check warning on line 791 in pkg/restapi/v1/oidc4ci/controller.go

View check run for this annotation

Codecov / codecov/patch

pkg/restapi/v1/oidc4ci/controller.go#L791

Added line #L791 was not covered by tests
}

encrypted, err := encrypter.Encrypt(b)
if err != nil {
return "", fmt.Errorf("encrypt credential response: %w", err)

Check warning on line 796 in pkg/restapi/v1/oidc4ci/controller.go

View check run for this annotation

Codecov / codecov/patch

pkg/restapi/v1/oidc4ci/controller.go#L796

Added line #L796 was not covered by tests
}

jwe, err := encrypted.CompactSerialize()
if err != nil {
return "", fmt.Errorf("serialize credential response: %w", err)

Check warning on line 801 in pkg/restapi/v1/oidc4ci/controller.go

View check run for this annotation

Codecov / codecov/patch

pkg/restapi/v1/oidc4ci/controller.go#L801

Added line #L801 was not covered by tests
}

return jwe, nil
}

//nolint:gocognit
func (c *Controller) validateProofClaims(
clientID string,
Expand Down
Loading

0 comments on commit 07722e3

Please sign in to comment.