Skip to content

Commit a53f0ce

Browse files
authored
fjernet støtte for tokens in cookies (#835)
1 parent 3ebfbeb commit a53f0ce

File tree

35 files changed

+67
-378
lines changed

35 files changed

+67
-378
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,6 @@ Add the modules that you need as Maven dependencies.
255255
- **`no.nav.security.jwt.issuer.[issuer shortname]`** - all properties relevant for a particular issuer must be listed under a short name for that issuer (not the actual issuer value from the token, but a chosen name to represent config for the actual issuer) you trust, e.g. **`citizen`** or **`employee`**
256256
- **`no.nav.security.jwt.issuer.[issuer shortname].discoveryurl`** - The identity provider configuration/discovery endpoint (metadata)
257257
- **`no.nav.security.jwt.issuer.[issuer shortname].accepted_audience`** - The value of the audience (aud) claim in the JWT token. For OIDC it is the client ID of the client responsible for acquiring the token, in OAuth 2.0 it should be the identifier for you api.
258-
- **`no.nav.security.jwt.issuer.[issuer shortname].cookiename`** - The value of the cookie containing a ID token (not required, only necessary if your api receives calls directly from a browser)
259258
- **`no.nav.security.jwt.issuer.[issuer shortname].validation.optional-claims`** - A comma separated list of optional claims that will be excluded from default claims.
260259
- **`no.nav.security.jwt.issuer.[issuer shortname].jwks-cache.lifespan`** - Cache the retrieved JWK keys to speed up subsequent look-ups. A non-negative lifespan expressed in minutes. (Default 15 min)
261260
- **`no.nav.security.jwt.issuer.[issuer shortname].jwks-cache.refreshtime`** - A non-negative refresh time expressed in minutes. (Default 5 min)
@@ -368,4 +367,4 @@ before eventually (a couple of minutes later) it is synced to [Maven Central](ht
368367

369368
If you have any questions, please open an issue on the Github issue tracker.
370369

371-
For NAV employees, you can ask questions at the Slack channel [#token-support](https://nav-it.slack.com/archives/C01381BAT62)
370+
For NAV employees, you can ask questions at the Slack channel [#token-support](https://nav-it.slack.com/archives/C01381BAT62)

token-client-core/src/main/kotlin/no/nav/security/token/support/client/core/auth/ClientAssertion.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import java.util.UUID
1717
import kotlin.DeprecationLevel.WARNING
1818
import no.nav.security.token.support.client.core.ClientAuthenticationProperties
1919

20-
class ClientAssertion(private val tokenEndpointUrl : URI?, private val clientId : String, private val rsaKey : RSAKey, private val expiryInSeconds : Int) {
21-
constructor(tokenEndpointUrl: URI?, auth : ClientAuthenticationProperties) : this(tokenEndpointUrl, auth.clientId, auth.clientRsaKey!!, EXPIRY_IN_SECONDS)
20+
class ClientAssertion(private val tokenEndpointUrl : URI, private val clientId : String, private val rsaKey : RSAKey, private val expiryInSeconds : Int) {
21+
constructor(tokenEndpointUrl: URI, auth : ClientAuthenticationProperties) : this(tokenEndpointUrl, auth.clientId, auth.clientRsaKey!!, EXPIRY_IN_SECONDS)
2222

2323
fun assertion() =
2424
now().run {

token-client-core/src/main/kotlin/no/nav/security/token/support/client/core/http/OAuth2HttpHeaders.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class OAuth2HttpHeaders (val headers : Map<String, List<String>>) {
2525
}
2626

2727
companion object {
28+
29+
@JvmField
30+
val NONE = OAuth2HttpHeaders(emptyMap())
2831
@JvmStatic
2932
fun of(headers : Map<String, List<String>>) = OAuth2HttpHeaders(headers)
3033

token-client-core/src/main/kotlin/no/nav/security/token/support/client/core/http/OAuth2HttpRequest.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ package no.nav.security.token.support.client.core.http
22

33
import java.net.URI
44
import java.util.Collections.unmodifiableMap
5+
import no.nav.security.token.support.client.core.http.OAuth2HttpHeaders.Companion.NONE
56

6-
class OAuth2HttpRequest (val tokenEndpointUrl : URI?, val oAuth2HttpHeaders : OAuth2HttpHeaders?, val formParameters : Map<String, String>) {
7+
class OAuth2HttpRequest(val tokenEndpointUrl : URI, val oAuth2HttpHeaders : OAuth2HttpHeaders = NONE, val formParameters : Map<String, String>) {
78

89

9-
class OAuth2HttpRequestBuilder @JvmOverloads constructor(private var tokenEndpointUrl: URI?,
10-
private var oAuth2HttpHeaders : OAuth2HttpHeaders? = null,
10+
class OAuth2HttpRequestBuilder @JvmOverloads constructor(private var tokenEndpointUrl: URI,
11+
private var oAuth2HttpHeaders : OAuth2HttpHeaders = NONE,
1112
private var formParameters: MutableMap<String,String> = mutableMapOf()) {
12-
fun tokenEndpointUrl(tokenEndpointUrl : URI?) = this.also { it.tokenEndpointUrl = tokenEndpointUrl }
13+
fun tokenEndpointUrl(tokenEndpointUrl : URI) = this.also { it.tokenEndpointUrl = tokenEndpointUrl }
1314

14-
fun oAuth2HttpHeaders(oAuth2HttpHeaders : OAuth2HttpHeaders?) = this.also { it.oAuth2HttpHeaders = oAuth2HttpHeaders }
15+
fun oAuth2HttpHeaders(oAuth2HttpHeaders : OAuth2HttpHeaders) = this.also { it.oAuth2HttpHeaders = oAuth2HttpHeaders }
1516

1617
fun formParameter(key : String, value : String) = this.also { formParameters[key] = value }
1718

@@ -25,7 +26,7 @@ class OAuth2HttpRequest (val tokenEndpointUrl : URI?, val oAuth2HttpHeaders : OA
2526

2627
}
2728
companion object {
28-
fun builder( tokenEndpointUrl: URI?) = OAuth2HttpRequestBuilder(tokenEndpointUrl)
29+
fun builder( tokenEndpointUrl: URI) = OAuth2HttpRequestBuilder(tokenEndpointUrl)
2930

3031
}
3132
}

token-client-core/src/main/kotlin/no/nav/security/token/support/client/core/oauth2/AbstractOAuth2TokenClient.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ abstract class AbstractOAuth2TokenClient<T : AbstractOAuth2GrantRequest?> intern
3030
fun getTokenResponse(grantRequest : T) =
3131
grantRequest?.clientProperties?.let {
3232
runCatching {
33-
oAuth2HttpClient.post(OAuth2HttpRequest.builder(it.tokenEndpointUrl)
33+
oAuth2HttpClient.post(OAuth2HttpRequest.builder(it.tokenEndpointUrl!!)
3434
.oAuth2HttpHeaders(OAuth2HttpHeaders.of(tokenRequestHeaders(it)))
3535
.formParameters(defaultFormParameters(grantRequest).apply {
3636
putAll(formParameters(grantRequest))
@@ -77,7 +77,7 @@ abstract class AbstractOAuth2TokenClient<T : AbstractOAuth2GrantRequest?> intern
7777
PRIVATE_KEY_JWT -> LinkedHashMap<String, String>().apply {
7878
put(CLIENT_ID, authentication.clientId)
7979
put(CLIENT_ASSERTION_TYPE, JWTAuthentication.CLIENT_ASSERTION_TYPE)
80-
put(CLIENT_ASSERTION, ClientAssertion(tokenEndpointUrl, authentication).assertion())
80+
put(CLIENT_ASSERTION, ClientAssertion(tokenEndpointUrl!!, authentication).assertion())
8181
}
8282
else -> mutableMapOf()
8383
}

token-client-core/src/test/kotlin/no/nav/security/token/support/client/core/auth/ClientAssertionTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ internal class ClientAssertionTest {
2020
fun testCreateAssertion() {
2121
val clientAuth = builder("client1", PRIVATE_KEY_JWT).clientJwk("src/test/resources/jwk.json").build()
2222
val p = builder(CLIENT_CREDENTIALS, clientAuth).tokenEndpointUrl(URI.create("http://token")).build()
23-
val signedJWT = SignedJWT.parse(ClientAssertion(p.tokenEndpointUrl, p.authentication).assertion())
23+
val signedJWT = SignedJWT.parse(ClientAssertion(p.tokenEndpointUrl!!, p.authentication).assertion())
2424
assertThat(signedJWT.header.keyID).isEqualTo(p.authentication.clientRsaKey?.keyID)
2525
assertThat(signedJWT.header.type).isEqualTo(JWT)
2626
assertThat(signedJWT.header.algorithm).isEqualTo(RS256)

token-client-core/src/test/kotlin/no/nav/security/token/support/client/core/http/SimpleOAuth2HttpClient.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class SimpleOAuth2HttpClient : OAuth2HttpClient {
2323
.processResponse()
2424

2525
private fun HttpRequest.Builder.configureRequest(request: OAuth2HttpRequest): HttpRequest.Builder {
26-
request.oAuth2HttpHeaders?.headers?.forEach { (key, values) -> values.forEach { header(key, it) } }
26+
request.oAuth2HttpHeaders.headers.forEach { (key, values) -> values.forEach { header(key, it) } }
2727
uri(request.tokenEndpointUrl)
2828
POST(BodyPublishers.ofString(request.formParameters.toUrlEncodedString()))
2929
return this

token-client-kotlin-demo/src/main/resources/application.conf

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ no.nav.security.jwt {
3333
issuer_name = someshortname
3434
discoveryurl = "http://localhost:1111/default/.well-known/oauth-authorization-server"
3535
accepted_audience = debugger
36-
cookie_name = localhost-idtoken
3736
}
3837
]
3938
}

token-client-spring-demo/src/main/resources/application.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ no.nav.security.jwt:
44
someshortname:
55
discovery-url: http://metadata
66
accepted_audience: aud-localhost
7-
cookie_name: localhost-idtoken
87

98
client:
109
registration:

token-client-spring/src/main/kotlin/no/nav/security/token/support/client/spring/oauth2/DefaultOAuth2HttpClient.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import no.nav.security.token.support.client.core.oauth2.OAuth2AccessTokenRespons
1111
open class DefaultOAuth2HttpClient(val restClient: RestClient) : OAuth2HttpClient {
1212

1313

14-
override fun post(oAuth2HttpRequest: OAuth2HttpRequest) =
14+
override fun post(request: OAuth2HttpRequest) =
1515
restClient.post()
16-
.uri(oAuth2HttpRequest.tokenEndpointUrl!!)
17-
.headers { it.addAll(headers(oAuth2HttpRequest)) }
16+
.uri(request.tokenEndpointUrl)
17+
.headers { it.addAll(headers(request)) }
1818
.body(LinkedMultiValueMap<String, String>().apply {
19-
setAll(oAuth2HttpRequest.formParameters)
19+
setAll(request.formParameters)
2020
}).retrieve()
2121
.onStatus({ it.isError }) { _, response ->
22-
throw OAuth2ClientException("Received $response.statusCode from $oAuth2HttpRequest.tokenEndpointUrl")
22+
throw OAuth2ClientException("Received $response.statusCode from $request.tokenEndpointUrl")
2323
}
2424
.body(OAuth2AccessTokenResponse::class.java)
2525

token-client-spring/src/main/kotlin/no/nav/security/token/support/client/spring/oauth2/OAuth2ClientRequestInterceptor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class OAuth2ClientRequestInterceptor(private val properties: ClientConfiguration
2424
private val matcher: ClientConfigurationPropertiesMatcher) : ClientHttpRequestInterceptor {
2525
override fun intercept(req: HttpRequest, body: ByteArray, execution: ClientHttpRequestExecution): ClientHttpResponse {
2626
matcher.findProperties(properties, req.uri)?.let {
27-
service.getAccessToken(it)?.accessToken?.let { it1 -> req.headers.setBearerAuth(it1) }
27+
service.getAccessToken(it)?.accessToken?.let { token -> req.headers.setBearerAuth(token) }
2828
}
2929
return execution.execute(req, body)
3030
}

token-client-spring/src/test/kotlin/no/nav/security/token/support/client/spring/oauth2/ClientConfigurationPropertiesTestWithResourceUrl.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ import no.nav.security.token.support.core.context.TokenValidationContextHolder
1818
@ActiveProfiles("test-withresourceurl")
1919
internal class ClientConfigurationPropertiesTestWithResourceUrl {
2020

21-
private val matcher = object: ClientConfigurationPropertiesMatcher {
22-
23-
}
21+
private val matcher = object: ClientConfigurationPropertiesMatcher {}
2422
@MockBean
2523
private val tokenValidationContextHolder: TokenValidationContextHolder? = null
2624

token-client-spring/src/test/kotlin/no/nav/security/token/support/client/spring/oauth2/DefaultOAuth2HttpClientTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ internal class DefaultOAuth2HttpClientTest {
4949
val body = recordedRequest.body.readUtf8()
5050
assertThat(recordedRequest.headers["header1"]).isEqualTo("headervalue1")
5151
assertThat(recordedRequest.headers["header2"]).isEqualTo("headervalue2")
52-
assertThat(body).contains("param1=value1")
53-
assertThat(body).contains("param2=value2")
52+
assertThat(body)
53+
.contains("param1=value1")
54+
.contains("param2=value2")
5455
}
5556

5657
companion object {

token-validation-core/src/main/kotlin/no/nav/security/token/support/core/configuration/IssuerConfiguration.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ open class IssuerConfiguration(val name : String, properties : IssuerProperties,
1111

1212
val metadata : AuthorizationServerMetadata
1313
val acceptedAudience = properties.acceptedAudience
14-
val cookieName = properties.cookieName
1514
val headerName = properties.headerName
1615
val tokenValidator : JwtTokenValidator
1716

@@ -20,7 +19,7 @@ open class IssuerConfiguration(val name : String, properties : IssuerProperties,
2019
tokenValidator = tokenValidator(properties, metadata, resourceRetriever)
2120
}
2221

23-
override fun toString() = ("${javaClass.simpleName} [name=$name, metaData=$metadata, acceptedAudience=$acceptedAudience, cookieName=$cookieName, headerName=$headerName, tokenValidator=$tokenValidator, resourceRetriever=$resourceRetriever]")
22+
override fun toString() = ("${javaClass.simpleName} [name=$name, metaData=$metadata, acceptedAudience=$acceptedAudience, headerName=$headerName, tokenValidator=$tokenValidator, resourceRetriever=$resourceRetriever]")
2423

2524
companion object {
2625

token-validation-core/src/main/kotlin/no/nav/security/token/support/core/configuration/IssuerProperties.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,18 @@ import no.nav.security.token.support.core.configuration.IssuerProperties.Validat
1212

1313
class IssuerProperties @JvmOverloads constructor(val discoveryUrl : URL,
1414
val acceptedAudience : List<String> = listOf(),
15-
val cookieName : String? = null,
15+
cookieName : String? = null,
1616
val headerName : String = AUTHORIZATION_HEADER,
1717
val validation : Validation = EMPTY,
1818
val jwksCache : JwksCache = EMPTY_CACHE,
1919
val proxyUrl: URL? = null,
2020
val usePlaintextForHttps: Boolean = false) {
2121

22-
private val LOG : Logger = LoggerFactory.getLogger(IssuerProperties::class.java)
23-
2422
init {
25-
cookieName?.let { LOG.warn("Cookie-support will be discontinued in future versions, please consider changing your configuration now") }
23+
cookieName?.let { throw IllegalArgumentException("Cookie-support is discontinued, please remove $it from ypur configuration now") }
2624
}
2725

28-
override fun toString() = "IssuerProperties(discoveryUrl=$discoveryUrl, acceptedAudience=$acceptedAudience, cookieName=$cookieName, headerName=$headerName, proxyUrl=$proxyUrl, usePlaintextForHttps=$usePlaintextForHttps, validation=$validation, jwksCache=$jwksCache)"
26+
override fun toString() = "IssuerProperties(discoveryUrl=$discoveryUrl, acceptedAudience=$acceptedAudience, headerName=$headerName, proxyUrl=$proxyUrl, usePlaintextForHttps=$usePlaintextForHttps, validation=$validation, jwksCache=$jwksCache)"
2927

3028
class Validation(val optionalClaims : List<String> = emptyList()) {
3129

token-validation-core/src/main/kotlin/no/nav/security/token/support/core/http/HttpRequest.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,4 @@ package no.nav.security.token.support.core.http
55
*/
66
interface HttpRequest {
77
fun getHeader(headerName: String): String?
8-
fun getCookies(): Array<out NameValue>?
9-
10-
interface NameValue {
11-
fun getName(): String
12-
fun getValue(): String
13-
}
148
}

token-validation-core/src/main/kotlin/no/nav/security/token/support/core/validation/JwtTokenRetriever.kt

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object JwtTokenRetriever {
1414

1515
@JvmStatic
1616
fun retrieveUnvalidatedTokens(config: MultiIssuerConfiguration, request: HttpRequest) =
17-
getTokensFromHeader(config, request) + getTokensFromCookies(config, request)
17+
getTokensFromHeader(config, request)
1818

1919
private fun getTokensFromHeader(config: MultiIssuerConfiguration, request: HttpRequest): List<JwtToken> = try {
2020
LOG.debug("Checking authorization header for tokens using config {}", config)
@@ -32,22 +32,6 @@ object JwtTokenRetriever {
3232
LOG.warn("Received exception when attempting to extract and parse token from Authorization header", e)
3333
}
3434
}
35-
private fun getTokensFromCookies(config: MultiIssuerConfiguration, request: HttpRequest) = try {
36-
request.getCookies()?.asList()
37-
?.filter { containsCookieName(config, it.getName()) }
38-
?.map { JwtToken(it.getValue()) }
39-
?: emptyList<JwtToken>().also {
40-
LOG.debug("No tokens found in cookies")
41-
}
42-
} catch (e: Exception) {
43-
LOG.warn("Received exception when attempting to extract and parse token from cookie", e)
44-
listOf()
45-
}
46-
47-
private fun containsCookieName(configuration: MultiIssuerConfiguration, cookieName: String) =
48-
configuration.issuers.values.any {
49-
cookieName.equals(it.cookieName, ignoreCase = true)
50-
}
5135

5236
private fun extractBearerTokens(headerValues: List<String>) =
5337
headerValues

0 commit comments

Comments
 (0)