Skip to content

Commit

Permalink
Merge pull request #37 from myndocs/release/0.3.0
Browse files Browse the repository at this point in the history
Release/0.3.0
  • Loading branch information
adhesivee authored Nov 15, 2018
2 parents 6681d96 + 6bae166 commit 84c29b6
Show file tree
Hide file tree
Showing 32 changed files with 220 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<properties>
<myndocs.oauth.version>0.2.2</myndocs.oauth.version>
<myndocs.oauth.version>0.3.0</myndocs.oauth.version>
</properties>
```

Expand Down
2 changes: 1 addition & 1 deletion oauth2-server-client-inmemory/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>kotlin-oauth2-server</artifactId>
<groupId>nl.myndocs</groupId>
<version>0.2.2</version>
<version>0.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
4 changes: 2 additions & 2 deletions oauth2-server-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>kotlin-oauth2-server</artifactId>
<groupId>nl.myndocs</groupId>
<version>0.2.2</version>
<version>0.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand All @@ -21,7 +21,7 @@
<dependency>
<groupId>io.mockk</groupId>
<artifactId>mockk</artifactId>
<version>1.8.6</version>
<version>1.8.12.kotlin13</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
31 changes: 23 additions & 8 deletions oauth2-server-core/src/main/java/nl/myndocs/oauth2/CallRouter.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
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.identity.TokenInfo
import nl.myndocs.oauth2.request.*
import nl.myndocs.oauth2.token.toMap

class CallRouter(
private val tokenService: TokenService,
val tokenEndpoint: String,
val authorizeEndpoint: String,
val userInfoEndpoint: String,
private val userInfoCallback: (UserInfo) -> Map<String, Any?>
val tokenInfoEndpoint: String,
private val tokenInfoCallback: (TokenInfo) -> Map<String, Any?>
) {
companion object {
const val METHOD_POST = "post"
Expand All @@ -28,7 +32,7 @@ class CallRouter(
when (callContext.path) {
tokenEndpoint -> routeTokenEndpoint(callContext)
authorizeEndpoint -> routeAuthorizeEndpoint(callContext, authorizer)
userInfoEndpoint -> routeUserInfoEndpoint(callContext)
tokenInfoEndpoint -> routeTokenInfoEndpoint(callContext)
}
}

Expand All @@ -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")

Expand All @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -193,7 +208,7 @@ class CallRouter(
}
}

private fun routeUserInfoEndpoint(callContext: CallContext) {
private fun routeTokenInfoEndpoint(callContext: CallContext) {
if (callContext.method.toLowerCase() != METHOD_GET) {
return
}
Expand All @@ -207,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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -143,7 +168,7 @@ class Oauth2TokenService(
refreshToken.username,
refreshToken.clientId,
refreshToken.scopes,
refreshToken
refreshTokenConverter.convertToToken(refreshToken)
)

tokenStore.storeAccessToken(accessToken)
Expand Down Expand Up @@ -290,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 = identityService.identityOf(client, storedAccessToken.username)
?: throw InvalidIdentityException()
val identity = storedAccessToken.username?.let { identityService.identityOf(client, it) }

return UserInfo(
return TokenInfo(
identity,
client,
storedAccessToken.scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,6 +13,8 @@ interface TokenService {

fun authorize(authorizationCodeRequest: AuthorizationCodeRequest): TokenResponse

fun authorize(clientCredentialsRequest: ClientCredentialsRequest): TokenResponse

fun refresh(refreshTokenRequest: RefreshTokenRequest): TokenResponse

fun redirect(
Expand All @@ -27,5 +29,5 @@ interface TokenService {
identityScopeVerifier: IdentityScopeVerifier?
): AccessToken

fun userInfo(accessToken: String): UserInfo
fun tokenInfo(accessToken: String): TokenInfo
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Any?> = { userInfo ->
var tokenInfoEndpoint: String = "/oauth/tokeninfo"
var tokenInfoCallback: (TokenInfo) -> Map<String, Any?> = { 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
}
Expand All @@ -29,7 +29,7 @@ internal object CallRouterBuilder {
configuration.tokenService!!,
configuration.tokenEndpoint,
configuration.authorizeEndpoint,
configuration.userInfoEndpoint,
configuration.userInfoCallback
configuration.tokenInfoEndpoint,
configuration.tokenInfoCallback
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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<String, Any?>
get() = callRouterConfiguration.userInfoCallback
var tokenInfoCallback: (TokenInfo) -> Map<String, Any?>
get() = callRouterConfiguration.tokenInfoCallback
set(value) {
callRouterConfiguration.userInfoCallback = value
callRouterConfiguration.tokenInfoCallback = value
}

var authorizerFactory: (CallContext) -> Authorizer = ::BasicAuthorizer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>
)
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
val refreshToken: RefreshToken?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>
) : ExpirableToken
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>, refreshToken: RefreshToken?): AccessToken
fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>, refreshToken: RefreshToken?): AccessToken
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ package nl.myndocs.oauth2.token.converter
import nl.myndocs.oauth2.token.RefreshToken

interface RefreshTokenConverter {
fun convertToToken(username: String, clientId: String, requestedScopes: Set<String>): RefreshToken
fun convertToToken(refreshToken: RefreshToken): RefreshToken = convertToToken(refreshToken.username, refreshToken.clientId, refreshToken.scopes)

fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>): RefreshToken
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class UUIDAccessTokenConverter(
private val accessTokenExpireInSeconds: Int = 3600
) : AccessTokenConverter {

override fun convertToToken(username: String, clientId: String, requestedScopes: Set<String>, refreshToken: RefreshToken?): AccessToken {
override fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>, refreshToken: RefreshToken?): AccessToken {
return AccessToken(
UUID.randomUUID().toString(),
"bearer",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import java.util.*
class UUIDRefreshTokenConverter(
private val refreshTokenExpireInSeconds: Int = 86400
) : RefreshTokenConverter {
override fun convertToToken(username: String, clientId: String, requestedScopes: Set<String>): RefreshToken {
override fun convertToToken(username: String?, clientId: String, requestedScopes: Set<String>): RefreshToken {
return RefreshToken(
UUID.randomUUID().toString(),
Instant.now().plusSeconds(refreshTokenExpireInSeconds.toLong()),
Expand Down
Loading

0 comments on commit 84c29b6

Please sign in to comment.