Skip to content

Commit 84c29b6

Browse files
authored
Merge pull request #37 from myndocs/release/0.3.0
Release/0.3.0
2 parents 6681d96 + 6bae166 commit 84c29b6

File tree

32 files changed

+220
-62
lines changed

32 files changed

+220
-62
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ It encourages to adapt to existing implementations instead the other way around.
1111
First define the version to be used and set it as a property
1212
```xml
1313
<properties>
14-
<myndocs.oauth.version>0.2.2</myndocs.oauth.version>
14+
<myndocs.oauth.version>0.3.0</myndocs.oauth.version>
1515
</properties>
1616
```
1717

oauth2-server-client-inmemory/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<artifactId>kotlin-oauth2-server</artifactId>
77
<groupId>nl.myndocs</groupId>
8-
<version>0.2.2</version>
8+
<version>0.3.0</version>
99
</parent>
1010
<modelVersion>4.0.0</modelVersion>
1111

oauth2-server-core/pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<artifactId>kotlin-oauth2-server</artifactId>
77
<groupId>nl.myndocs</groupId>
8-
<version>0.2.2</version>
8+
<version>0.3.0</version>
99
</parent>
1010
<modelVersion>4.0.0</modelVersion>
1111

@@ -21,7 +21,7 @@
2121
<dependency>
2222
<groupId>io.mockk</groupId>
2323
<artifactId>mockk</artifactId>
24-
<version>1.8.6</version>
24+
<version>1.8.12.kotlin13</version>
2525
<scope>test</scope>
2626
</dependency>
2727
<dependency>

oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt

+23-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
package nl.myndocs.oauth2
22

33
import nl.myndocs.oauth2.authenticator.Authorizer
4+
import nl.myndocs.oauth2.client.AuthorizedGrantType.AUTHORIZATION_CODE
5+
import nl.myndocs.oauth2.client.AuthorizedGrantType.CLIENT_CREDENTIALS
6+
import nl.myndocs.oauth2.client.AuthorizedGrantType.PASSWORD
7+
import nl.myndocs.oauth2.client.AuthorizedGrantType.REFRESH_TOKEN
48
import nl.myndocs.oauth2.exception.*
5-
import nl.myndocs.oauth2.identity.UserInfo
9+
import nl.myndocs.oauth2.identity.TokenInfo
610
import nl.myndocs.oauth2.request.*
711
import nl.myndocs.oauth2.token.toMap
812

