Skip to content

Commit

Permalink
draft-12 updates (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
dzarras authored Nov 30, 2023
1 parent 6476946 commit 7424b1c
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 79 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ and requires the use of a suitable OAUTH2 server.
| [Credential Issuer MetaData](#credential-issuer-metadata) | Yes, using `scopes` |
| Batch Endpoint ||
| Deferred Endpoint ||
| Proof | ✅ JWT (`jwk`, `x5c`) , ❌ CWT |
| Proof | ✅ JWT (`jwk`, `x5c`) , ❌ CWT |

## How to use docker

Expand Down
6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[versions]
coroutines = "1.7.3"
dokka = "1.9.10"
kotlin = "1.9.20"
kotlin = "1.9.21"
arrow = "1.2.1"
foojay = "0.7.0"
springboot = "3.1.5"
springboot = "3.2.0"
springDependencyManagement = "1.1.4"
spotless = "6.22.0"
java = "17"
kotlinxSerialization = "1.6.0"
kotlinxSerialization = "1.6.1"
ktlint = "0.50.0"
nimbusJoseJwt = "9.37.1"
nimbusOAuth2 = "11.6"
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
2 changes: 1 addition & 1 deletion pid-issuer.http
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ GET {{issuer_publicUrl}}/.well-known/openid-credential-issuer
client.global.set("deferred_credential_endpoint", response.body.deferred_credential_endpoint)
%}

### Get sample credentilas offer
### Get sample credentials offer

GET {{issuer_publicUrl}}/issuer/credentialsOffer

Expand Down
40 changes: 27 additions & 13 deletions src/main/kotlin/eu/europa/ec/eudi/pidissuer/PidIssuerApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import org.springframework.security.web.server.authentication.HttpStatusServerEn
import org.springframework.web.reactive.config.EnableWebFlux
import org.springframework.web.reactive.config.WebFluxConfigurer
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.util.UriComponentsBuilder
import reactor.netty.http.client.HttpClient
import java.time.Clock
import java.time.Duration
Expand Down Expand Up @@ -158,22 +159,17 @@ fun beans(clock: Clock) = beans {
// Specific Issuers
//
bean {
val issuerPublicUrl = env.readRequiredUrl("issuer.publicUrl")
val issuerPublicUrl = env.readRequiredUrl("issuer.publicUrl", removeTrailingSlash = true)

bean {
EncodePidInCborWithMicroService(env.readRequiredUrl("issuer.pid.mso_mdoc.encoderUrl"), ref())
}

CredentialIssuerMetaData(
id = issuerPublicUrl,
credentialEndPoint = env.readRequiredUrl("issuer.publicUrl").run {
HttpsUrl.unsafe("${this.value}${WalletApi.CREDENTIAL_ENDPOINT}")
},
deferredCredentialEndpoint = env.readRequiredUrl("issuer.publicUrl").run {
HttpsUrl.unsafe("${this.value}${WalletApi.DEFERRED_ENDPOINT}")
},
authorizationServer = env.readRequiredUrl("issuer.authorizationServer"),

credentialEndPoint = issuerPublicUrl.appendPath(WalletApi.CREDENTIAL_ENDPOINT),
deferredCredentialEndpoint = issuerPublicUrl.appendPath(WalletApi.DEFERRED_ENDPOINT),
authorizationServers = listOf(env.readRequiredUrl("issuer.authorizationServer")),
credentialResponseEncryption = env.credentialResponseEncryption(),
specificCredentialIssuers = buildList {
val enableMsoMdocPid = env.getProperty<Boolean>("issuer.pid.mso_mdoc.enabled") ?: true
Expand Down Expand Up @@ -341,10 +337,20 @@ private fun Environment.credentialResponseEncryption(): CredentialResponseEncryp
)
}

private fun Environment.readRequiredUrl(key: String): HttpsUrl =
getRequiredProperty(key).let { url ->
HttpsUrl.of(url) ?: HttpsUrl.unsafe(url)
}
private fun Environment.readRequiredUrl(key: String, removeTrailingSlash: Boolean = false): HttpsUrl =
getRequiredProperty(key)
.let { url ->
fun String.normalize() =
if (removeTrailingSlash) {
this.removeSuffix("/")
} else {
this
}

fun String.toHttpsUrl(): HttpsUrl = HttpsUrl.of(this) ?: HttpsUrl.unsafe(this)

url.normalize().toHttpsUrl()
}

private fun <T> Environment.readNonEmptySet(key: String, f: (String) -> T?): NonEmptySet<T> {
val nonEmptySet = getRequiredProperty<MutableSet<String>>(key)
Expand All @@ -353,6 +359,14 @@ private fun <T> Environment.readNonEmptySet(key: String, f: (String) -> T?): Non
return checkNotNull(nonEmptySet) { "Missing or incorrect values values for key `$key`" }
}

private fun HttpsUrl.appendPath(path: String): HttpsUrl =
HttpsUrl.unsafe(
UriComponentsBuilder.fromHttpUrl(externalForm)
.path(path)
.build()
.toUriString(),
)

fun BeanDefinitionDsl.initializer(): ApplicationContextInitializer<GenericApplicationContext> =
ApplicationContextInitializer<GenericApplicationContext> { initialize(it) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ val PidMsoMdocV1: MsoMdocMetaData = run {
JWSAlgorithm.ES256,
)
MsoMdocMetaData(
id = CredentialUniqueId(PidMsoMdocScope.value),
docType = pidDocType(1),
display = pidDisplay,
msoClaims = mapOf(pidAttributes),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ private object Attributes {
}

val PidSdJwtVcV1: SdJwtVcMetaData = SdJwtVcMetaData(
id = CredentialUniqueId(PidSdJwtVcScope.value),
type = SdJwtVcType(pidDocType(1)),
display = pidDisplay,
claims = Attributes.pidAttributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ fun <T> CredentialResponseEncryption.fold(

/**
* @param id The Credential Issuer's identifier
* @param authorizationServer Identifier of the OAuth 2.0 Authorization
* Server (as defined in [RFC8414]) the Credential Issuer relies on for authorization
* @param authorizationServers Identifiers of the OAuth 2.0 Authorization
* Servers (as defined in [RFC8414]) the Credential Issuer relies on for authorization
* @param credentialEndPoint URL of the Credential Issuer's Credential Endpoint.
* This URL MUST use the https scheme and MAY contain port, path,
* and query parameter components
Expand All @@ -79,7 +79,7 @@ fun <T> CredentialResponseEncryption.fold(
*/
data class CredentialIssuerMetaData(
val id: CredentialIssuerId,
val authorizationServer: HttpsUrl,
val authorizationServers: List<HttpsUrl>,
val credentialEndPoint: HttpsUrl,
val batchCredentialEndpoint: HttpsUrl? = null,
val deferredCredentialEndpoint: HttpsUrl? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,25 @@ package eu.europa.ec.eudi.pidissuer.domain
import arrow.core.Ior
import java.time.Duration

/**
* A
*/
sealed interface CredentialOffer {
@JvmInline
value class ByScope(val value: Scope) : CredentialOffer
data class ByMetaData(val value: CredentialMetaData) : CredentialOffer
}

data class AuthorizationCodeGrant(val issuerState: String? = null)
data class AuthorizationCodeGrant(
val issuerState: String? = null,
val authorizationServer: HttpsUrl? = null,
)

@JvmInline
value class PreAuthorizedCode(val value: String)

data class PreAuthorizedCodeGrant(
val preAuthorizedCode: PreAuthorizedCode,
val userPinRequired: Boolean = false,
val interval: Duration,
val authorizationServer: HttpsUrl? = null,
)

typealias Grants = Ior<AuthorizationCodeGrant, PreAuthorizedCodeGrant>

data class CredentialsOffer(
val credentialIssuer: CredentialIssuerId,
val grants: Grants,
val credentials: List<CredentialOffer>,
val grants: Grants? = null,
val credentials: List<CredentialUniqueId>,
)
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const val JWT_VS_JSON_FORMAT = "jwt_vc_json"
* W3C VC signed as a JWT, not using JSON-LD (jwt_vc_json)
*/
data class JwtVcJsonMetaData(
val id: String,
override val id: CredentialUniqueId,
override val scope: Scope? = null,
val cryptographicSuitesSupported: NonEmptySet<JWSAlgorithm>,
override val display: List<CredentialDisplay>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typealias MsoClaims = Map<MsoNameSpace, List<AttributeDetails>>
* @param docType string identifying the credential type as defined in ISO.18013-5.
*/
data class MsoMdocMetaData(
override val id: CredentialUniqueId,
val docType: MsoDocType,
override val cryptographicBindingMethodsSupported: List<CryptographicBindingMethod>,
override val scope: Scope? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ value class SdJwtVcType(val value: String)
* @param type As defined in https://datatracker.ietf.org/doc/html/draft-ietf-oauth-sd-jwt-vc-00#type-claim
*/
data class SdJwtVcMetaData(
override val id: CredentialUniqueId,
val type: SdJwtVcType,
override val scope: Scope? = null,
override val cryptographicBindingMethodsSupported: List<CryptographicBindingMethod> = emptyList(),
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/eu/europa/ec/eudi/pidissuer/domain/Types.kt
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,18 @@ enum class ProofType {
CWT,
}

/**
* The unique identifier of an offered Credential.
*/
@JvmInline
value class CredentialUniqueId(val value: String)

/**
* Representing metadata about a separate credential type
* that the Credential Issuer can issue
*/
sealed interface CredentialMetaData {
val id: CredentialUniqueId
val format: Format
val scope: Scope?
val display: List<CredentialDisplay>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class GetCredentialIssuerMetaData(private val credentialIssuerMetaData: Credenti
data class CredentialIssuerMetaDataTO(
@Required @SerialName("credential_issuer")
val credentialIssuer: String,
@SerialName("authorization_server")
val authorizationServer: String? = null,
@SerialName("authorization_servers")
val authorizationServers: List<String>? = null,
@Required @SerialName("credential_endpoint")
val credentialEndpoint: String,
@SerialName("batch_credential_endpoint")
Expand All @@ -44,12 +44,12 @@ data class CredentialIssuerMetaDataTO(
val encryptionMethods: List<String> = emptyList(),
@SerialName("require_credential_response_encryption")
val encryptionRequired: Boolean = false,
@Required @SerialName("credentials_supported") val credentialsSupported: List<JsonObject>,
@Required @SerialName("credentials_supported") val credentialsSupported: JsonObject,
)

private fun CredentialIssuerMetaData.toTransferObject(): CredentialIssuerMetaDataTO = CredentialIssuerMetaDataTO(
credentialIssuer = id.externalForm,
authorizationServer = authorizationServer.externalForm,
authorizationServers = authorizationServers.map { it.externalForm },
credentialEndpoint = credentialEndPoint.externalForm,
batchCredentialEndpoint = batchCredentialEndpoint?.externalForm,
deferredCredentialEndpoint = deferredCredentialEndpoint?.externalForm,
Expand All @@ -60,7 +60,7 @@ private fun CredentialIssuerMetaData.toTransferObject(): CredentialIssuerMetaDat
required.encryptionMethods.map { it.name }
},
encryptionRequired = credentialResponseEncryption.fold(false) { _ -> true },
credentialsSupported = credentialsSupported.map { credentialMetaDataJson(it) },
credentialsSupported = JsonObject(credentialsSupported.associate { it.id.value to credentialMetaDataJson(it) }),
)

@OptIn(ExperimentalSerializationApi::class)
Expand Down
Loading

0 comments on commit 7424b1c

Please sign in to comment.