Skip to content

Commit ea12ec5

Browse files
authored
Merge pull request #1721 from walt-id/feat/authorization-details-wal-992
Feat/authorization details wal 992
2 parents a9ee76b + 8202f45 commit ea12ec5

20 files changed

Lines changed: 403 additions & 64 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
version:
1212
uses: walt-id/waltid-identity/.github/workflows/version.yml@6f15f719a6820cf631bce0399a89b3f060302d3d
1313
gradle-build:
14-
uses: walt-id/waltid-identity/.github/workflows/gradle.yml@354260aebde1a117cb35e4dffaecaa67a0182eae
14+
uses: walt-id/waltid-identity/.github/workflows/gradle.yml@42302437bcdd1ba26da603f998887049aea0a009
1515
secrets: inherit
1616
needs: version
1717
with:

.github/workflows/gradle.yml

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
matrix:
2121
# macos-latest disabled until all multiplatform modules set up properly
2222
# currently configured on workflow dispatch as a separate workflow (gradle-macos)
23-
os: [ ubuntu-latest ]
23+
os: [ ubuntu-22.04 ]
2424
steps:
2525
- name: Checkout all repos with ref fallback
2626
uses: walt-id/waltid-identity/.github/actions/checkout-repos@864f84343fdeea17f6f63434bc8036c39f178dc7
@@ -63,49 +63,12 @@ jobs:
6363
# -e "s#8443#443#g" \
6464
# "$FILE"
6565

66-
- name: Check if Google Chrome is installed
67-
id: check-browser
68-
run: |
69-
if [ -x /usr/bin/google-chrome ] || [ -x /usr/bin/google-chrome-stable ]; then
70-
echo "installed=true" >> $GITHUB_OUTPUT
71-
echo "Google Chrome is already installed"
72-
else
73-
echo "installed=false" >> $GITHUB_OUTPUT
74-
echo "Google Chrome is not installed"
75-
fi
76-
77-
- name: Install Google Chrome stable
78-
if: steps.check-browser.outputs.installed == 'false'
79-
run: |
80-
sudo apt-get update
81-
sudo apt-get install -y wget gnupg
82-
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg
83-
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | \
84-
sudo tee /etc/apt/sources.list.d/google-chrome.list
85-
sudo apt-get update
86-
sudo apt-get install -y google-chrome-stable
87-
88-
- name: Export CHROME_BIN
89-
run: |
90-
if [ -x /usr/bin/google-chrome ]; then
91-
CHROME_BIN=/usr/bin/google-chrome
92-
elif [ -x /usr/bin/google-chrome-stable ]; then
93-
CHROME_BIN=/usr/bin/google-chrome-stable
94-
else
95-
CHROME_BIN=""
96-
fi
97-
if [ -z "$CHROME_BIN" ]; then
98-
echo "Could not determine Google Chrome binary path"
99-
exit 1
100-
fi
101-
echo "CHROME_BIN=$CHROME_BIN" >> $GITHUB_ENV
102-
echo "SELENIUM_BROWSER=chrome" >> $GITHUB_ENV
103-
echo "Using CHROME_BIN=$CHROME_BIN"
104-
10566
- name: Clean and build with all tests
10667
if: ${{ runner.os != 'macos' }}
10768
run: ./gradlew clean build cleanAllTests allTests --rerun-tasks --no-daemon --max-workers=3
10869
env:
70+
CI: "true"
71+
PLAYWRIGHT_INSTALL_WITH_DEPS: "true"
10972
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
11073
MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
11174

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
with:
1414
preferred: ${{ inputs.release_version }}
1515
gradle:
16-
uses: walt-id/waltid-identity/.github/workflows/gradle.yml@354260aebde1a117cb35e4dffaecaa67a0182eae
16+
uses: walt-id/waltid-identity/.github/workflows/gradle.yml@42302437bcdd1ba26da603f998887049aea0a009
1717
secrets: inherit
1818
needs: version
1919
with:
@@ -141,4 +141,4 @@ jobs:
141141
token: ${{ secrets.GH_ACCESS_TOKEN }}
142142
repository: walt-id/waltid-unified-build
143143
tag_name: ${{ needs.unified-build-tag.outputs.tag }}
144-
prerelease: ${{ contains(needs.unified-build-tag.outputs.tag, 'PRE-RELEASE') }}
144+
prerelease: ${{ contains(needs.unified-build-tag.outputs.tag, 'PRE-RELEASE') }}