913
class CallRouter(
1014
private val tokenService: TokenService,
1115
val tokenEndpoint: String,
1216
val authorizeEndpoint: String,
13-
val userInfoEndpoint: String,
14-
private val userInfoCallback: (UserInfo) -> Map<String, Any?>
17+
val tokenInfoEndpoint: String,
18+
private val tokenInfoCallback: (TokenInfo) -> Map<String, Any?>
1519
) {
1620
companion object {
1721
const val METHOD_POST = "post"
@@ -28,7 +32,7 @@ class CallRouter(
2832
when (callContext.path) {
2933
tokenEndpoint -> routeTokenEndpoint(callContext)
3034
authorizeEndpoint -> routeAuthorizeEndpoint(callContext, authorizer)
31-
userInfoEndpoint -> routeUserInfoEndpoint(callContext)
35+
tokenInfoEndpoint -> routeTokenInfoEndpoint(callContext)
3236
}
3337
}
3438

@@ -38,7 +42,7 @@ class CallRouter(
3842
}
3943

4044
try {
41-
val allowedGrantTypes = setOf("password", "authorization_code", "refresh_token")
45+
val allowedGrantTypes = setOf(PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, CLIENT_CREDENTIALS)
4246
val grantType = callContext.formParameters["grant_type"]
4347
?: throw InvalidRequestException("'grant_type' not given")
4448

@@ -50,6 +54,7 @@ class CallRouter(
5054
"password" -> routePasswordGrant(callContext, tokenService)
5155
"authorization_code" -> routeAuthorizationCodeGrant(callContext, tokenService)
5256
"refresh_token" -> routeRefreshTokenGrant(callContext, tokenService)
57+
"client_credentials" -> routeClientCredentialsGrant(callContext, tokenService)
5358
}
5459
} catch (oauthException: OauthException) {
5560
callContext.respondStatus(STATUS_BAD_REQUEST)
@@ -71,6 +76,16 @@ class CallRouter(
7176
callContext.respondJson(tokenResponse.toMap())
7277
}
7378

79+
fun routeClientCredentialsGrant(callContext: CallContext, tokenService: TokenService) {
80+
val tokenResponse = tokenService.authorize(ClientCredentialsRequest(
81+
callContext.formParameters["client_id"],
82+
callContext.formParameters["client_secret"],
83+
callContext.formParameters["scope"]
84+
))
85+
86+
callContext.respondJson(tokenResponse.toMap())
87+
}
88+
7489
fun routeRefreshTokenGrant(callContext: CallContext, tokenService: TokenService) {
7590
val accessToken = tokenService.refresh(
7691
RefreshTokenRequest(
@@ -193,7 +208,7 @@ class CallRouter(
193208
}
194209
}
195210

196-
private fun routeUserInfoEndpoint(callContext: CallContext) {
211+
private fun routeTokenInfoEndpoint(callContext: CallContext) {
197212
if (callContext.method.toLowerCase() != METHOD_GET) {
198213
return
199214
}
@@ -207,8 +222,8 @@ class CallRouter(
207222

208223
val token = authorization.substring(7)
209224

210-
val userInfoCallback = userInfoCallback(tokenService.userInfo(token))
225+
val tokenInfoCallback = tokenInfoCallback(tokenService.tokenInfo(token))
211226

212-
callContext.respondJson(userInfoCallback)
227+
callContext.respondJson(tokenInfoCallback)
213228
}
214229
}

oauth2-server-core/src/main/java/nl/myndocs/oauth2/Oauth2TokenService.kt

+30-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import nl.myndocs.oauth2.client.ClientService
88
import nl.myndocs.oauth2.exception.*
99
import nl.myndocs.oauth2.identity.Identity
1010
import nl.myndocs.oauth2.identity.IdentityService
11-
import nl.myndocs.oauth2.identity.UserInfo
11+
import nl.myndocs.oauth2.identity.TokenInfo
1212
import nl.myndocs.oauth2.request.*
1313
import nl.myndocs.oauth2.response.TokenResponse
1414
import nl.myndocs.oauth2.scope.ScopeParser
@@ -119,6 +119,31 @@ class Oauth2TokenService(
119119
return accessToken.toTokenResponse()
120120
}
121121

122+
override fun authorize(clientCredentialsRequest: ClientCredentialsRequest): TokenResponse {
123+
throwExceptionIfUnverifiedClient(clientCredentialsRequest)
124+
125+
val requestedClient = clientService.clientOf(clientCredentialsRequest.clientId!!) ?: throw InvalidClientException()
126+
127+
val scopes = clientCredentialsRequest.scope
128+
?.let { ScopeParser.parseScopes(it).toSet() }
129+
?: requestedClient.clientScopes
130+
131+
val accessToken = accessTokenConverter.convertToToken(
132+
username = null,
133+
clientId = clientCredentialsRequest.clientId,
134+
requestedScopes = scopes,
135+
refreshToken = refreshTokenConverter.convertToToken(
136+
username = null,
137+
clientId = clientCredentialsRequest.clientId,
138+
requestedScopes = scopes
139+
)
140+
)
141+
142+
tokenStore.storeAccessToken(accessToken)
143+
144+
return accessToken.toTokenResponse()
145+
}
146+
122147
override fun refresh(refreshTokenRequest: RefreshTokenRequest): TokenResponse {
123148
throwExceptionIfUnverifiedClient(refreshTokenRequest)
124149

@@ -143,7 +168,7 @@ class Oauth2TokenService(
143168
refreshToken.username,
144169
refreshToken.clientId,
145170
refreshToken.scopes,
146-
refreshToken
171+
refreshTokenConverter.convertToToken(refreshToken)
147172
)
148173

149174
tokenStore.storeAccessToken(accessToken)
@@ -290,13 +315,12 @@ class Oauth2TokenService(
290315
}
291316
}
292317

293-
override fun userInfo(accessToken: String): UserInfo {
318+
override fun tokenInfo(accessToken: String): TokenInfo {
294319
val storedAccessToken = tokenStore.accessToken(accessToken) ?: throw InvalidGrantException()
295320
val client = clientService.clientOf(storedAccessToken.clientId) ?: throw InvalidClientException()
296-
val identity = identityService.identityOf(client, storedAccessToken.username)
297-
?: throw InvalidIdentityException()
321+
val identity = storedAccessToken.username?.let { identityService.identityOf(client, it) }
298322

299-
return UserInfo(
323+
return TokenInfo(
300324
identity,
301325
client,
302326
storedAccessToken.scopes

oauth2-server-core/src/main/java/nl/myndocs/oauth2/TokenService.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package nl.myndocs.oauth2
22

33
import nl.myndocs.oauth2.authenticator.Authenticator
44
import nl.myndocs.oauth2.authenticator.IdentityScopeVerifier
5-
import nl.myndocs.oauth2.identity.UserInfo
5+
import nl.myndocs.oauth2.identity.TokenInfo
66
import nl.myndocs.oauth2.request.*
77
import nl.myndocs.oauth2.response.TokenResponse
88
import nl.myndocs.oauth2.token.AccessToken
@@ -13,6 +13,8 @@ interface TokenService {
1313

1414
fun authorize(authorizationCodeRequest: AuthorizationCodeRequest): TokenResponse
1515

16+
fun authorize(clientCredentialsRequest: ClientCredentialsRequest): TokenResponse
17+
1618
fun refresh(refreshTokenRequest: RefreshTokenRequest): TokenResponse
1719

1820
fun redirect(
@@ -27,5 +29,5 @@ interface TokenService {
2729
identityScopeVerifier: IdentityScopeVerifier?
2830
): AccessToken
2931

30-
fun userInfo(accessToken: String): UserInfo
32+
fun tokenInfo(accessToken: String): TokenInfo
3133
}

oauth2-server-core/src/main/java/nl/myndocs/oauth2/client/AuthorizedGrantType.kt

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ object AuthorizedGrantType {
55
const val REFRESH_TOKEN = "refresh_token"
66
const val PASSWORD = "password"
77
const val AUTHORIZATION_CODE = "authorization_code"
8+
const val CLIENT_CREDENTIALS = "client_credentials"
89
}

oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/CallRouterBuilder.kt

+8-8
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ package nl.myndocs.oauth2.config
22

33
import nl.myndocs.oauth2.CallRouter
44
import nl.myndocs.oauth2.TokenService
5-
import nl.myndocs.oauth2.identity.UserInfo
5+
import nl.myndocs.oauth2.identity.TokenInfo
66

77
internal object CallRouterBuilder {
88
class Configuration {
99
var tokenEndpoint: String = "/oauth/token"
1010
var authorizeEndpoint: String = "/oauth/authorize"
11-
var userInfoEndpoint: String = "/oauth/userinfo"
12-
var userInfoCallback: (UserInfo) -> Map<String, Any?> = { userInfo ->
11+
var tokenInfoEndpoint: String = "/oauth/tokeninfo"
12+
var tokenInfoCallback: (TokenInfo) -> Map<String, Any?> = { tokenInfo ->
1313
mapOf(
14-
"username" to userInfo.identity.username,
15-
"scopes" to userInfo.scopes
16-
)
14+
"username" to tokenInfo.identity?.username,
15+
"scopes" to tokenInfo.scopes
16+
).filterValues { it != null }
1717
}
1818
var tokenService: TokenService? = null
1919
}
@@ -29,7 +29,7 @@ internal object CallRouterBuilder {
2929
configuration.tokenService!!,
3030
configuration.tokenEndpoint,
3131
configuration.authorizeEndpoint,
32-
configuration.userInfoEndpoint,
33-
configuration.userInfoCallback
32+
configuration.tokenInfoEndpoint,
33+
configuration.tokenInfoCallback
3434
)
3535
}

oauth2-server-core/src/main/java/nl/myndocs/oauth2/config/ConfigurationBuilder.kt

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package nl.myndocs.oauth2.config
22

33
import nl.myndocs.oauth2.TokenService
44
import nl.myndocs.oauth2.authenticator.Authorizer
5-
import nl.myndocs.oauth2.identity.UserInfo
5+
import nl.myndocs.oauth2.identity.TokenInfo
66
import nl.myndocs.oauth2.request.CallContext
77
import nl.myndocs.oauth2.request.auth.BasicAuthorizer
88

@@ -28,16 +28,16 @@ object ConfigurationBuilder {
2828
callRouterConfiguration.tokenEndpoint = value
2929
}
3030

31-
var userInfoEndpoint: String
32-
get() = callRouterConfiguration.userInfoEndpoint
31+
var tokenInfoEndpoint: String
32+
get() = callRouterConfiguration.tokenInfoEndpoint
3333
set(value) {
34-
callRouterConfiguration.userInfoEndpoint = value
34+
callRouterConfiguration.tokenInfoEndpoint = value
3535
}
3636

37-
var userInfoCallback: (UserInfo) -> Map<String, Any?>
38-
get() = callRouterConfiguration.userInfoCallback
37+
var tokenInfoCallback: (TokenInfo) -> Map<String, Any?>
38+
get() = callRouterConfiguration.tokenInfoCallback
3939
set(value) {
40-
callRouterConfiguration.userInfoCallback = value
40+
callRouterConfiguration.tokenInfoCallback = value
4141
}
4242

4343
var authorizerFactory: (CallContext) -> Authorizer = ::BasicAuthorizer

oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/UserInfo.kt renamed to oauth2-server-core/src/main/java/nl/myndocs/oauth2/identity/TokenInfo.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package nl.myndocs.oauth2.identity
22

33
import nl.myndocs.oauth2.client.Client
44

5-
data class UserInfo(
6-
val identity: Identity,
5+
data class TokenInfo(
6+
val identity: Identity?,
77
val client: Client,
88
val scopes: Set<String>
99
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package nl.myndocs.oauth2.request
2+
3+
data class ClientCredentialsRequest(
4+
override val clientId: String?,
5+
override val clientSecret: String?,
6+
val scope: String?
7+
) : ClientRequest{
8+
val grant_type = "client_credentials"
9+
}

oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/AccessToken.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ data class AccessToken(
66
val accessToken: String,
77
val tokenType: String,
88
override val expireTime: Instant,
9-
val username: String,
9+
val username: String?,
1010
val clientId: String,
1111
val scopes: Set<String>,
1212
val refreshToken: RefreshToken?

oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/RefreshToken.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import java.time.Instant
55
data class RefreshToken(
66
val refreshToken: String,
77
override val expireTime: Instant,
8-
val username: String,
8+
val username: String?,
99
val clientId: String,
1010
val scopes: Set<String>
1111
) : ExpirableToken

oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/AccessTokenConverter.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ import nl.myndocs.oauth2.token.AccessToken
44
import nl.myndocs.oauth2.token.RefreshToken
55

66
interface AccessTokenConverter {
7-
fun convertToToken(username: String, clientId: String, requestedScopes: Set<String>, refreshToken: RefreshToken?): AccessToken
7+
fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>, refreshToken: RefreshToken?): AccessToken
88
}

oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/RefreshTokenConverter.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ package nl.myndocs.oauth2.token.converter
33
import nl.myndocs.oauth2.token.RefreshToken
44

55
interface RefreshTokenConverter {
6-
fun convertToToken(username: String, clientId: String, requestedScopes: Set<String>): RefreshToken
6+
fun convertToToken(refreshToken: RefreshToken): RefreshToken = convertToToken(refreshToken.username, refreshToken.clientId, refreshToken.scopes)
7+
8+
fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>): RefreshToken
79
}

oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDAccessTokenConverter.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class UUIDAccessTokenConverter(
99
private val accessTokenExpireInSeconds: Int = 3600
1010
) : AccessTokenConverter {
1111

12-
override fun convertToToken(username: String, clientId: String, requestedScopes: Set<String>, refreshToken: RefreshToken?): AccessToken {
12+
override fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>, refreshToken: RefreshToken?): AccessToken {
1313
return AccessToken(
1414
UUID.randomUUID().toString(),
1515
"bearer",

oauth2-server-core/src/main/java/nl/myndocs/oauth2/token/converter/UUIDRefreshTokenConverter.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import java.util.*
77
class UUIDRefreshTokenConverter(
88
private val refreshTokenExpireInSeconds: Int = 86400
99
) : RefreshTokenConverter {
10-
override fun convertToToken(username: String, clientId: String, requestedScopes: Set<String>): RefreshToken {
10+
override fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>): RefreshToken {
1111
return RefreshToken(
1212
UUID.randomUUID().toString(),
1313
Instant.now().plusSeconds(refreshTokenExpireInSeconds.toLong()),

0 commit comments

Comments
 (0)