Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: oid4vci credential configuration and metadata endpoints #1021

Merged
merged 25 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading
Loading