Skip to content

Commit

Permalink
Merge pull request #808 from navikt/deprecate
Browse files Browse the repository at this point in the history
deprecate OAuth2GrantType
  • Loading branch information
jan-olaveide authored Dec 2, 2023
2 parents c26ddf3 + 5adbd72 commit a697298
Show file tree
Hide file tree
Showing 23 changed files with 111 additions and 124 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package no.nav.security.token.support.client.core;

import com.nimbusds.jose.util.DefaultResourceRetriever;
import com.nimbusds.oauth2.sdk.GrantType
import com.nimbusds.oauth2.sdk.ParseException
import com.nimbusds.oauth2.sdk.`as`.AuthorizationServerMetadata
import java.io.IOException
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.CLIENT_CREDENTIALS
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.JWT_BEARER
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.TOKEN_EXCHANGE
import java.net.URI
class ClientProperties @JvmOverloads constructor(var tokenEndpointUrl: URI? = null,
private val wellKnownUrl: URI? = null,
val grantType: OAuth2GrantType,
val grantType: GrantType,
val scope: List<String> = emptyList(),
val authentication: ClientAuthenticationProperties,
val resourceUrl: URI? = null,
Expand All @@ -32,15 +30,15 @@ class ClientProperties @JvmOverloads constructor(var tokenEndpointUrl: URI? = nu
.tokenExchange(tokenExchange)

companion object {
private val GRANT_TYPES = listOf(JWT_BEARER, CLIENT_CREDENTIALS, TOKEN_EXCHANGE)
private val GRANT_TYPES = listOf(GrantType.JWT_BEARER, GrantType.CLIENT_CREDENTIALS, GrantType.TOKEN_EXCHANGE)

@JvmStatic
fun builder(grantType: OAuth2GrantType, authentication: ClientAuthenticationProperties) = ClientPropertiesBuilder(grantType, authentication)
fun builder(grantType: GrantType, authentication: ClientAuthenticationProperties) = ClientPropertiesBuilder(grantType, authentication)

private fun endpointUrlFromMetadata(wellKnown: URI?) =
runCatching {
wellKnown?.let { AuthorizationServerMetadata.parse(DefaultResourceRetriever().retrieveResource(wellKnown.toURL()).content).tokenEndpointURI }
?: throw OAuth2ClientException("Well known url cannot be null, please check your configuration")
?: throw OAuth2ClientException("Well knowcn url cannot be null, please check your configuration")
}.getOrElse {
when(it) {
is ParseException-> throw OAuth2ClientException("Unable to parse response from $wellKnown", it)
Expand All @@ -51,7 +49,7 @@ class ClientProperties @JvmOverloads constructor(var tokenEndpointUrl: URI? = nu
}
}

class ClientPropertiesBuilder @JvmOverloads constructor(private val grantType: OAuth2GrantType, val authentication: ClientAuthenticationProperties,
class ClientPropertiesBuilder @JvmOverloads constructor(private val grantType: GrantType, val authentication: ClientAuthenticationProperties,
private var tokenEndpointUrl: URI? = null,
private var wellKnownUrl: URI? = null,
private var scope: List<String> = emptyList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object OAuth2CacheFactory {
private fun <T> evictOnResponseExpiresIn(skewInSeconds : Long) : Expiry<T, OAuth2AccessTokenResponse> {
return object : Expiry<T, OAuth2AccessTokenResponse> {
override fun expireAfterCreate(key : T, response : OAuth2AccessTokenResponse, currentTime : Long) =
SECONDS.toNanos(if (response.expiresIn!! > skewInSeconds) response.expiresIn - skewInSeconds else response.expiresIn.toLong())
SECONDS.toNanos(if (response.expiresIn!! > skewInSeconds) response.expiresIn!! - skewInSeconds else response.expiresIn!!.toLong())
override fun expireAfterUpdate(key : T, response : OAuth2AccessTokenResponse, currentTime : Long, currentDuration : Long) = currentDuration
override fun expireAfterRead(key : T, response : OAuth2AccessTokenResponse, currentTime : Long, currentDuration : Long) = currentDuration
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
package no.nav.security.token.support.client.core

data class OAuth2GrantType(@JvmField val value : String) {
import com.nimbusds.oauth2.sdk.GrantType

@Deprecated("Use GrantType from nimbus instead", ReplaceWith("GrantType"), DeprecationLevel.WARNING)
data class OAuth2GrantType(@JvmField val value : String) {
fun value() = value

companion object {
@JvmField
val JWT_BEARER = OAuth2GrantType("urn:ietf:params:oauth:grant-type:jwt-bearer")
@Deprecated("Use GrantType.JWT_BEARER from nimbus instead")
val JWT_BEARER = OAuth2GrantType(GrantType.JWT_BEARER.value)
@JvmField
val CLIENT_CREDENTIALS = OAuth2GrantType("client_credentials")
@Deprecated("Use GrantType.CLIENT_CREDENTIALS from nimbus instead")
val CLIENT_CREDENTIALS = OAuth2GrantType(GrantType.CLIENT_CREDENTIALS.value)
@JvmField
val TOKEN_EXCHANGE = OAuth2GrantType("urn:ietf:params:oauth:grant-type:token-exchange")
@Deprecated("Use GrantType.TOKEN_EXCHANGE from nimbus instead")
val TOKEN_EXCHANGE = OAuth2GrantType(GrantType.TOKEN_EXCHANGE.value)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package no.nav.security.token.support.client.core.auth

import com.nimbusds.jose.JOSEException
import com.nimbusds.jose.JOSEObjectType.*
import com.nimbusds.jose.JWSAlgorithm.*
import com.nimbusds.jose.JWSHeader
Expand All @@ -9,8 +8,8 @@ import com.nimbusds.jose.jwk.RSAKey
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.JWTClaimsSet.Builder
import com.nimbusds.jwt.SignedJWT
import com.nimbusds.oauth2.sdk.auth.JWTAuthentication.*
import java.net.URI
import java.time.Instant
import java.time.Instant.*
import java.util.Date
import java.util.UUID
Expand All @@ -32,7 +31,7 @@ class ClientAssertion(private val tokenEndpointUrl : URI, private val clientId :
.build()).serialize()
}

fun assertionType() = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
fun assertionType() = CLIENT_ASSERTION_TYPE

private fun createSignedJWT(rsaJwk : RSAKey, claimsSet : JWTClaimsSet) =

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ class OAuth2HttpHeaders (val headers : Map<String, List<String>>) {

class Builder(private val headers : TreeMap<String, MutableList<String>> = TreeMap(CASE_INSENSITIVE_ORDER)) {

fun header(name : String, value : String) =
this.also {
headers.computeIfAbsent(name) { ArrayList(1) }
.add(value)
}
fun header(name : String, value : String) = this.also { headers.computeIfAbsent(name) { ArrayList(1) }.add(value) }

fun build() = of(headers)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package no.nav.security.token.support.client.core.jwk

import com.nimbusds.jose.jwk.JWKSet
import com.nimbusds.jose.jwk.JWKSet.*
import com.nimbusds.jose.jwk.RSAKey
import com.nimbusds.jose.jwk.RSAKey.Builder
import com.nimbusds.jose.util.Base64URL.*
import com.nimbusds.jose.jwk.RSAKey.parse
import com.nimbusds.jose.util.Base64URL.encode
import java.io.InputStream
import java.nio.charset.StandardCharsets.*
import java.nio.file.Files
import java.nio.file.Path
import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.Files.readString
import java.nio.file.Path.of
import java.security.KeyStore
import java.security.MessageDigest.*
import java.security.NoSuchAlgorithmException
import java.security.MessageDigest.getInstance
import org.slf4j.LoggerFactory

object JwkFactory {
Expand All @@ -19,17 +20,17 @@ object JwkFactory {
@JvmStatic
fun fromJsonFile(filePath : String) =
runCatching {
LOG.debug("Attempting to read jwk from path: {}", Path.of(filePath).toAbsolutePath())
fromJson(Files.readString(Path.of(filePath), UTF_8))
LOG.debug("Attempting to read JWK from path: {}", of(filePath).toAbsolutePath())
fromJson(readString(of(filePath), UTF_8))
}.getOrElse {
throw JwkInvalidException(it)
}


@JvmStatic
fun fromJson(jsonJwk : String) =
fun fromJson(jwk : String) =
runCatching {
RSAKey.parse(jsonJwk)
parse(jwk)
}.getOrElse {
throw JwkInvalidException(it)
}
Expand All @@ -48,7 +49,7 @@ object JwkFactory {
KeyStore.getInstance("JKS").run {
with(password.toCharArray()) {
load(keyStoreFile, this)
JWKSet.load(this@run) { this }
load(this@run) { this }
}
}
}.getOrElse {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package no.nav.security.token.support.client.core.oauth2

import com.nimbusds.oauth2.sdk.GrantType
import java.util.Objects
import no.nav.security.token.support.client.core.ClientProperties
import no.nav.security.token.support.client.core.OAuth2GrantType

abstract class AbstractOAuth2GrantRequest(val grantType : OAuth2GrantType, val clientProperties : ClientProperties) {
abstract class AbstractOAuth2GrantRequest(val grantType : GrantType, val clientProperties : ClientProperties) {

override fun equals(other : Any?) : Boolean {
if (this === other) return true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package no.nav.security.token.support.client.core.oauth2

import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod
import com.nimbusds.oauth2.sdk.GrantType
import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.*
import java.lang.String.*
import java.nio.charset.StandardCharsets
import java.util.Base64
import java.util.Optional
import no.nav.security.token.support.client.core.ClientProperties
import no.nav.security.token.support.client.core.OAuth2ClientException
import no.nav.security.token.support.client.core.OAuth2GrantType
import no.nav.security.token.support.client.core.OAuth2ParameterNames
import no.nav.security.token.support.client.core.auth.ClientAssertion
import no.nav.security.token.support.client.core.http.OAuth2HttpClient
Expand Down Expand Up @@ -51,8 +50,8 @@ abstract class AbstractOAuth2TokenClient<T : AbstractOAuth2GrantRequest?> intern
fun createDefaultFormParameters(grantRequest : T) : MutableMap<String, String> {
val clientProperties = grantRequest?.clientProperties ?: throw OAuth2ClientException("ClientProperties cannot be null")
val formParameters : MutableMap<String, String> = LinkedHashMap(clientAuthenticationFormParameters(grantRequest))
formParameters[OAuth2ParameterNames.GRANT_TYPE] = grantRequest.grantType.value()
if (clientProperties.grantType != OAuth2GrantType.TOKEN_EXCHANGE) {
formParameters[OAuth2ParameterNames.GRANT_TYPE] = grantRequest.grantType.value
if (clientProperties.grantType != GrantType.TOKEN_EXCHANGE) {
formParameters[OAuth2ParameterNames.SCOPE] = join(" ", clientProperties.scope)
}
return formParameters
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package no.nav.security.token.support.client.core.oauth2

import com.nimbusds.oauth2.sdk.GrantType
import no.nav.security.token.support.client.core.ClientProperties
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.CLIENT_CREDENTIALS

class ClientCredentialsGrantRequest(clientProperties : ClientProperties) : AbstractOAuth2GrantRequest(CLIENT_CREDENTIALS, clientProperties)
class ClientCredentialsGrantRequest(clientProperties : ClientProperties) : AbstractOAuth2GrantRequest(GrantType.CLIENT_CREDENTIALS, clientProperties)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package no.nav.security.token.support.client.core.oauth2

data class OAuth2AccessTokenResponse (var access_token : String? = null, var expires_at : Int? = null, var expires_in : Int? = 60, private val additionalParameters : Map<String, Any> = emptyMap()) {
val accessToken = access_token
val expiresAt = expires_at
val expiresIn = expires_in
val accessToken get() = access_token
val expiresAt get() = expires_at
val expiresIn get() = expires_in
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package no.nav.security.token.support.client.core.oauth2

import com.github.benmanes.caffeine.cache.Cache
import com.nimbusds.oauth2.sdk.GrantType.CLIENT_CREDENTIALS
import com.nimbusds.oauth2.sdk.GrantType.JWT_BEARER
import com.nimbusds.oauth2.sdk.GrantType.TOKEN_EXCHANGE
import java.util.function.Function
import org.slf4j.LoggerFactory
import no.nav.security.token.support.client.core.ClientProperties
import no.nav.security.token.support.client.core.OAuth2ClientException
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.CLIENT_CREDENTIALS
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.JWT_BEARER
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.TOKEN_EXCHANGE
import no.nav.security.token.support.client.core.context.JwtBearerTokenResolver

class OAuth2AccessTokenService @JvmOverloads constructor(private val tokenResolver : JwtBearerTokenResolver,
Expand All @@ -21,12 +21,12 @@ class OAuth2AccessTokenService @JvmOverloads constructor(private val tokenResolv


fun getAccessToken(clientProperties : ClientProperties) : OAuth2AccessTokenResponse? {
log.debug("Getting access_token for grant={}", clientProperties.grantType)
log.trace("Getting access_token for grant={}", clientProperties.grantType)
return when (clientProperties.grantType) {
JWT_BEARER -> executeOnBehalfOf(clientProperties)
CLIENT_CREDENTIALS -> executeClientCredentials(clientProperties)
TOKEN_EXCHANGE -> executeTokenExchange(clientProperties)
else -> throw OAuth2ClientException("invalid grant-type=${clientProperties.grantType.value()} from OAuth2ClientConfig.OAuth2Client. grant-type not in supported grant-types ($SUPPORTED_GRANT_TYPES)")
else -> throw OAuth2ClientException("invalid grant-type=${clientProperties.grantType.value} from OAuth2ClientConfig.OAuth2Client. grant-type not in supported grant-types ($SUPPORTED_GRANT_TYPES)")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package no.nav.security.token.support.client.core.oauth2

import com.nimbusds.oauth2.sdk.GrantType
import java.util.Objects
import no.nav.security.token.support.client.core.ClientProperties
import no.nav.security.token.support.client.core.OAuth2GrantType
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.JWT_BEARER

class OnBehalfOfGrantRequest(clientProperties : ClientProperties, val assertion : String) : AbstractOAuth2GrantRequest(JWT_BEARER, clientProperties) {
class OnBehalfOfGrantRequest(clientProperties : ClientProperties, val assertion : String) : AbstractOAuth2GrantRequest(GrantType.JWT_BEARER, clientProperties) {

override fun equals(other : Any?) : Boolean {
if (this === other) return true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package no.nav.security.token.support.client.core.oauth2

import com.nimbusds.oauth2.sdk.GrantType
import java.util.Objects
import no.nav.security.token.support.client.core.ClientProperties
import no.nav.security.token.support.client.core.OAuth2GrantType.Companion.TOKEN_EXCHANGE

class TokenExchangeGrantRequest(clientProperties : ClientProperties, val subjectToken : String) : AbstractOAuth2GrantRequest(TOKEN_EXCHANGE,
class TokenExchangeGrantRequest(clientProperties : ClientProperties, val subjectToken : String) : AbstractOAuth2GrantRequest(GrantType.TOKEN_EXCHANGE,
clientProperties) {

override fun equals(other : Any?) : Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package no.nav.security.token.support.client.core

import com.nimbusds.oauth2.sdk.GrantType
import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod
import java.io.IOException
import java.net.URI
Expand Down Expand Up @@ -28,15 +29,9 @@ internal class ClientPropertiesTest {

@Test
fun validGrantTypes() {
Assertions.assertNotNull(clientPropertiesFromGrantType(OAuth2GrantType.JWT_BEARER))
Assertions.assertNotNull(clientPropertiesFromGrantType(OAuth2GrantType.CLIENT_CREDENTIALS))
Assertions.assertNotNull(clientPropertiesFromGrantType(OAuth2GrantType.TOKEN_EXCHANGE))
}

@Test
fun invalidGrantTypes() {
org.assertj.core.api.Assertions.assertThatExceptionOfType(IllegalArgumentException::class.java)
.isThrownBy { clientPropertiesFromGrantType(OAuth2GrantType("somegrantNotSupported")) }
Assertions.assertNotNull(clientPropertiesFromGrantType(GrantType.JWT_BEARER))
Assertions.assertNotNull(clientPropertiesFromGrantType(GrantType.CLIENT_CREDENTIALS))
Assertions.assertNotNull(clientPropertiesFromGrantType(GrantType.TOKEN_EXCHANGE))
}

@Test
Expand All @@ -63,7 +58,7 @@ internal class ClientPropertiesTest {
return ClientProperties(
null,
wellKnownUrl,
OAuth2GrantType.CLIENT_CREDENTIALS, listOf("scope1", "scope2"),
GrantType.CLIENT_CREDENTIALS, listOf("scope1", "scope2"),
clientAuth(),
null,
tokenExchange()
Expand All @@ -85,7 +80,7 @@ internal class ClientPropertiesTest {
)
}

private fun clientPropertiesFromGrantType(grantType : OAuth2GrantType) : ClientProperties {
private fun clientPropertiesFromGrantType(grantType : GrantType) : ClientProperties {
return ClientProperties(
URI.create("http://token"),
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package no.nav.security.token.support.client.core
import com.nimbusds.jwt.JWT
import com.nimbusds.jwt.JWTClaimsSet.Builder
import com.nimbusds.jwt.PlainJWT
import com.nimbusds.oauth2.sdk.GrantType
import com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod
import java.io.IOException
import java.io.UnsupportedEncodingException
Expand All @@ -29,7 +30,7 @@ object TestUtils {
const val CONTENT_TYPE_FORM_URL_ENCODED = "application/x-www-form-urlencoded;charset=UTF-8"
const val CONTENT_TYPE_JSON = "application/json;charset=UTF-8"
@JvmStatic
fun clientProperties(tokenEndpointUrl : String?, oAuth2GrantType : OAuth2GrantType?) : ClientProperties {
fun clientProperties(tokenEndpointUrl : String?, oAuth2GrantType : GrantType?) : ClientProperties {
return builder(oAuth2GrantType!!, builder("client1", ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.clientSecret("clientSecret1")
.build())
Expand All @@ -40,7 +41,7 @@ object TestUtils {

fun tokenExchangeClientProperties(
tokenEndpointUrl : String?,
oAuth2GrantType : OAuth2GrantType?,
oAuth2GrantType : GrantType?,
clientPrivateKey : String?
) : ClientProperties {
return builder(oAuth2GrantType!!, builder("client1", ClientAuthenticationMethod.PRIVATE_KEY_JWT)
Expand Down
Loading

0 comments on commit a697298

Please sign in to comment.