.github/workflows/test-deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
with:
1313
preferred: ${{ inputs.release_version }}
1414
gradle:
15-
uses: walt-id/waltid-identity/.github/workflows/gradle.yml@354260aebde1a117cb35e4dffaecaa67a0182eae
15+
uses: walt-id/waltid-identity/.github/workflows/gradle.yml@42302437bcdd1ba26da603f998887049aea0a009
1616
secrets: inherit
1717
needs: version
1818
with:
@@ -51,4 +51,4 @@ jobs:
5151
uses: walt-id/waltid-identity/.github/workflows/tag.yml@40178bca5ee6dbc8fb3ec818d308e8b72b0c6dd3
5252
needs: [ version, deploy ]
5353
with:
54-
version: ${{ needs.version.outputs.release_version }}
54+
version: ${{ needs.version.outputs.release_version }}

waltid-libraries/protocols/waltid-openid4vci-wallet/src/commonMain/kotlin/id/waltid/openid4vci/wallet/authorization/AuthorizationRequestBuilder.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package id.waltid.openid4vci.wallet.authorization
22

33
import id.walt.openid4vci.ResponseType
44
import id.walt.openid4vci.metadata.oauth.AuthorizationServerMetadata
5+
import id.walt.openid4vci.requests.authorization.OPENID_CREDENTIAL_AUTHORIZATION_DETAIL_TYPE
56
import id.waltid.openid4vci.wallet.oauth.ClientConfiguration
67
import id.waltid.openid4vci.wallet.oauth.PKCEManager
78
import id.waltid.openid4vci.wallet.oauth.StateManager
89
import io.github.oshai.kotlinlogging.KotlinLogging
910
import io.ktor.http.*
11+
import kotlinx.serialization.EncodeDefault
12+
import kotlinx.serialization.ExperimentalSerializationApi
1013
import kotlinx.serialization.Serializable
1114
import kotlinx.serialization.json.Json
1215

