Skip to content

Commit c0a859c

Browse files
authored
Grant extension (#45)
* Allow grant extension through extendable GrantingCall * Fix infinite loop * Granters should be configurable * Immutable granter map
1 parent ad84914 commit c0a859c

File tree

7 files changed

+114
-67
lines changed

7 files changed

+114
-67
lines changed

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

Lines changed: 25 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
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
84
import nl.myndocs.oauth2.exception.*
5+
import nl.myndocs.oauth2.grant.Granter
6+
import nl.myndocs.oauth2.grant.GrantingCall
97
import nl.myndocs.oauth2.identity.TokenInfo
10-
import nl.myndocs.oauth2.request.*
11-
import nl.myndocs.oauth2.token.toMap
8+
import nl.myndocs.oauth2.request.CallContext
9+
import nl.myndocs.oauth2.request.RedirectAuthorizationCodeRequest
10+
import nl.myndocs.oauth2.request.RedirectTokenRequest
11+
import nl.myndocs.oauth2.request.headerCaseInsensitive
1212

1313
class CallRouter(
1414
private val tokenService: TokenService,
1515
val tokenEndpoint: String,
1616
val authorizeEndpoint: String,
1717
val tokenInfoEndpoint: String,
18-
private val tokenInfoCallback: (TokenInfo) -> Map<String, Any?>
18+
private val tokenInfoCallback: (TokenInfo) -> Map<String, Any?>,
19+
private val granters: List<GrantingCall.() -> Granter>
1920
) {
2021
companion object {
2122
const val METHOD_POST = "post"
@@ -42,76 +43,36 @@ class CallRouter(
4243
}
4344

4445
try {
45-
val allowedGrantTypes = setOf(PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, CLIENT_CREDENTIALS)
4646
val grantType = callContext.formParameters["grant_type"]
4747
?: throw InvalidRequestException("'grant_type' not given")
4848

49+
val grantingCall = object: GrantingCall {
50+
override val callContext: CallContext
51+
get() = callContext
52+
53+
override val tokenService = this@CallRouter.tokenService
54+
}
55+
56+
val granterMap = granters
57+
.map {
58+
val granter = grantingCall.it()
59+
granter.grantType to granter
60+
}
61+
.toMap()
62+
63+
val allowedGrantTypes = granterMap.keys
64+
4965
if (!allowedGrantTypes.contains(grantType)) {
5066
throw InvalidGrantException("'grant_type' with value '$grantType' not allowed")
5167
}
5268

53-
when (grantType) {
54-
"password" -> routePasswordGrant(callContext, tokenService)
55-
"authorization_code" -> routeAuthorizationCodeGrant(callContext, tokenService)
56-
"refresh_token" -> routeRefreshTokenGrant(callContext, tokenService)
57-
"client_credentials" -> routeClientCredentialsGrant(callContext, tokenService)
58-
}
69+
granterMap[grantType]!!.callback.invoke()
5970
} catch (oauthException: OauthException) {
6071
callContext.respondStatus(STATUS_BAD_REQUEST)
6172
callContext.respondJson(oauthException.toMap())
6273
}
6374
}
6475

65-
fun routePasswordGrant(callContext: CallContext, tokenService: TokenService) {
66-
val tokenResponse = tokenService.authorize(
67-
PasswordGrantRequest(
68-
callContext.formParameters["client_id"],
69-
callContext.formParameters["client_secret"],
70-
callContext.formParameters["username"],
71-
callContext.formParameters["password"],
72-
callContext.formParameters["scope"]
73-
)
74-
)
75-
76-
callContext.respondJson(tokenResponse.toMap())
77-
}
78-
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-
89-
fun routeRefreshTokenGrant(callContext: CallContext, tokenService: TokenService) {
90-
val accessToken = tokenService.refresh(
91-
RefreshTokenRequest(
92-
callContext.formParameters["client_id"],
93-
callContext.formParameters["client_secret"],
94-
callContext.formParameters["refresh_token"]
95-
)
96-
)
97-
98-
callContext.respondJson(accessToken.toMap())
99-
}
100-
101-
fun routeAuthorizationCodeGrant(callContext: CallContext, tokenService: TokenService) {
102-
val accessToken = tokenService.authorize(
103-
AuthorizationCodeRequest(
104-
callContext.formParameters["client_id"],
105-
callContext.formParameters["client_secret"],
106-
callContext.formParameters["code"],
107-
callContext.formParameters["redirect_uri"]
108-
)
109-
)
110-
111-
callContext.respondJson(accessToken.toMap())
112-
}
113-
114-
11576
fun routeAuthorizationCodeRedirect(
11677
callContext: CallContext,
11778
tokenService: TokenService,

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package nl.myndocs.oauth2.config
22

3-
import nl.myndocs.oauth2.CallRouter
4-
import nl.myndocs.oauth2.TokenService
3+
import nl.myndocs.oauth2.*
4+
import nl.myndocs.oauth2.grant.*
55
import nl.myndocs.oauth2.identity.TokenInfo
66

77
internal object CallRouterBuilder {
@@ -16,6 +16,7 @@ internal object CallRouterBuilder {
1616
).filterValues { it != null }
1717
}
1818
var tokenService: TokenService? = null
19+
var granters: List<GrantingCall.() -> Granter> = listOf()
1920
}
2021

2122
fun build(configurer: Configuration.() -> Unit): CallRouter {
@@ -30,6 +31,12 @@ internal object CallRouterBuilder {
3031
configuration.tokenEndpoint,
3132
configuration.authorizeEndpoint,
3233
configuration.tokenInfoEndpoint,
33-
configuration.tokenInfoCallback
34+
configuration.tokenInfoCallback,
35+
listOf<GrantingCall.() -> Granter>(
36+
{ grantPassword() },
37+
{ grantAuthorizationCode() },
38+
{ grantClientCredentials() },
39+
{ grantRefreshToken() }
40+
) + configuration.granters
3441
)
3542
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package nl.myndocs.oauth2.config
22

33
import nl.myndocs.oauth2.TokenService
44
import nl.myndocs.oauth2.authenticator.Authorizer
5+
import nl.myndocs.oauth2.grant.Granter
6+
import nl.myndocs.oauth2.grant.GrantingCall
57
import nl.myndocs.oauth2.identity.TokenInfo
68
import nl.myndocs.oauth2.request.CallContext
79
import nl.myndocs.oauth2.request.auth.BasicAuthorizer
@@ -40,6 +42,12 @@ object ConfigurationBuilder {
4042
callRouterConfiguration.tokenInfoCallback = value
4143
}
4244

45+
var granters: List<GrantingCall.() -> Granter>
46+
get() = callRouterConfiguration.granters
47+
set(value) {
48+
callRouterConfiguration.granters = value
49+
}
50+
4351
var authorizerFactory: (CallContext) -> Authorizer = ::BasicAuthorizer
4452
}
4553

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package nl.myndocs.oauth2.grant
2+
3+
import nl.myndocs.oauth2.request.*
4+
import nl.myndocs.oauth2.token.toMap
5+
6+
fun GrantingCall.grantPassword() = granter("password") {
7+
val tokenResponse = tokenService.authorize(
8+
PasswordGrantRequest(
9+
callContext.formParameters["client_id"],
10+
callContext.formParameters["client_secret"],
11+
callContext.formParameters["username"],
12+
callContext.formParameters["password"],
13+
callContext.formParameters["scope"]
14+
)
15+
)
16+
17+
callContext.respondJson(tokenResponse.toMap())
18+
}
19+
20+
fun GrantingCall.grantClientCredentials() = granter("client_credentials") {
21+
val tokenResponse = tokenService.authorize(ClientCredentialsRequest(
22+
callContext.formParameters["client_id"],
23+
callContext.formParameters["client_secret"],
24+
callContext.formParameters["scope"]
25+
))
26+
27+
callContext.respondJson(tokenResponse.toMap())
28+
}
29+
30+
fun GrantingCall.grantRefreshToken() = granter("refresh_token") {
31+
val accessToken = tokenService.refresh(
32+
RefreshTokenRequest(
33+
callContext.formParameters["client_id"],
34+
callContext.formParameters["client_secret"],
35+
callContext.formParameters["refresh_token"]
36+
)
37+
)
38+
39+
callContext.respondJson(accessToken.toMap())
40+
}
41+
42+
fun GrantingCall.grantAuthorizationCode() = granter("authorization_code") {
43+
val accessToken = tokenService.authorize(
44+
AuthorizationCodeRequest(
45+
callContext.formParameters["client_id"],
46+
callContext.formParameters["client_secret"],
47+
callContext.formParameters["code"],
48+
callContext.formParameters["redirect_uri"]
49+
)
50+
)
51+
52+
callContext.respondJson(accessToken.toMap())
53+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package nl.myndocs.oauth2.grant
2+
3+
fun granter(grantType: String, callback: () -> Unit) = Granter(grantType, callback)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package nl.myndocs.oauth2.grant
2+
3+
class Granter(
4+
val grantType: String,
5+
val callback: () -> Unit
6+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package nl.myndocs.oauth2.grant
2+
3+
import nl.myndocs.oauth2.TokenService
4+
import nl.myndocs.oauth2.request.CallContext
5+
6+
interface GrantingCall {
7+
val callContext: CallContext
8+
val tokenService: TokenService
9+
}

0 commit comments

Comments
 (0)