@@ -3,15 +3,11 @@ package no.nav.security.mock.oauth2.token
3
3
import com.nimbusds.jose.JOSEObjectType
4
4
import com.nimbusds.oauth2.sdk.GrantType
5
5
import com.nimbusds.oauth2.sdk.TokenRequest
6
- import kotlinx.serialization.json.Json
7
- import kotlinx.serialization.json.JsonArray
8
- import kotlinx.serialization.json.JsonObject
9
- import kotlinx.serialization.json.JsonPrimitive
10
6
import no.nav.security.mock.oauth2.extensions.clientIdAsString
11
7
import no.nav.security.mock.oauth2.extensions.grantType
8
+ import no.nav.security.mock.oauth2.extensions.replaceValues
12
9
import no.nav.security.mock.oauth2.extensions.scopesWithoutOidcScopes
13
10
import no.nav.security.mock.oauth2.extensions.tokenExchangeGrantOrNull
14
- import no.nav.security.mock.oauth2.http.objectMapper
15
11
import java.time.Duration
16
12
import java.util.*
17
13
@@ -31,49 +27,49 @@ interface OAuth2TokenCallback {
31
27
32
28
// TODO: for JwtBearerGrant and TokenExchange should be able to ovverride sub, make sub nullable and return some default
33
29
open class DefaultOAuth2TokenCallback
34
- @JvmOverloads
35
- constructor (
36
- private val issuerId: String = " default" ,
37
- private val subject: String = UUID .randomUUID().toString(),
38
- private val typeHeader: String = JOSEObjectType .JWT .type,
39
- // needs to be nullable in order to know if a list has explicitly been set, empty list should be a allowable value
40
- private val audience: List <String >? = null ,
41
- private val claims: Map <String , Any > = emptyMap(),
42
- private val expiry: Long = 3600 ,
43
- ) : OAuth2TokenCallback {
44
- override fun issuerId (): String = issuerId
45
-
46
- override fun subject (tokenRequest : TokenRequest ): String {
47
- return when (GrantType .CLIENT_CREDENTIALS ) {
48
- tokenRequest.grantType() -> tokenRequest.clientIdAsString()
49
- else -> subject
30
+ @JvmOverloads
31
+ constructor (
32
+ private val issuerId: String = " default" ,
33
+ private val subject: String = UUID .randomUUID().toString(),
34
+ private val typeHeader: String = JOSEObjectType .JWT .type,
35
+ // needs to be nullable in order to know if a list has explicitly been set, empty list should be a allowable value
36
+ private val audience: List <String >? = null ,
37
+ private val claims: Map <String , Any > = emptyMap(),
38
+ private val expiry: Long = 3600 ,
39
+ ) : OAuth2TokenCallback {
40
+ override fun issuerId (): String = issuerId
41
+
42
+ override fun subject (tokenRequest : TokenRequest ): String {
43
+ return when (GrantType .CLIENT_CREDENTIALS ) {
44
+ tokenRequest.grantType() -> tokenRequest.clientIdAsString()
45
+ else -> subject
46
+ }
50
47
}
51
- }
52
48
53
- override fun typeHeader (tokenRequest : TokenRequest ): String {
54
- return typeHeader
55
- }
56
-
57
- override fun audience (tokenRequest : TokenRequest ): List <String > {
58
- val audienceParam = tokenRequest.tokenExchangeGrantOrNull()?.audience
59
- return when {
60
- audience != null -> audience
61
- audienceParam != null -> audienceParam
62
- tokenRequest.scope != null -> tokenRequest.scopesWithoutOidcScopes()
63
- else -> listOf (" default" )
49
+ override fun typeHeader (tokenRequest : TokenRequest ): String {
50
+ return typeHeader
64
51
}
65
- }
66
52
67
- override fun addClaims (tokenRequest : TokenRequest ): Map <String , Any > =
68
- mutableMapOf<String , Any >(
69
- " tid" to issuerId,
70
- ).apply {
71
- putAll(claims)
72
- put(" azp" , tokenRequest.clientIdAsString())
53
+ override fun audience (tokenRequest : TokenRequest ): List <String > {
54
+ val audienceParam = tokenRequest.tokenExchangeGrantOrNull()?.audience
55
+ return when {
56
+ audience != null -> audience
57
+ audienceParam != null -> audienceParam
58
+ tokenRequest.scope != null -> tokenRequest.scopesWithoutOidcScopes()
59
+ else -> listOf (" default" )
60
+ }
73
61
}
74
62
75
- override fun tokenExpiry (): Long = expiry
76
- }
63
+ override fun addClaims (tokenRequest : TokenRequest ): Map <String , Any > =
64
+ mutableMapOf<String , Any >(
65
+ " tid" to issuerId,
66
+ ).apply {
67
+ putAll(claims)
68
+ put(" azp" , tokenRequest.clientIdAsString())
69
+ }
70
+
71
+ override fun tokenExpiry (): Long = expiry
72
+ }
77
73
78
74
data class RequestMappingTokenCallback (
79
75
val issuerId : String ,
@@ -94,54 +90,14 @@ data class RequestMappingTokenCallback(
94
90
95
91
private fun List<RequestMapping>.getClaims (tokenRequest : TokenRequest ): Map <String , Any > {
96
92
val claims = firstOrNull { it.isMatch(tokenRequest) }?.claims ? : emptyMap()
97
-
98
- // TODO: hack choose first element. Rewrite to support multiple elements and custom objects
99
- val params = (tokenRequest.toHTTPRequest().bodyAsFormParameters.map {
100
- it.key to it.value.first()
101
- }).toMap() + mapOf (" clientId" to tokenRequest.clientIdAsString())
102
-
103
- return claims.mapValues { (_, value) ->
104
- val v = objectMapper.writeValueAsString(value)
105
- val jsonElement = Json .parseToJsonElement(v)
106
- when (jsonElement) {
107
- is JsonPrimitive ->
108
- if (jsonElement.isString) {
109
- replaceVariables(jsonElement.content, params)
110
- } else {
111
- jsonElement.content
112
- }
113
-
114
- is JsonObject -> {
115
- jsonElement.mapValues { (_, value) ->
116
- if (value is JsonPrimitive ) {
117
- replaceVariables(value.content, params)
118
- } else if (value is JsonArray )
119
- value.map { element ->
120
- if (element is JsonPrimitive ) {
121
- replaceVariables(element.content, params)
122
- } else {
123
- element
124
- }
125
- }
126
- else {
127
- value
128
- }
129
- }
130
- }
131
-
132
- is JsonArray -> {
133
- jsonElement.map { element ->
134
- if (element is JsonPrimitive ) {
135
- replaceVariables(element.content, params)
136
- } else {
137
- element
138
- }
139
- }
140
- }
141
-
142
- else -> value
143
- }
144
- }
93
+ val templateParams = tokenRequest.toHTTPRequest().bodyAsFormParameters.mapValues { it.value.joinToString(separator = " " ) }
94
+
95
+ // in case client_id is not set as form param but as basic auth, we add it to the template params in two different formats for backwards compatibility
96
+ return claims.replaceValues(
97
+ templateParams +
98
+ mapOf (" clientId" to tokenRequest.clientIdAsString()) +
99
+ mapOf (" client_id" to tokenRequest.clientIdAsString()),
100
+ )
145
101
}
146
102
147
103
private inline fun <reified T > List<RequestMapping>.getClaimOrNull (
@@ -150,15 +106,6 @@ data class RequestMappingTokenCallback(
150
106
): T ? = getClaims(tokenRequest)[key] as ? T
151
107
152
108
private fun List<RequestMapping>.getTypeHeader (tokenRequest : TokenRequest ) = firstOrNull { it.isMatch(tokenRequest) }?.typeHeader ? : JOSEObjectType .JWT .type
153
-
154
- private fun replaceVariables (
155
- input : String ,
156
- replacements : Map <String , String >,
157
- ): String {
158
- return replacements.entries.fold(input) { acc, (key, value) ->
159
- acc.replace(" \$ {$key }" , value)
160
- }
161
- }
162
109
}
163
110
164
111
data class RequestMapping (
0 commit comments