@@ -31,9 +34,11 @@ class AuthorizationRequestBuilder(
3134
/**
3235
* Authorization details for OpenID4VCI (RFC 9396)
3336
*/
37+
@OptIn(ExperimentalSerializationApi::class)
3438
@Serializable
3539
data class AuthorizationDetails(
36-
val type: String = "openid_credential",
40+
@EncodeDefault(EncodeDefault.Mode.ALWAYS)
41+
val type: String = OPENID_CREDENTIAL_AUTHORIZATION_DETAIL_TYPE,
3742
val credential_configuration_id: String,
3843
val locations: List<String>? = null,
3944
)

waltid-libraries/protocols/waltid-openid4vci-wallet/src/commonTest/kotlin/id/waltid/openid4vci/wallet/authorization/AuthorizationRequestBuilderTest.kt

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package id.waltid.openid4vci.wallet.authorization
22

33
import id.waltid.openid4vci.wallet.oauth.ClientConfiguration
44
import io.ktor.http.*
5+
import kotlinx.serialization.json.Json
6+
import kotlinx.serialization.json.jsonArray
7+
import kotlinx.serialization.json.jsonObject
8+
import kotlinx.serialization.json.jsonPrimitive
59
import kotlin.test.Test
610
import kotlin.test.assertEquals
711
import kotlin.test.assertNotNull
8-
import kotlin.test.assertTrue
912

1013
class AuthorizationRequestBuilderTest {
1114

@@ -32,8 +35,9 @@ class AuthorizationRequestBuilderTest {
3235
assertEquals("test-client", params["client_id"])
3336
assertEquals("https://wallet.example.com/callback", params["redirect_uri"])
3437
assertNotNull(params["state"])
35-
assertNotNull(params["authorization_details"])
36-
assertTrue(params["authorization_details"]!!.contains("test_config"))
38+
val authorizationDetail = authorizationDetail(params)
39+
assertEquals("openid_credential", authorizationDetail["type"]?.jsonPrimitive?.content)
40+
assertEquals("test_config", authorizationDetail["credential_configuration_id"]?.jsonPrimitive?.content)
3741
}
3842

3943
@Test
@@ -44,10 +48,10 @@ class AuthorizationRequestBuilderTest {
4448
usePKCE = true
4549
)
4650

47-
assertNotNull(request.pkceData)
51+
val pkceData = assertNotNull(request.pkceData)
4852
val url = Url(request.url)
4953
val params = url.parameters
50-
assertEquals(request.pkceData!!.codeChallenge, params["code_challenge"])
54+
assertEquals(pkceData.codeChallenge, params["code_challenge"])
5155
assertEquals("S256", params["code_challenge_method"])
5256
}
5357

@@ -77,7 +81,17 @@ class AuthorizationRequestBuilderTest {
7781
assertEquals("code", params["response_type"])
7882
assertEquals("test-client", params["client_id"])
7983
assertNotNull(params["authorization_details"])
80-
assertNotNull(pkce)
81-
assertEquals(pkce!!.codeChallenge, params["code_challenge"])
84+
assertEquals(
85+
"openid_credential",
86+
authorizationDetail(params["authorization_details"]!!)["type"]?.jsonPrimitive?.content,
87+
)
88+
val pkceData = assertNotNull(pkce)
89+
assertEquals(pkceData.codeChallenge, params["code_challenge"])
8290
}
91+
92+
private fun authorizationDetail(params: Parameters) =
93+
authorizationDetail(params["authorization_details"]!!)
94+
95+
private fun authorizationDetail(authorizationDetails: String) =
96+
Json.parseToJsonElement(authorizationDetails).jsonArray.single().jsonObject
8397
}

waltid-libraries/protocols/waltid-openid4vci/src/commonMain/kotlin/id/walt/openid4vci/Session.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ sealed interface Session {
1616
val subject: String?
1717
@SerialName("expires_at")
1818
val expiresAt: Map<TokenType, Instant>
19+
@SerialName("custom_attributes")
20+
val customAttributes: Map<String, String>
1921

2022
fun withExpiresAt(tokenType: TokenType, instant: Instant): Session
2123
fun withSubject(subject: String?): Session
24+
fun withCustomAttribute(name: String, value: String): Session
2225
fun copy(): Session
2326
}
2427

@@ -34,13 +37,19 @@ data class DefaultSession(
3437
override val expiresAt: Map<TokenType, Instant> = emptyMap(),
3538
@SerialName("subject")
3639
override val subject: String? = null,
40+
@SerialName("custom_attributes")
41+
override val customAttributes: Map<String, String> = emptyMap(),
3742
) : Session {
3843
override fun withExpiresAt(tokenType: TokenType, instant: Instant): Session =
3944
copy(expiresAt = expiresAt.toMutableMap().apply { this[tokenType] = instant })
4045

4146
override fun withSubject(subject: String?): Session = copy(subject = subject)
4247

48+
override fun withCustomAttribute(name: String, value: String): Session =
49+
copy(customAttributes = customAttributes.toMutableMap().apply { this[name] = value })
50+
4351
override fun copy(): Session = this.copy(
4452
expiresAt = expiresAt.toMutableMap(),
53+
customAttributes = customAttributes.toMutableMap(),
4554
)
4655
}

