Skip to content

Commit

Permalink
feat: oid4vci credential configuration and metadata endpoints (#1021)
Browse files Browse the repository at this point in the history
Signed-off-by: Pat Losoponkul <pat.losoponkul@iohk.io>
  • Loading branch information
patlo-iog committed May 8, 2024
1 parent 9a0a690 commit 28e2a0e
Show file tree
Hide file tree
Showing 74 changed files with 965 additions and 432 deletions.
11 changes: 11 additions & 0 deletions .mega-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ DISABLE_LINTERS:
- CPP_CPPLINT # For pollux/lib/anoncreds/src/main/c
- JAVA_CHECKSTYLE # For pollux/lib/anoncreds/src/main/java
- GHERKIN_GHERKIN_LINT
# For python, disable all except PYTHON_BLACK linter
- PYTHON_PYLINT
- PYTHON_FLAKE8
- PYTHON_ISORT
- PYTHON_BANDIT
- PYTHON_MYPY
- PYTHON_PYRIGHT
- PYTHON_RUFF
# TODO: revert before merging to `main`. Disabled to ease the development of keycloak extension
- JAVA_CHECKSTYLE
- JAVA_PMD

DISABLE_ERRORS_LINTERS:
- KOTLIN_KTLINT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import org.hyperledger.identus.iam.entity.http.EntityServerEndpoints
import org.hyperledger.identus.iam.wallet.http.WalletManagementServerEndpoints
import org.hyperledger.identus.issue.controller.IssueServerEndpoints
import org.hyperledger.identus.mercury.{DidOps, HttpClient}
import org.hyperledger.identus.oidc4vc.CredentialIssuerServerEndpoints
import org.hyperledger.identus.oid4vci.CredentialIssuerServerEndpoints
import org.hyperledger.identus.pollux.core.service.{CredentialService, PresentationService}
import org.hyperledger.identus.pollux.credentialdefinition.CredentialDefinitionRegistryServerEndpoints
import org.hyperledger.identus.pollux.credentialschema.{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ import org.hyperledger.identus.iam.entity.http.controller.{EntityController, Ent
import org.hyperledger.identus.iam.wallet.http.controller.WalletManagementControllerImpl
import org.hyperledger.identus.issue.controller.IssueControllerImpl
import org.hyperledger.identus.mercury.*
import org.hyperledger.identus.oidc4vc.controller.CredentialIssuerControllerImpl
import org.hyperledger.identus.oidc4vc.service.OIDCCredentialIssuerServiceImpl
import org.hyperledger.identus.oidc4vc.storage.InMemoryIssuanceSessionService
import org.hyperledger.identus.pollux.core.repository.InMemoryOIDC4VCIssuerMetadataRepository
import org.hyperledger.identus.oid4vci.controller.CredentialIssuerControllerImpl
import org.hyperledger.identus.oid4vci.service.OIDCCredentialIssuerServiceImpl
import org.hyperledger.identus.oid4vci.storage.InMemoryIssuanceSessionService
import org.hyperledger.identus.pollux.core.service.*
import org.hyperledger.identus.pollux.core.service.verification.VcVerificationServiceImpl
import org.hyperledger.identus.pollux.credentialdefinition.controller.CredentialDefinitionControllerImpl
Expand All @@ -46,6 +45,7 @@ import org.hyperledger.identus.pollux.credentialschema.controller.{
CredentialSchemaControllerImpl,
VerificationPolicyControllerImpl
}
import org.hyperledger.identus.pollux.sql.repository.JdbcOID4VCIIssuerMetadataRepository
import org.hyperledger.identus.pollux.sql.repository.{
JdbcCredentialDefinitionRepository,
JdbcCredentialRepository,
Expand Down Expand Up @@ -200,12 +200,12 @@ object MainApp extends ZIOAppDefault {
RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcCredentialSchemaRepository.layer,
RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcCredentialDefinitionRepository.layer,
RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcPresentationRepository.layer,
RepoModule.polluxContextAwareTransactorLayer ++ RepoModule.polluxTransactorLayer >>> JdbcOID4VCIIssuerMetadataRepository.layer,
RepoModule.polluxContextAwareTransactorLayer >>> JdbcVerificationPolicyRepository.layer,
// oidc
CredentialIssuerControllerImpl.layer,
InMemoryIssuanceSessionService.layer,
InMemoryOIDC4VCIssuerMetadataRepository.layer,
OIDC4VCIssuerMetadataServiceImpl.layer,
OID4VCIIssuerMetadataServiceImpl.layer,
OIDCCredentialIssuerServiceImpl.layer,
// event notification service
ZLayer.succeed(500) >>> EventNotificationServiceImpl.layer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.hyperledger.identus.oidc4vc
package org.hyperledger.identus.oid4vci

import org.hyperledger.identus.api.http.{EndpointOutputs, ErrorResponse, RequestContext}
import org.hyperledger.identus.castor.controller.http.DIDInput
Expand All @@ -7,7 +7,7 @@ import org.hyperledger.identus.iam.authentication.apikey.ApiKeyCredentials
import org.hyperledger.identus.iam.authentication.apikey.ApiKeyEndpointSecurityLogic.apiKeyHeader
import org.hyperledger.identus.iam.authentication.oidc.JwtCredentials
import org.hyperledger.identus.iam.authentication.oidc.JwtSecurityLogic.jwtAuthHeader
import org.hyperledger.identus.oidc4vc.http.*
import org.hyperledger.identus.oid4vci.http.*
import sttp.apispec.Tag
import sttp.model.StatusCode
import sttp.tapir.*
Expand All @@ -17,7 +17,7 @@ import java.util.UUID

object CredentialIssuerEndpoints {

private val tagName = "OIDC Credential Issuer"
private val tagName = "OpenID for Verifiable Credential Issuance"
private val tagDescription =
s"""
|The __${tagName}__ is a service that issues credentials to users by implementing the [OIDC for Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html) specification.
Expand All @@ -32,13 +32,17 @@ object CredentialIssuerEndpoints {
type ExtendedErrorResponse = Either[ErrorResponse, CredentialErrorResponse]

private val issuerIdPathSegment = path[UUID]("issuerId")
.description("An issuer identifier in the oidc4vc protocol")
.description("An issuer identifier in the oid4vci protocol")
.example(UUID.fromString("f47ac10b-58cc-4372-a567-0e02b2c3d479"))

private val credentialConfigIdSegment = path[String]("credentialConfigId")
.description("An identifier for the credential configuration")
.example("UniversityDegree")

private val baseEndpoint = endpoint
.tag(tagName)
.in(extractFromRequest[RequestContext](RequestContext.apply))
.in("oidc4vc" / "issuers")
.in("oid4vci" / "issuers")

private val baseIssuerPrivateEndpoint = baseEndpoint
.securityIn(apiKeyHeader)
Expand Down Expand Up @@ -96,6 +100,10 @@ object CredentialIssuerEndpoints {
.errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden)
.name("createCredentialOffer")
.summary("Create a new credential offer")
.description(
"""Create a new credential offer and return a compliant `CredentialOffer` for the holder's
|[Credential Offer Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer-endpoint).""".stripMargin
)

val nonceEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
Expand Down Expand Up @@ -126,13 +134,56 @@ object CredentialIssuerEndpoints {
] = baseIssuerPrivateEndpoint.post
.in(jsonBody[CreateCredentialIssuerRequest])
.out(
statusCode(StatusCode.Created).description("Credential Issuer created successfully")
statusCode(StatusCode.Created).description("Credential issuer created successfully")
)
.out(jsonBody[CredentialIssuer])
.errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden)
.name("createCredentialIssuer")
.summary("Create a new credential issuer")

val getCredentialIssuersEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
RequestContext,
ErrorResponse,
CredentialIssuerPage,
Any
] = baseIssuerPrivateEndpoint.get
.errorOut(EndpointOutputs.basicFailuresAndForbidden)
.out(statusCode(StatusCode.Ok).description("List the credential issuers"))
.out(jsonBody[CredentialIssuerPage])
.name("getCredentialIssuers")
.summary("List all credential issuers")

val updateCredentialIssuerEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
(RequestContext, UUID, PatchCredentialIssuerRequest),
ErrorResponse,
CredentialIssuer,
Any
] = baseIssuerPrivateEndpoint.patch
.in(issuerIdPathSegment)
.in(jsonBody[PatchCredentialIssuerRequest])
.out(
statusCode(StatusCode.Ok).description("Credential issuer updated successfully")
)
.out(jsonBody[CredentialIssuer])
.errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden)
.name("updateCredentialIssuer")
.summary("Update the credential issuer")

val deleteCredentialIssuerEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
(RequestContext, UUID),
ErrorResponse,
Unit,
Any
] = baseIssuerPrivateEndpoint.delete
.in(issuerIdPathSegment)
.errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden)
.out(statusCode(StatusCode.Ok).description("Credential issuer deleted successfully"))
.name("deleteCredentialIssuer")
.summary("Delete the credential issuer")

val createCredentialConfigurationEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
(RequestContext, UUID, CreateCredentialConfigurationRequest),
Expand All @@ -149,6 +200,42 @@ object CredentialIssuerEndpoints {
.errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden)
.name("createCredentialConfiguration")
.summary("Create a new credential configuration")
.description(
"""Create a new credential configuration for the issuer.
|It represents the configuration of the credential that can be issued by the issuer.
|This credential configuration object will be displayed in the credential issuer metadata.""".stripMargin
)

val getCredentialConfigurationEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
(RequestContext, UUID, String),
ErrorResponse,
CredentialConfiguration,
Any
] = baseIssuerPrivateEndpoint.get
.in(issuerIdPathSegment / "credential-configurations" / credentialConfigIdSegment)
.out(
statusCode(StatusCode.Ok).description("Get credential configuration successfully")
)
.out(jsonBody[CredentialConfiguration])
.errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden)
.name("getCredentialConfiguration")
.summary("Get the credential configuration")

val deleteCredentialConfigurationEndpoint: Endpoint[
(ApiKeyCredentials, JwtCredentials),
(RequestContext, UUID, String),
ErrorResponse,
Unit,
Any
] = baseIssuerPrivateEndpoint.delete
.in(issuerIdPathSegment / "credential-configurations" / credentialConfigIdSegment)
.out(
statusCode(StatusCode.Ok).description("Credential configuration deleted successfully")
)
.errorOut(EndpointOutputs.basicFailureAndNotFoundAndForbidden)
.name("deleteCredentialConfiguration")
.summary("Delete the credential configuration")

val issuerMetadataEndpoint: Endpoint[
Unit,
Expand All @@ -164,6 +251,6 @@ object CredentialIssuerEndpoints {
.out(jsonBody[IssuerMetadata])
.errorOut(EndpointOutputs.basicFailuresAndNotFound)
.name("getIssuerMetadata")
.summary("Get oidc4vc credential issuer metadata")
.summary("Get the credential issuer metadata")

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.hyperledger.identus.oidc4vc
package org.hyperledger.identus.oid4vci

import org.hyperledger.identus.LogUtils.*
import org.hyperledger.identus.agent.walletapi.model.BaseEntity
import org.hyperledger.identus.api.http.{ErrorResponse, RequestContext}
import org.hyperledger.identus.iam.authentication.{Authenticator, Authorizer, DefaultAuthenticator, SecurityLogic}
import org.hyperledger.identus.oidc4vc.controller.CredentialIssuerController
import org.hyperledger.identus.oidc4vc.http.{CredentialErrorResponse, CredentialRequest, NonceResponse}
import org.hyperledger.identus.oid4vci.controller.CredentialIssuerController
import org.hyperledger.identus.oid4vci.http.{CredentialErrorResponse, CredentialRequest, NonceResponse}
import sttp.tapir.ztapir.*
import zio.*

Expand Down Expand Up @@ -69,6 +69,42 @@ case class CredentialIssuerServerEndpoints(
}
}

val getCredentialIssuersServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.getCredentialIssuersEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic { wac =>
{ case rc =>
credentialIssuerController
.getCredentialIssuers(rc)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
}

val updateCredentialIssuerServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.updateCredentialIssuerEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic { wac =>
{ case (rc, issuerId, request) =>
credentialIssuerController
.updateCredentialIssuer(rc, issuerId, request)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
}

val deleteCredentialIssuerServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.deleteCredentialIssuerEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic { wac =>
{ case (rc, issuerId) =>
credentialIssuerController
.deleteCredentialIssuer(rc, issuerId)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
}

val createCredentialConfigurationServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.createCredentialConfigurationEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
Expand All @@ -81,6 +117,30 @@ case class CredentialIssuerServerEndpoints(
}
}

val getCredentialConfigurationServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.getCredentialConfigurationEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic { wac =>
{ case (rc, issuerId, configurationId) =>
credentialIssuerController
.getCredentialConfiguration(rc, issuerId, configurationId)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
}

val deleteCredentialConfigurationServerEndpoint: ZServerEndpoint[Any, Any] =
CredentialIssuerEndpoints.deleteCredentialConfigurationEndpoint
.zServerSecurityLogic(SecurityLogic.authorizeWalletAccessWith(_)(authenticator, authorizer))
.serverLogic { wac =>
{ case (rc, issuerId, configurationId) =>
credentialIssuerController
.deleteCredentialConfiguration(rc, issuerId, configurationId)
.provideSomeLayer(ZLayer.succeed(wac))
.logTrace(rc)
}
}

val issuerMetadataServerEndpoint: ZServerEndpoint[Any, Any] = CredentialIssuerEndpoints.issuerMetadataEndpoint
.zServerLogic {
{ case (rc, didRef) => credentialIssuerController.getIssuerMetadata(rc, didRef).logTrace(rc) }
Expand All @@ -91,7 +151,12 @@ case class CredentialIssuerServerEndpoints(
createCredentialOfferServerEndpoint,
nonceServerEndpoint,
createCredentialIssuerServerEndpoint,
getCredentialIssuersServerEndpoint,
updateCredentialIssuerServerEndpoint,
deleteCredentialIssuerServerEndpoint,
createCredentialConfigurationServerEndpoint,
getCredentialConfigurationServerEndpoint,
deleteCredentialConfigurationServerEndpoint,
issuerMetadataServerEndpoint
)
}
Expand Down

0 comments on commit 28e2a0e

Please sign in to comment.