Skip to content

Commit

Permalink
Add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
n0900 committed Oct 9, 2024
1 parent 29cf053 commit adce946
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ data class AuthenticationRequestParameters(
* Optional when JAR (RFC9101) is used.
*/
@SerialName("response_type")
val responseType: String? = null,
override val responseType: String? = null,

/**
* OIDC: REQUIRED. OAuth 2.0 Client Identifier valid at the Authorization Server.
*/
@SerialName("client_id")
val clientId: String? = null,
override val clientId: String,

/**
* OIDC: REQUIRED. Redirection URI to which the response will be sent. This URI MUST exactly match one of the
Expand All @@ -64,15 +64,15 @@ data class AuthenticationRequestParameters(
* parameter with a browser cookie.
*/
@SerialName("state")
val state: String? = null,
override val state: String? = null,

/**
* OIDC: OPTIONAL. String value used to associate a Client session with an ID Token, and to mitigate replay attacks.
* The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient entropy MUST
* be present in the nonce values used to prevent attackers from guessing values.
*/
@SerialName("nonce")
val nonce: String? = null,
override val nonce: String? = null,

/**
* OIDC: OPTIONAL. This parameter is used to request that specific Claims be returned. The value is a JSON object
Expand Down Expand Up @@ -173,7 +173,7 @@ data class AuthenticationRequestParameters(
* scheme.
*/
@SerialName("client_id_scheme")
val clientIdScheme: OpenIdConstants.ClientIdScheme? = null,
override val clientIdScheme: OpenIdConstants.ClientIdScheme? = null,

/**
* OID4VP: OPTIONAL. String containing the Wallet's identifier. The Credential Issuer can use the discovery process
Expand Down Expand Up @@ -207,7 +207,7 @@ data class AuthenticationRequestParameters(
* authentication process to a certain endpoint using the HTTP POST method.
*/
@SerialName("response_mode")
val responseMode: OpenIdConstants.ResponseMode? = null,
override val responseMode: OpenIdConstants.ResponseMode? = null,

/**
* OID4VP: OPTIONAL. The Response URI to which the Wallet MUST send the Authorization Response using an HTTPS POST
Expand All @@ -218,7 +218,7 @@ data class AuthenticationRequestParameters(
* `invalid_request` Authorization Response error.
*/
@SerialName("response_uri")
val responseUrl: String? = null,
override val responseUri: String? = null,

/**
* OAuth 2.0 JAR: If signed, the Authorization Request Object SHOULD contain the Claims `iss` (issuer) and `aud`
Expand Down Expand Up @@ -368,7 +368,7 @@ data class AuthenticationRequestParameters(
if (userHint != other.userHint) return false
if (issuerState != other.issuerState) return false
if (responseMode != other.responseMode) return false
if (responseUrl != other.responseUrl) return false
if (responseUri != other.responseUri) return false
if (audience != other.audience) return false
if (issuer != other.issuer) return false
if (issuedAt != other.issuedAt) return false
Expand Down Expand Up @@ -413,7 +413,7 @@ data class AuthenticationRequestParameters(
result = 31 * result + (userHint?.hashCode() ?: 0)
result = 31 * result + (issuerState?.hashCode() ?: 0)
result = 31 * result + (responseMode?.hashCode() ?: 0)
result = 31 * result + (responseUrl?.hashCode() ?: 0)
result = 31 * result + (responseUri?.hashCode() ?: 0)
result = 31 * result + (audience?.hashCode() ?: 0)
result = 31 * result + (issuer?.hashCode() ?: 0)
result = 31 * result + (issuedAt?.hashCode() ?: 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject

@Serializable
sealed interface RequestParameters
sealed interface RequestParameters {
val responseType: String?
val clientId: String
val clientIdScheme: OpenIdConstants.ClientIdScheme?
val responseMode: OpenIdConstants.ResponseMode?
val responseUri: String?
val nonce: String?
val state: String?
}

object RequestParametersSerializer : JsonContentPolymorphicSerializer<RequestParameters>(RequestParameters::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<RequestParameters> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import at.asitplus.signum.indispensable.asn1.Asn1Element
import at.asitplus.signum.indispensable.asn1.ObjectIdentifier
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.json.JsonObject

/**
Expand All @@ -22,52 +23,126 @@ import kotlinx.serialization.json.JsonObject
* In the Wallet centric model this is the request
* coming from the Driving application to the wallet which starts
* the process
*
* This should not be confused with the CSC-related extensions to [AuthenticationRequestParameters] which are used
* by the wallet to communicate with the QTSP using OAuth2
*/
@Serializable
data class SignatureRequestParameters(

/**
* OIDC: REQUIRED. OAuth 2.0 Response Type value that determines the authorization processing flow to be used,
* including what parameters are returned from the endpoints used. When using the Authorization Code Flow, this
* value is `code`.
*
* For OIDC SIOPv2, this is typically `id_token`. For OID4VP, this is typically `vp_token`.
*
* Optional when JAR (RFC9101) is used.
*/
@SerialName("response_type")
val responseType: String,
override val responseType: String,

/**
* OIDC: REQUIRED. OAuth 2.0 Client Identifier valid at the Authorization Server.
*/
@SerialName("client_id")
val clientId: String,
override val clientId: String,

/**
* OID4VP: OPTIONAL. A string identifying the scheme of the value in the `client_id` Authorization Request parameter
* (Client Identifier scheme). The [clientIdScheme] parameter namespaces the respective Client Identifier. If an
* Authorization Request uses the [clientIdScheme] parameter, the Wallet MUST interpret the Client Identifier of
* the Verifier in the context of the Client Identifier scheme. If the parameter is not present, the Wallet MUST
* behave as specified in RFC6749. If the same Client Identifier is used with different Client Identifier schemes,
* those occurrences MUST be treated as different Verifiers. Note that the Verifier needs to determine which Client
* Identifier schemes the Wallet supports prior to sending the Authorization Request in order to choose a supported
* scheme.
*/
@SerialName("client_id_scheme")
val clientIdScheme: String? = null,
override val clientIdScheme: OpenIdConstants.ClientIdScheme? = null,

/**
* OAuth 2.0 Responses: OPTIONAL. Informs the Authorization Server of the mechanism to be used for returning
* Authorization Response parameters from the Authorization Endpoint. This use of this parameter is NOT RECOMMENDED
* with a value that specifies the same Response Mode as the default Response Mode for the Response Type used.
* SHOULD be direct post
*/
@SerialName("response_mode")
val responseMode: OpenIdConstants.ResponseMode? = null,
override val responseMode: OpenIdConstants.ResponseMode? = null,

/**
* MUST be present if direct post
* OID4VP: OPTIONAL. The Response URI to which the Wallet MUST send the Authorization Response using an HTTPS POST
* request as defined by the Response Mode `direct_post`. The Response URI receives all Authorization Response
* parameters as defined by the respective Response Type. When the `response_uri` parameter is present,
* the `redirect_uri` Authorization Request parameter MUST NOT be present. If the `redirect_uri` Authorization
* Request parameter is present when the Response Mode is `direct_post`, the Wallet MUST return an
* `invalid_request` Authorization Response error.
*/
@SerialName("response_uri")
val responseUri: String? = null,
override val responseUri: String? = null,

/**
* OIDC: OPTIONAL. String value used to associate a Client session with an ID Token, and to mitigate replay attacks.
* The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient entropy MUST
* be present in the nonce values used to prevent attackers from guessing values.
*/
@SerialName("nonce")
val nonce: String,
override val nonce: String,

/**
* OIDC: RECOMMENDED. Opaque value used to maintain state between the request and the callback. Typically,
* Cross-Site Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this
* parameter with a browser cookie.
*/
@SerialName("state")
val state: String? = null,
override val state: String? = null,

/**
* UC5 Draft REQUIRED.
* This parameter contains the symbolic identifier determining the kind of
* signature to be created
*/
@SerialName("signatureQualifier")
val signatureQualifier: SignatureQualifierEnum = SignatureQualifierEnum.EU_EIDAS_QES,

/**
* UC5 Draft REQUIRED.
* An array composed of entries for every
* document to be signed
*/
@SerialName("documentDigests")
val documentDigests: List<OAuthDocumentDigest>,

/**
* UC5 Draft REQUIRED.
* An array composed of entries for every
* document to be signed
*/
@SerialName("documentLocations")
val documentLocations: List<DocumentLocation>,

/**
* UC5 Draft REQUIRED.
* String containing the OID of the hash
* algorithm used to generate the hashes listed
* in [documentDigests]
*/
@SerialName("hashAlgorithmOID")
val hashAlgorithmOid: ObjectIdentifier = Digest.SHA256.oid,

/**
* CSC: OPTIONAL
* Arbitrary data from the signature application. It can be used to handle a
* transaction identifier or other application-spe cific data that may be useful for
* debugging purposes
*/
@SerialName("clientData")
val clientData: String?,
) : RequestParameters {

@Transient
val hashAlgorithm: Digest? = Digest.entries.find { digest -> digest.oid == hashAlgorithmOid }

fun toAuthorizationDetails(): AuthorizationDetails =
AuthorizationDetails.CSCCredential(
credentialID = this.clientId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class OidcSiopVerifier private constructor(
*/
val responseMode: OpenIdConstants.ResponseMode = OpenIdConstants.ResponseMode.FRAGMENT,
/**
* Response URL to set in the [AuthenticationRequestParameters.responseUrl],
* Response URL to set in the [AuthenticationRequestParameters.responseUri],
* required if [responseMode] is set to [OpenIdConstants.ResponseMode.DIRECT_POST] or
* [OpenIdConstants.ResponseMode.DIRECT_POST_JWT].
*/
Expand Down Expand Up @@ -324,7 +324,7 @@ class OidcSiopVerifier private constructor(
.also { stateToResponseTypeStore.put(requestOptions.state, it) },
clientId = clientId,
redirectUrl = requestOptions.buildRedirectUrl(),
responseUrl = requestOptions.responseUrl,
responseUri = requestOptions.responseUrl,
clientIdScheme = clientIdScheme.clientIdScheme,
scope = requestOptions.buildScope(),
nonce = nonceService.provideNonce()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal class AuthenticationResponseFactory(
request: AuthenticationRequestParametersFrom,
response: AuthenticationResponse,
): AuthenticationResponseResult.Post {
val url = request.parameters.responseUrl
val url = request.parameters.responseUri
?: request.parameters.redirectUrl
?: throw OAuth2Exception(Errors.INVALID_REQUEST)
val responseSerialized = buildJarm(request, response)
Expand All @@ -55,7 +55,7 @@ internal class AuthenticationResponseFactory(
request: AuthenticationRequestParametersFrom,
response: AuthenticationResponse,
): AuthenticationResponseResult.Post {
val url = request.parameters.responseUrl
val url = request.parameters.responseUri
?: request.parameters.redirectUrl
?: throw OAuth2Exception(Errors.INVALID_REQUEST)
return AuthenticationResponseResult.Post(url, response.params.encodeToParameters())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ internal class AuthorizationRequestValidator {
Napier.w("response_mode is $responseMode, but redirect_url is set")
throw OAuth2Exception(Errors.INVALID_REQUEST)
}
if (responseUrl == null) {
if (responseUri == null) {
Napier.w("response_mode is $responseMode, but response_url is not set")
throw OAuth2Exception(Errors.INVALID_REQUEST)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ class OidcSiopInteropTest : FreeSpec({
val parsed = jsonSerializer.decodeFromString<AuthenticationRequestParameters>(input)
parsed.shouldNotBeNull()

parsed.responseUrl shouldBe "https://verifier-backend.eudiw.dev/wallet/direct_post"
parsed.responseUri shouldBe "https://verifier-backend.eudiw.dev/wallet/direct_post"
parsed.clientIdScheme shouldBe OpenIdConstants.ClientIdScheme.X509_SAN_DNS
parsed.responseType shouldBe "vp_token"
parsed.nonce shouldBe "nonce"
Expand Down Expand Up @@ -287,7 +287,7 @@ class OidcSiopInteropTest : FreeSpec({
payload = AuthenticationRequestParameters(
nonce = "RjEQKQeG8OUaKT4ij84E8mCvry6pVSgDyqRBMW5eBTPItP4DIfbKaT6M6v6q2Dvv8fN7Im7Ifa6GI2j6dHsJaQ==",
state = "ef391e30-bacc-4441-af5d-7f42fb682e02",
responseUrl = "https://example.com/ef391e30-bacc-4441-af5d-7f42fb682e02",
responseUri = "https://example.com/ef391e30-bacc-4441-af5d-7f42fb682e02",
clientId = "https://example.com/ef391e30-bacc-4441-af5d-7f42fb682e02",
).serialize().encodeToByteArray(),
addX5c = false
Expand All @@ -302,8 +302,8 @@ class OidcSiopInteropTest : FreeSpec({
val parsed = wallet.parseAuthenticationRequestParameters(input).getOrThrow()

parsed.parameters.state shouldBe "ef391e30-bacc-4441-af5d-7f42fb682e02"
parsed.parameters.responseUrl shouldBe "https://example.com/ef391e30-bacc-4441-af5d-7f42fb682e02"
parsed.parameters.clientId shouldBe parsed.parameters.responseUrl
parsed.parameters.responseUri shouldBe "https://example.com/ef391e30-bacc-4441-af5d-7f42fb682e02"
parsed.parameters.clientId shouldBe parsed.parameters.responseUri
}

"empty client_id" {
Expand Down

0 comments on commit adce946

Please sign in to comment.