waltid-libraries/protocols/waltid-openid4vci/src/commonMain/kotlin/id/walt/openid4vci/core/DefaultOAuth2Provider.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import id.walt.openid4vci.responses.authorization.AuthorizationResponseHttp
1616
import id.walt.openid4vci.responses.token.AccessTokenResponse
1717
import id.walt.openid4vci.responses.token.AccessTokenResponseHttp
1818
import id.walt.openid4vci.responses.token.AccessTokenResponseResult
19+
import id.walt.openid4vci.responses.token.TokenResponseOptions
20+
import id.walt.openid4vci.responses.token.withOptions
1921
import id.walt.openid4vci.responses.credential.CredentialResponse
2022
import id.walt.openid4vci.responses.credential.CredentialResponseHttp
2123
import id.walt.openid4vci.responses.credential.CredentialResponseResult
@@ -146,13 +148,21 @@ class DefaultOAuth2Provider(
146148
)
147149
}
148150

149-
override suspend fun createAccessTokenResponse(request: AccessTokenRequest): AccessTokenResponseResult {
151+
override suspend fun createAccessTokenResponse(
152+
request: AccessTokenRequest,
153+
options: TokenResponseOptions,
154+
): AccessTokenResponseResult {
150155
for (handler in config.tokenEndpointHandlers.toList()) {
151156
if (!handler.canHandleTokenEndpointRequest(request)) {
152157
continue
153158
}
154159

155-
return handler.handleTokenEndpointRequest(request)
160+
return when (val result = handler.handleTokenEndpointRequest(request)) {
161+
is AccessTokenResponseResult.Success -> result.copy(
162+
response = result.response.withOptions(options, result.request),
163+
)
164+
is AccessTokenResponseResult.Failure -> result
165+
}
156166
}
157167

158168
val description = request.grantTypes.joinToString(" ").takeIf { it.isNotBlank() }

waltid-libraries/protocols/waltid-openid4vci/src/commonMain/kotlin/id/walt/openid4vci/core/OAuth2Provider.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import id.walt.openid4vci.responses.authorization.AuthorizationResponseHttp
1616
import id.walt.openid4vci.responses.token.AccessTokenResponse
1717
import id.walt.openid4vci.responses.token.AccessTokenResponseHttp
1818
import id.walt.openid4vci.responses.token.AccessTokenResponseResult
19+
import id.walt.openid4vci.responses.token.TokenResponseOptions
1920
import id.walt.openid4vci.responses.credential.CredentialResponseResult
2021
import id.walt.openid4vci.responses.credential.CredentialResponse
2122
import id.walt.openid4vci.responses.credential.CredentialResponseHttp
@@ -66,7 +67,10 @@ interface OAuth2Provider {
6667
session: Session? = null
6768
): AccessTokenRequestResult
6869

69-
suspend fun createAccessTokenResponse(request: AccessTokenRequest): AccessTokenResponseResult
70+
suspend fun createAccessTokenResponse(
71+
request: AccessTokenRequest,
72+
options: TokenResponseOptions = TokenResponseOptions(),
73+
): AccessTokenResponseResult
7074

7175
fun writeAccessTokenError(request: AccessTokenRequest, error: OAuthError): AccessTokenResponseHttp
7276

waltid-libraries/protocols/waltid-openid4vci/src/commonMain/kotlin/id/walt/openid4vci/handlers/granttypes/authorizationcode/AuthorizationCodeTokenEndpoint.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,12 @@ class AuthorizationCodeTokenEndpoint(
7676
audience = record.grantedAudience.firstOrNull(),
7777
scopes = scopedRequest.grantedScopes,
7878
expiresAt = expiresAt,
79-
additional = mapOf(
80-
"client_id" to client.id,
81-
),
79+
additional = buildMap {
80+
put("client_id", client.id)
81+
session.customAttributes["issuance_session_id"]?.let {
82+
put("issuance_session_id", it)
83+
}
84+
},
8285
)
8386

8487
val accessToken = tokenService.createAccessToken(claims)

0 commit comments

Comments
 (0)