From 9d83b1c68429dc32cb9295f48ddf9bd5b90c0665 Mon Sep 17 00:00:00 2001 From: Tom Reay Date: Mon, 15 Oct 2018 08:54:46 +0100 Subject: [PATCH 1/9] Fix RefreshTokenGrantTokenServiceTest which was expecting the new refresh token to be converted to an access token rather than the old one --- .../java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt index 9328c04..09b22da 100644 --- a/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt +++ b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt @@ -70,7 +70,7 @@ internal class RefreshTokenGrantTokenServiceTest { every { tokenStore.refreshToken(refreshToken) } returns token every { identityService.identityOf(client, username) } returns identity every { refreshTokenConverter.convertToToken(username, clientId, scopes) } returns newRefreshToken - every { accessTokenConverter.convertToToken(username, clientId, scopes, newRefreshToken) } returns accessToken + every { accessTokenConverter.convertToToken(username, clientId, scopes, token) } returns accessToken tokenService.refresh(refreshTokenRequest) From 363273964a8a412f0250ffa7eb903ddbabd8a92a Mon Sep 17 00:00:00 2001 From: Tom Reay Date: Mon, 15 Oct 2018 10:11:15 +0100 Subject: [PATCH 2/9] Update Ktor to 0.9.5 (#2) --- oauth2-server-ktor/pom.xml | 2 +- .../java/nl/myndocs/oauth2/ktor/feature/Oauth2ServerFeature.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2-server-ktor/pom.xml b/oauth2-server-ktor/pom.xml index 13b29c6..7858cb8 100644 --- a/oauth2-server-ktor/pom.xml +++ b/oauth2-server-ktor/pom.xml @@ -12,7 +12,7 @@ oauth2-server-ktor - 0.9.3 + 0.9.5 0.6.1 diff --git a/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/Oauth2ServerFeature.kt b/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/Oauth2ServerFeature.kt index 4392f09..83c5a05 100644 --- a/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/Oauth2ServerFeature.kt +++ b/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/Oauth2ServerFeature.kt @@ -24,7 +24,7 @@ class Oauth2ServerFeature(configuration: Configuration) { val feature = Oauth2ServerFeature(configuration) - pipeline.intercept(ApplicationCallPipeline.Infrastructure) { + pipeline.intercept(ApplicationCallPipeline.Features) { val ktorCallContext = KtorCallContext(call) val authorizer = feature.authorizerFactory(ktorCallContext) From d06a928d5eb1e358c978ea03a6563d77b266757a Mon Sep 17 00:00:00 2001 From: Tom Reay Date: Tue, 16 Oct 2018 14:53:00 +0100 Subject: [PATCH 3/9] Add support for client credentials grant --- .../main/java/nl/myndocs/oauth2/CallRouter.kt | 17 ++- .../nl/myndocs/oauth2/Oauth2TokenService.kt | 27 ++++- .../java/nl/myndocs/oauth2/TokenService.kt | 2 + .../oauth2/client/AuthorizedGrantType.kt | 1 + .../request/ClientCredentialsRequest.kt | 9 ++ .../nl/myndocs/oauth2/token/AccessToken.kt | 2 +- .../nl/myndocs/oauth2/token/RefreshToken.kt | 2 +- .../token/converter/AccessTokenConverter.kt | 2 +- .../token/converter/RefreshTokenConverter.kt | 2 +- .../converter/UUIDAccessTokenConverter.kt | 2 +- .../converter/UUIDRefreshTokenConverter.kt | 2 +- .../ClientCredentialsTokenServiceTest.kt | 106 ++++++++++++++++++ 12 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 oauth2-server-core/src/main/java/nl/myndocs/oauth2/request/ClientCredentialsRequest.kt create mode 100644 oauth2-server-core/src/test/java/nl/myndocs/oauth2/ClientCredentialsTokenServiceTest.kt diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt index 6a7e332..fb7bbb6 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt @@ -1,6 +1,10 @@ package nl.myndocs.oauth2 import nl.myndocs.oauth2.authenticator.Authorizer +import nl.myndocs.oauth2.client.AuthorizedGrantType.AUTHORIZATION_CODE +import nl.myndocs.oauth2.client.AuthorizedGrantType.CLIENT_CREDENTIALS +import nl.myndocs.oauth2.client.AuthorizedGrantType.PASSWORD +import nl.myndocs.oauth2.client.AuthorizedGrantType.REFRESH_TOKEN import nl.myndocs.oauth2.exception.* import nl.myndocs.oauth2.identity.UserInfo import nl.myndocs.oauth2.request.* @@ -38,7 +42,7 @@ class CallRouter( } try { - val allowedGrantTypes = setOf("password", "authorization_code", "refresh_token") + val allowedGrantTypes = setOf(PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, CLIENT_CREDENTIALS) val grantType = callContext.formParameters["grant_type"] ?: throw InvalidRequestException("'grant_type' not given") @@ -50,6 +54,7 @@ class CallRouter( "password" -> routePasswordGrant(callContext, tokenService) "authorization_code" -> routeAuthorizationCodeGrant(callContext, tokenService) "refresh_token" -> routeRefreshTokenGrant(callContext, tokenService) + "client_credentials" -> routeClientCredentialsGrant(callContext, tokenService) } } catch (oauthException: OauthException) { callContext.respondStatus(STATUS_BAD_REQUEST) @@ -71,6 +76,16 @@ class CallRouter( callContext.respondJson(tokenResponse.toMap()) } + fun routeClientCredentialsGrant(callContext: CallContext, tokenService: TokenService) { + val tokenResponse = tokenService.authorize(ClientCredentialsRequest( + callContext.formParameters["client_id"], + callContext.formParameters["client_secret"], + callContext.formParameters["scope"] + )) + + callContext.respondJson(tokenResponse.toMap()) + } + fun routeRefreshTokenGrant(callContext: CallContext, tokenService: TokenService) { val accessToken = tokenService.refresh( RefreshTokenRequest( diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt index f8157e5..ff7be94 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt @@ -119,6 +119,31 @@ class Oauth2TokenService( return accessToken.toTokenResponse() } + override fun authorize(clientCredentialsRequest: ClientCredentialsRequest): TokenResponse { + throwExceptionIfUnverifiedClient(clientCredentialsRequest) + + val requestedClient = clientService.clientOf(clientCredentialsRequest.clientId!!) ?: throw InvalidClientException() + + val scopes = clientCredentialsRequest.scope + ?.let { ScopeParser.parseScopes(it).toSet() } + ?: requestedClient.clientScopes + + val accessToken = accessTokenConverter.convertToToken( + username = null, + clientId = clientCredentialsRequest.clientId, + requestedScopes = scopes, + refreshToken = refreshTokenConverter.convertToToken( + username = null, + clientId = clientCredentialsRequest.clientId, + requestedScopes = scopes + ) + ) + + tokenStore.storeAccessToken(accessToken) + + return accessToken.toTokenResponse() + } + override fun refresh(refreshTokenRequest: RefreshTokenRequest): TokenResponse { throwExceptionIfUnverifiedClient(refreshTokenRequest) @@ -293,7 +318,7 @@ class Oauth2TokenService( override fun userInfo(accessToken: String): UserInfo { val storedAccessToken = tokenStore.accessToken(accessToken) ?: throw InvalidGrantException() val client = clientService.clientOf(storedAccessToken.clientId) ?: throw InvalidClientException() - val identity = identityService.identityOf(client, storedAccessToken.username) + val identity = storedAccessToken.username?.let { identityService.identityOf(client, it) } ?: throw InvalidIdentityException() return UserInfo( diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt index 80c9e78..5d60d3e 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt @@ -13,6 +13,8 @@ interface TokenService { fun authorize(authorizationCodeRequest: AuthorizationCodeRequest): TokenResponse + fun authorize(clientCredentialsRequest: ClientCredentialsRequest): TokenResponse + fun refresh(refreshTokenRequest: RefreshTokenRequest): TokenResponse fun redirect( diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/client/AuthorizedGrantType.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/client/AuthorizedGrantType.kt index 42445be..67138b9 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/client/AuthorizedGrantType.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/client/AuthorizedGrantType.kt @@ -5,4 +5,5 @@ object AuthorizedGrantType { const val REFRESH_TOKEN = "refresh_token" const val PASSWORD = "password" const val AUTHORIZATION_CODE = "authorization_code" + const val CLIENT_CREDENTIALS = "client_credentials" } diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/request/ClientCredentialsRequest.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/request/ClientCredentialsRequest.kt new file mode 100644 index 0000000..c592126 --- /dev/null +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/request/ClientCredentialsRequest.kt @@ -0,0 +1,9 @@ +package nl.myndocs.oauth2.request + +data class ClientCredentialsRequest( + override val clientId: String?, + override val clientSecret: String?, + val scope: String? +) : ClientRequest{ + val grant_type = "client_credentials" +} \ No newline at end of file diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/AccessToken.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/AccessToken.kt index 46f0adb..81c730c 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/AccessToken.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/AccessToken.kt @@ -6,7 +6,7 @@ data class AccessToken( val accessToken: String, val tokenType: String, override val expireTime: Instant, - val username: String, + val username: String?, val clientId: String, val scopes: Set, val refreshToken: RefreshToken? diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/RefreshToken.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/RefreshToken.kt index 18f7a72..e196c31 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/RefreshToken.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/RefreshToken.kt @@ -5,7 +5,7 @@ import java.time.Instant data class RefreshToken( val refreshToken: String, override val expireTime: Instant, - val username: String, + val username: String?, val clientId: String, val scopes: Set ) : ExpirableToken \ No newline at end of file diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/AccessTokenConverter.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/AccessTokenConverter.kt index b263390..d21120a 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/AccessTokenConverter.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/AccessTokenConverter.kt @@ -4,5 +4,5 @@ import nl.myndocs.oauth2.token.AccessToken import nl.myndocs.oauth2.token.RefreshToken interface AccessTokenConverter { - fun convertToToken(username: String, clientId: String, requestedScopes: Set, refreshToken: RefreshToken?): AccessToken + fun convertToToken(username: String?, clientId: String, requestedScopes: Set, refreshToken: RefreshToken?): AccessToken } \ No newline at end of file diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt index 5222764..4ab0457 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt @@ -3,5 +3,5 @@ package nl.myndocs.oauth2.token.converter import nl.myndocs.oauth2.token.RefreshToken interface RefreshTokenConverter { - fun convertToToken(username: String, clientId: String, requestedScopes: Set): RefreshToken + fun convertToToken(username: String?, clientId: String, requestedScopes: Set): RefreshToken } \ No newline at end of file diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDAccessTokenConverter.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDAccessTokenConverter.kt index bba0908..5ba0d2a 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDAccessTokenConverter.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDAccessTokenConverter.kt @@ -9,7 +9,7 @@ class UUIDAccessTokenConverter( private val accessTokenExpireInSeconds: Int = 3600 ) : AccessTokenConverter { - override fun convertToToken(username: String, clientId: String, requestedScopes: Set, refreshToken: RefreshToken?): AccessToken { + override fun convertToToken(username: String?, clientId: String, requestedScopes: Set, refreshToken: RefreshToken?): AccessToken { return AccessToken( UUID.randomUUID().toString(), "bearer", diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDRefreshTokenConverter.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDRefreshTokenConverter.kt index 3164ded..93c9b88 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDRefreshTokenConverter.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDRefreshTokenConverter.kt @@ -7,7 +7,7 @@ import java.util.* class UUIDRefreshTokenConverter( private val refreshTokenExpireInSeconds: Int = 86400 ) : RefreshTokenConverter { - override fun convertToToken(username: String, clientId: String, requestedScopes: Set): RefreshToken { + override fun convertToToken(username: String?, clientId: String, requestedScopes: Set): RefreshToken { return RefreshToken( UUID.randomUUID().toString(), Instant.now().plusSeconds(refreshTokenExpireInSeconds.toLong()), diff --git a/oauth2-server-core/src/test/java/nl/myndocs/oauth2/ClientCredentialsTokenServiceTest.kt b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/ClientCredentialsTokenServiceTest.kt new file mode 100644 index 0000000..a3c1277 --- /dev/null +++ b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/ClientCredentialsTokenServiceTest.kt @@ -0,0 +1,106 @@ +package nl.myndocs.oauth2 + +import io.mockk.every +import io.mockk.impl.annotations.InjectMockKs +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.junit5.MockKExtension +import io.mockk.verify +import nl.myndocs.oauth2.client.AuthorizedGrantType +import nl.myndocs.oauth2.client.Client +import nl.myndocs.oauth2.client.ClientService +import nl.myndocs.oauth2.exception.InvalidClientException +import nl.myndocs.oauth2.identity.IdentityService +import nl.myndocs.oauth2.request.ClientCredentialsRequest +import nl.myndocs.oauth2.token.AccessToken +import nl.myndocs.oauth2.token.RefreshToken +import nl.myndocs.oauth2.token.TokenStore +import nl.myndocs.oauth2.token.converter.AccessTokenConverter +import nl.myndocs.oauth2.token.converter.CodeTokenConverter +import nl.myndocs.oauth2.token.converter.RefreshTokenConverter +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import java.time.Instant + +@ExtendWith(MockKExtension::class) +internal class ClientCredentialsTokenServiceTest { + @MockK + lateinit var identityService: IdentityService + @MockK + lateinit var clientService: ClientService + @RelaxedMockK + lateinit var tokenStore: TokenStore + @MockK + lateinit var accessTokenConverter: AccessTokenConverter + @MockK + lateinit var refreshTokenConverter: RefreshTokenConverter + @MockK + lateinit var codeTokenConverter: CodeTokenConverter + + @InjectMockKs + lateinit var tokenService: Oauth2TokenService + + private val clientId = "client-foo" + private val clientSecret = "client-secret" + private val scope = "scope1" + private val scopes = setOf(scope) + private val clientCredentialsRequest = ClientCredentialsRequest(clientId, clientSecret, scope) + + @Test + fun validClientCredentialsGrant() { + val client = Client(clientId, emptySet(), emptySet(), setOf(AuthorizedGrantType.CLIENT_CREDENTIALS)) + val refreshToken = RefreshToken("test", Instant.now(), null, clientId, scopes) + val accessToken = AccessToken("test", "bearer", Instant.now(), null, clientId, scopes, refreshToken) + + every { clientService.clientOf(clientId) } returns client + every { clientService.validClient(client, clientSecret) } returns true + every { refreshTokenConverter.convertToToken(null, clientId, scopes) } returns refreshToken + every { accessTokenConverter.convertToToken(null, clientId, scopes, refreshToken) } returns accessToken + + tokenService.authorize(clientCredentialsRequest) + + verify { tokenStore.storeAccessToken(accessToken) } + } + + @Test + fun nonExistingClientException() { + every { clientService.clientOf(clientId) } returns null + + Assertions.assertThrows( + InvalidClientException::class.java + ) { tokenService.authorize(clientCredentialsRequest) } + } + + @Test + fun invalidClientException() { + val client = Client(clientId, emptySet(), emptySet(), setOf(AuthorizedGrantType.CLIENT_CREDENTIALS)) + every { clientService.clientOf(clientId) } returns client + every { clientService.validClient(client, clientSecret) } returns false + + Assertions.assertThrows( + InvalidClientException::class.java + ) { tokenService.authorize(clientCredentialsRequest) } + } + + @Test + fun clientScopesAsFallback() { + val clientCredentialsRequest = ClientCredentialsRequest( + clientId, + clientSecret, + null + ) + + val client = Client(clientId, setOf("scope1", "scope2"), setOf(), setOf(AuthorizedGrantType.CLIENT_CREDENTIALS)) + val requestScopes = setOf("scope1", "scope2") + val refreshToken = RefreshToken("test", Instant.now(), null, clientId, requestScopes) + val accessToken = AccessToken("test", "bearer", Instant.now(), null, clientId, requestScopes, refreshToken) + + every { clientService.clientOf(clientId) } returns client + every { clientService.validClient(client, clientSecret) } returns true + every { refreshTokenConverter.convertToToken(null, clientId, requestScopes) } returns refreshToken + every { accessTokenConverter.convertToToken(null, clientId, requestScopes, refreshToken) } returns accessToken + + tokenService.authorize(clientCredentialsRequest) + } +} \ No newline at end of file From 352d6a1103a8916a3002951d0f2bf1f1e104ba0e Mon Sep 17 00:00:00 2001 From: Tom Reay Date: Tue, 16 Oct 2018 15:04:02 +0100 Subject: [PATCH 4/9] Fix refresh token always being reused - allow the implementor to create a new refresh token each time (Fixes #29) --- .../src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt | 6 +++++- .../nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt index f8157e5..c4f1345 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt @@ -143,7 +143,11 @@ class Oauth2TokenService( refreshToken.username, refreshToken.clientId, refreshToken.scopes, - refreshToken + refreshTokenConverter.convertToToken( + refreshToken.username, + refreshToken.clientId, + refreshToken.scopes + ) ) tokenStore.storeAccessToken(accessToken) diff --git a/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt index 09b22da..61cb84c 100644 --- a/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt +++ b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt @@ -61,7 +61,7 @@ internal class RefreshTokenGrantTokenServiceTest { fun validRefreshToken() { val client = Client(clientId, setOf("scope1", "scope2"), setOf(), setOf(AuthorizedGrantType.REFRESH_TOKEN)) val token = RefreshToken("test", Instant.now(), username, clientId, scopes) - val newRefreshToken = RefreshToken("test", Instant.now(), username, clientId, scopes) + val newRefreshToken = RefreshToken("new-test", Instant.now(), username, clientId, scopes) val accessToken = AccessToken("test", "bearer", Instant.now(), username, clientId, scopes, newRefreshToken) val identity = Identity(username) @@ -70,7 +70,7 @@ internal class RefreshTokenGrantTokenServiceTest { every { tokenStore.refreshToken(refreshToken) } returns token every { identityService.identityOf(client, username) } returns identity every { refreshTokenConverter.convertToToken(username, clientId, scopes) } returns newRefreshToken - every { accessTokenConverter.convertToToken(username, clientId, scopes, token) } returns accessToken + every { accessTokenConverter.convertToToken(username, clientId, scopes, newRefreshToken) } returns accessToken tokenService.refresh(refreshTokenRequest) From 3aa273160a54e94dfac12c2c2bd3d681b73fdba0 Mon Sep 17 00:00:00 2001 From: Tom Reay Date: Tue, 16 Oct 2018 21:06:14 +0100 Subject: [PATCH 5/9] Create overloaded refresh token convert method so that the implementor can choose to reuse the refresh token --- .../src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt | 6 +----- .../myndocs/oauth2/token/converter/RefreshTokenConverter.kt | 1 + .../nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt index c4f1345..ddc3ae0 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt @@ -143,11 +143,7 @@ class Oauth2TokenService( refreshToken.username, refreshToken.clientId, refreshToken.scopes, - refreshTokenConverter.convertToToken( - refreshToken.username, - refreshToken.clientId, - refreshToken.scopes - ) + refreshTokenConverter.convertToToken(refreshToken) ) tokenStore.storeAccessToken(accessToken) diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt index 5222764..8a957d4 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt @@ -3,5 +3,6 @@ package nl.myndocs.oauth2.token.converter import nl.myndocs.oauth2.token.RefreshToken interface RefreshTokenConverter { + fun convertToToken(refreshToken: RefreshToken): RefreshToken = convertToToken(refreshToken.username, refreshToken.clientId, refreshToken.scopes) fun convertToToken(username: String, clientId: String, requestedScopes: Set): RefreshToken } \ No newline at end of file diff --git a/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt index 61cb84c..96a428c 100644 --- a/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt +++ b/oauth2-server-core/src/test/java/nl/myndocs/oauth2/RefreshTokenGrantTokenServiceTest.kt @@ -69,7 +69,7 @@ internal class RefreshTokenGrantTokenServiceTest { every { clientService.validClient(client, clientSecret) } returns true every { tokenStore.refreshToken(refreshToken) } returns token every { identityService.identityOf(client, username) } returns identity - every { refreshTokenConverter.convertToToken(username, clientId, scopes) } returns newRefreshToken + every { refreshTokenConverter.convertToToken(token) } returns newRefreshToken every { accessTokenConverter.convertToToken(username, clientId, scopes, newRefreshToken) } returns accessToken tokenService.refresh(refreshTokenRequest) From 2fbacabf88c95e208a1acd36c47c388bbc2ec7b9 Mon Sep 17 00:00:00 2001 From: Albert Date: Fri, 19 Oct 2018 21:30:50 +0200 Subject: [PATCH 6/9] Upgrade sparkjava --- oauth2-server-sparkjava/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2-server-sparkjava/pom.xml b/oauth2-server-sparkjava/pom.xml index 8cf0d7d..0746433 100644 --- a/oauth2-server-sparkjava/pom.xml +++ b/oauth2-server-sparkjava/pom.xml @@ -15,7 +15,7 @@ com.sparkjava spark-core - 2.5.4 + 2.8.0 provided From 42e7ddb7a9b2782bc466ae67361bcacec3b0da93 Mon Sep 17 00:00:00 2001 From: Albert Bos Date: Wed, 14 Nov 2018 13:56:08 +0100 Subject: [PATCH 7/9] Upgrade kotlin to 1.3 Upgrade ktor to 1.0.0-beta-4 --- oauth2-server-core/pom.xml | 2 +- oauth2-server-ktor/pom.xml | 3 +-- .../nl/myndocs/oauth2/ktor/feature/request/KtorCallContext.kt | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/oauth2-server-core/pom.xml b/oauth2-server-core/pom.xml index 08130b6..39db353 100644 --- a/oauth2-server-core/pom.xml +++ b/oauth2-server-core/pom.xml @@ -21,7 +21,7 @@ io.mockk mockk - 1.8.6 + 1.8.12.kotlin13 test diff --git a/oauth2-server-ktor/pom.xml b/oauth2-server-ktor/pom.xml index 7858cb8..2a34929 100644 --- a/oauth2-server-ktor/pom.xml +++ b/oauth2-server-ktor/pom.xml @@ -12,8 +12,7 @@ oauth2-server-ktor - 0.9.5 - 0.6.1 + 1.0.0-beta-4 diff --git a/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/request/KtorCallContext.kt b/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/request/KtorCallContext.kt index 301495a..dba2115 100644 --- a/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/request/KtorCallContext.kt +++ b/oauth2-server-ktor/src/main/java/nl/myndocs/oauth2/ktor/feature/request/KtorCallContext.kt @@ -10,7 +10,7 @@ import io.ktor.response.header import io.ktor.response.respondRedirect import io.ktor.response.respondText import io.ktor.util.toMap -import kotlinx.coroutines.experimental.runBlocking +import kotlinx.coroutines.runBlocking import nl.myndocs.oauth2.json.JsonMapper import nl.myndocs.oauth2.request.CallContext diff --git a/pom.xml b/pom.xml index ae6e118..e934fe1 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 0.2.2 - 1.2.60 + 1.3.0 1.8 1.8 From 4eddcbac71fb02f0bc638ebcd998b379a438bcbc Mon Sep 17 00:00:00 2001 From: Albert Date: Thu, 15 Nov 2018 21:07:45 +0100 Subject: [PATCH 8/9] Change user info to token info --- .../main/java/nl/myndocs/oauth2/CallRouter.kt | 14 +++++++------- .../java/nl/myndocs/oauth2/Oauth2TokenService.kt | 7 +++---- .../main/java/nl/myndocs/oauth2/TokenService.kt | 4 ++-- .../myndocs/oauth2/config/CallRouterBuilder.kt | 16 ++++++++-------- .../oauth2/config/ConfigurationBuilder.kt | 14 +++++++------- .../identity/{UserInfo.kt => TokenInfo.kt} | 4 ++-- .../nl/myndocs/oauth2/http4k/Oauth2Server.kt | 2 +- .../nl/myndocs/oauth2/javalin/Oauth2Server.kt | 2 +- .../nl/myndocs/oauth2/sparkjava/Oauth2Server.kt | 2 +- 9 files changed, 32 insertions(+), 33 deletions(-) rename oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/{UserInfo.kt => TokenInfo.kt} (71%) diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt index fb7bbb6..6fbcf73 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt @@ -6,7 +6,7 @@ import nl.myndocs.oauth2.client.AuthorizedGrantType.CLIENT_CREDENTIALS import nl.myndocs.oauth2.client.AuthorizedGrantType.PASSWORD import nl.myndocs.oauth2.client.AuthorizedGrantType.REFRESH_TOKEN import nl.myndocs.oauth2.exception.* -import nl.myndocs.oauth2.identity.UserInfo +import nl.myndocs.oauth2.identity.TokenInfo import nl.myndocs.oauth2.request.* import nl.myndocs.oauth2.token.toMap @@ -14,8 +14,8 @@ class CallRouter( private val tokenService: TokenService, val tokenEndpoint: String, val authorizeEndpoint: String, - val userInfoEndpoint: String, - private val userInfoCallback: (UserInfo) -> Map + val tokenInfoEndpoint: String, + private val tokenInfoCallback: (TokenInfo) -> Map ) { companion object { const val METHOD_POST = "post" @@ -32,7 +32,7 @@ class CallRouter( when (callContext.path) { tokenEndpoint -> routeTokenEndpoint(callContext) authorizeEndpoint -> routeAuthorizeEndpoint(callContext, authorizer) - userInfoEndpoint -> routeUserInfoEndpoint(callContext) + tokenInfoEndpoint -> routeTokenInfoEndpoint(callContext) } } @@ -208,7 +208,7 @@ class CallRouter( } } - private fun routeUserInfoEndpoint(callContext: CallContext) { + private fun routeTokenInfoEndpoint(callContext: CallContext) { if (callContext.method.toLowerCase() != METHOD_GET) { return } @@ -222,8 +222,8 @@ class CallRouter( val token = authorization.substring(7) - val userInfoCallback = userInfoCallback(tokenService.userInfo(token)) + val tokenInfoCallback = tokenInfoCallback(tokenService.tokenInfo(token)) - callContext.respondJson(userInfoCallback) + callContext.respondJson(tokenInfoCallback) } } \ No newline at end of file diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt index 969d378..7ccaca3 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt @@ -8,7 +8,7 @@ import nl.myndocs.oauth2.client.ClientService import nl.myndocs.oauth2.exception.* import nl.myndocs.oauth2.identity.Identity import nl.myndocs.oauth2.identity.IdentityService -import nl.myndocs.oauth2.identity.UserInfo +import nl.myndocs.oauth2.identity.TokenInfo import nl.myndocs.oauth2.request.* import nl.myndocs.oauth2.response.TokenResponse import nl.myndocs.oauth2.scope.ScopeParser @@ -315,13 +315,12 @@ class Oauth2TokenService( } } - override fun userInfo(accessToken: String): UserInfo { + override fun tokenInfo(accessToken: String): TokenInfo { val storedAccessToken = tokenStore.accessToken(accessToken) ?: throw InvalidGrantException() val client = clientService.clientOf(storedAccessToken.clientId) ?: throw InvalidClientException() val identity = storedAccessToken.username?.let { identityService.identityOf(client, it) } - ?: throw InvalidIdentityException() - return UserInfo( + return TokenInfo( identity, client, storedAccessToken.scopes diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt index 5d60d3e..215405e 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt @@ -2,7 +2,7 @@ package nl.myndocs.oauth2 import nl.myndocs.oauth2.authenticator.Authenticator import nl.myndocs.oauth2.authenticator.IdentityScopeVerifier -import nl.myndocs.oauth2.identity.UserInfo +import nl.myndocs.oauth2.identity.TokenInfo import nl.myndocs.oauth2.request.* import nl.myndocs.oauth2.response.TokenResponse import nl.myndocs.oauth2.token.AccessToken @@ -29,5 +29,5 @@ interface TokenService { identityScopeVerifier: IdentityScopeVerifier? ): AccessToken - fun userInfo(accessToken: String): UserInfo + fun tokenInfo(accessToken: String): TokenInfo } \ No newline at end of file diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/CallRouterBuilder.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/CallRouterBuilder.kt index f31aec1..33732cb 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/CallRouterBuilder.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/CallRouterBuilder.kt @@ -2,18 +2,18 @@ package nl.myndocs.oauth2.config import nl.myndocs.oauth2.CallRouter import nl.myndocs.oauth2.TokenService -import nl.myndocs.oauth2.identity.UserInfo +import nl.myndocs.oauth2.identity.TokenInfo internal object CallRouterBuilder { class Configuration { var tokenEndpoint: String = "/oauth/token" var authorizeEndpoint: String = "/oauth/authorize" - var userInfoEndpoint: String = "/oauth/userinfo" - var userInfoCallback: (UserInfo) -> Map = { userInfo -> + var tokenInfoEndpoint: String = "/oauth/tokeninfo" + var tokenInfoCallback: (TokenInfo) -> Map = { tokenInfo -> mapOf( - "username" to userInfo.identity.username, - "scopes" to userInfo.scopes - ) + "username" to tokenInfo.identity?.username, + "scopes" to tokenInfo.scopes + ).filterValues { it != null } } var tokenService: TokenService? = null } @@ -29,7 +29,7 @@ internal object CallRouterBuilder { configuration.tokenService!!, configuration.tokenEndpoint, configuration.authorizeEndpoint, - configuration.userInfoEndpoint, - configuration.userInfoCallback + configuration.tokenInfoEndpoint, + configuration.tokenInfoCallback ) } \ No newline at end of file diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/ConfigurationBuilder.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/ConfigurationBuilder.kt index 6acd614..e73177f 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/ConfigurationBuilder.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/ConfigurationBuilder.kt @@ -2,7 +2,7 @@ package nl.myndocs.oauth2.config import nl.myndocs.oauth2.TokenService import nl.myndocs.oauth2.authenticator.Authorizer -import nl.myndocs.oauth2.identity.UserInfo +import nl.myndocs.oauth2.identity.TokenInfo import nl.myndocs.oauth2.request.CallContext import nl.myndocs.oauth2.request.auth.BasicAuthorizer @@ -28,16 +28,16 @@ object ConfigurationBuilder { callRouterConfiguration.tokenEndpoint = value } - var userInfoEndpoint: String - get() = callRouterConfiguration.userInfoEndpoint + var tokenInfoEndpoint: String + get() = callRouterConfiguration.tokenInfoEndpoint set(value) { - callRouterConfiguration.userInfoEndpoint = value + callRouterConfiguration.tokenInfoEndpoint = value } - var userInfoCallback: (UserInfo) -> Map - get() = callRouterConfiguration.userInfoCallback + var tokenInfoCallback: (TokenInfo) -> Map + get() = callRouterConfiguration.tokenInfoCallback set(value) { - callRouterConfiguration.userInfoCallback = value + callRouterConfiguration.tokenInfoCallback = value } var authorizerFactory: (CallContext) -> Authorizer = ::BasicAuthorizer diff --git a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/UserInfo.kt b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/TokenInfo.kt similarity index 71% rename from oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/UserInfo.kt rename to oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/TokenInfo.kt index f34e0bd..6b91525 100644 --- a/oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/UserInfo.kt +++ b/oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/TokenInfo.kt @@ -2,8 +2,8 @@ package nl.myndocs.oauth2.identity import nl.myndocs.oauth2.client.Client -data class UserInfo( - val identity: Identity, +data class TokenInfo( + val identity: Identity?, val client: Client, val scopes: Set ) \ No newline at end of file diff --git a/oauth2-server-http4k/src/main/java/nl/myndocs/oauth2/http4k/Oauth2Server.kt b/oauth2-server-http4k/src/main/java/nl/myndocs/oauth2/http4k/Oauth2Server.kt index 402f2ec..833fc49 100644 --- a/oauth2-server-http4k/src/main/java/nl/myndocs/oauth2/http4k/Oauth2Server.kt +++ b/oauth2-server-http4k/src/main/java/nl/myndocs/oauth2/http4k/Oauth2Server.kt @@ -30,7 +30,7 @@ infix fun RoutingHttpHandler.`enable oauth2`(configurationCallback: Configuratio responseBuilder.build() }, - callRouter.userInfoEndpoint bind Method.GET to { request: Request -> + callRouter.tokenInfoEndpoint bind Method.GET to { request: Request -> val responseBuilder = ResponseBuilder() val callContext = Http4kCallContext(request, responseBuilder) callRouter.route(callContext, configuration.authorizerFactory(callContext)) diff --git a/oauth2-server-javalin/src/main/java/nl/myndocs/oauth2/javalin/Oauth2Server.kt b/oauth2-server-javalin/src/main/java/nl/myndocs/oauth2/javalin/Oauth2Server.kt index 2cc0840..b004224 100644 --- a/oauth2-server-javalin/src/main/java/nl/myndocs/oauth2/javalin/Oauth2Server.kt +++ b/oauth2-server-javalin/src/main/java/nl/myndocs/oauth2/javalin/Oauth2Server.kt @@ -26,7 +26,7 @@ fun Javalin.enableOauthServer(configurationCallback: ConfigurationBuilder.Config } } - path(callRouter.userInfoEndpoint) { + path(callRouter.tokenInfoEndpoint) { get { ctx -> val javalinCallContext = JavalinCallContext(ctx) callRouter.route(javalinCallContext, configuration.authorizerFactory(javalinCallContext)) diff --git a/oauth2-server-sparkjava/src/main/java/nl/myndocs/oauth2/sparkjava/Oauth2Server.kt b/oauth2-server-sparkjava/src/main/java/nl/myndocs/oauth2/sparkjava/Oauth2Server.kt index e8606c0..9f89796 100644 --- a/oauth2-server-sparkjava/src/main/java/nl/myndocs/oauth2/sparkjava/Oauth2Server.kt +++ b/oauth2-server-sparkjava/src/main/java/nl/myndocs/oauth2/sparkjava/Oauth2Server.kt @@ -26,7 +26,7 @@ object Oauth2Server { res.body() } - get(callRouter.userInfoEndpoint) { req, res -> + get(callRouter.tokenInfoEndpoint) { req, res -> val sparkjavaCallContext = SparkjavaCallContext(req, res) callRouter.route(sparkjavaCallContext, configuration.authorizerFactory(sparkjavaCallContext)) From 6bae16672fd35d95bf31ac932ab2b829b7b03afb Mon Sep 17 00:00:00 2001 From: Albert Date: Thu, 15 Nov 2018 21:13:57 +0100 Subject: [PATCH 9/9] Upgrade to 0.3.0 --- README.md | 2 +- oauth2-server-client-inmemory/pom.xml | 2 +- oauth2-server-core/pom.xml | 2 +- oauth2-server-http4k/pom.xml | 2 +- oauth2-server-identity-inmemory/pom.xml | 2 +- oauth2-server-javalin/pom.xml | 2 +- oauth2-server-json/pom.xml | 2 +- oauth2-server-ktor/pom.xml | 2 +- oauth2-server-sparkjava/pom.xml | 2 +- oauth2-server-token-store-inmemory/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 27918d2..2cef4ca 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It encourages to adapt to existing implementations instead the other way around. First define the version to be used and set it as a property ```xml - 0.2.2 + 0.3.0 ``` diff --git a/oauth2-server-client-inmemory/pom.xml b/oauth2-server-client-inmemory/pom.xml index 3d528dc..cbd17df 100644 --- a/oauth2-server-client-inmemory/pom.xml +++ b/oauth2-server-client-inmemory/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-core/pom.xml b/oauth2-server-core/pom.xml index 39db353..9775ce2 100644 --- a/oauth2-server-core/pom.xml +++ b/oauth2-server-core/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-http4k/pom.xml b/oauth2-server-http4k/pom.xml index f546dd3..e6f6555 100644 --- a/oauth2-server-http4k/pom.xml +++ b/oauth2-server-http4k/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-identity-inmemory/pom.xml b/oauth2-server-identity-inmemory/pom.xml index b27370c..6c01c87 100644 --- a/oauth2-server-identity-inmemory/pom.xml +++ b/oauth2-server-identity-inmemory/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-javalin/pom.xml b/oauth2-server-javalin/pom.xml index 1dd27a0..fccb1d2 100644 --- a/oauth2-server-javalin/pom.xml +++ b/oauth2-server-javalin/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-json/pom.xml b/oauth2-server-json/pom.xml index cd96b98..7990d5c 100644 --- a/oauth2-server-json/pom.xml +++ b/oauth2-server-json/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-ktor/pom.xml b/oauth2-server-ktor/pom.xml index 2a34929..4b92824 100644 --- a/oauth2-server-ktor/pom.xml +++ b/oauth2-server-ktor/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-sparkjava/pom.xml b/oauth2-server-sparkjava/pom.xml index 0746433..04dcc95 100644 --- a/oauth2-server-sparkjava/pom.xml +++ b/oauth2-server-sparkjava/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/oauth2-server-token-store-inmemory/pom.xml b/oauth2-server-token-store-inmemory/pom.xml index 08bc0f3..954b00f 100644 --- a/oauth2-server-token-store-inmemory/pom.xml +++ b/oauth2-server-token-store-inmemory/pom.xml @@ -5,7 +5,7 @@ kotlin-oauth2-server nl.myndocs - 0.2.2 + 0.3.0 4.0.0 diff --git a/pom.xml b/pom.xml index e934fe1..91c21f3 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ nl.myndocs kotlin-oauth2-server pom - 0.2.2 + 0.3.0 1.3.0