Skip to content

Commit d5a4733

Browse files
committed
Merge pull request #76 from tsuyoshizawa/improve-authorization-handler
Improve authorization handler
2 parents 975183e + 52fafd1 commit d5a4733

20 files changed

+273
-288
lines changed

play2-oauth2-provider/src/main/scala/scalaoauth2/provider/OAuth2Provider.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@ trait OAuth2BaseProvider extends Results {
1616
val tokenEndpoint: TokenEndpoint = TokenEndpoint
1717

1818
implicit def play2oauthRequest(request: RequestHeader): AuthorizationRequest = {
19-
AuthorizationRequest(request.headers.toMap, request.queryString)
19+
new AuthorizationRequest(request.headers.toMap, request.queryString)
2020
}
2121

2222
implicit def play2oauthRequest[A](request: Request[A]): AuthorizationRequest = {
2323
val param: Map[String, Seq[String]] = getParam(request)
24-
AuthorizationRequest(request.headers.toMap, param)
24+
new AuthorizationRequest(request.headers.toMap, param)
2525
}
2626

2727
implicit def play2protectedResourceRequest(request: RequestHeader): ProtectedResourceRequest = {
28-
ProtectedResourceRequest(request.headers.toMap, request.queryString)
28+
new ProtectedResourceRequest(request.headers.toMap, request.queryString)
2929
}
3030

3131
implicit def play2protectedResourceRequest[A](request: Request[A]): ProtectedResourceRequest = {
3232
val param: Map[String, Seq[String]] = getParam(request)
33-
ProtectedResourceRequest(request.headers.toMap, param)
33+
new ProtectedResourceRequest(request.headers.toMap, param)
3434
}
3535

3636
private[provider] def getParam[A](request: Request[A]): Map[String, Seq[String]] = {

scala-oauth2-core/src/main/scala/scalaoauth2/provider/AuthorizationHandler.scala

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import scala.concurrent.Future
99
*
1010
* <h4>Authorization Code Grant</h4>
1111
* <ul>
12-
* <li>validateClient(clientCredential, grantType)</li>
12+
* <li>validateClient(request)</li>
1313
* <li>findAuthInfoByCode(code)</li>
1414
* <li>deleteAuthCode(code)</li>
1515
* <li>getStoredAccessToken(authInfo)</li>
@@ -26,25 +26,25 @@ import scala.concurrent.Future
2626
*
2727
* <h4>Resource Owner Password Credentials Grant</h4>
2828
* <ul>
29-
* <li>validateClient(clientCredential, grantType)</li>
30-
* <li>findUser(username, password)</li>
29+
* <li>validateClient(request)</li>
30+
* <li>findUser(request)</li>
3131
* <li>getStoredAccessToken(authInfo)</li>
3232
* <li>refreshAccessToken(authInfo, token)</li>
3333
* <li>createAccessToken(authInfo)</li>
3434
* </ul>
3535
*
3636
* <h4>Client Credentials Grant</h4>
3737
* <ul>
38-
* <li>validateClient(clientCredential, grantType)</li>
39-
* <li>findClientUser(clientCredential)</li>
38+
* <li>validateClient(request)</li>
39+
* <li>findUser(request)</li>
4040
* <li>getStoredAccessToken(authInfo)</li>
4141
* <li>refreshAccessToken(authInfo, token)</li>
4242
* <li>createAccessToken(authInfo)</li>
4343
* </ul>
4444
*
4545
* <h4>Implicit Grant</h4>
4646
* <ul>
47-
* <li>validateClient(clientCredential, grantType)</li>
47+
* <li>validateClient(request)</li>
4848
* <li>findUser(request)</li>
4949
* <li>getStoredAccessToken(authInfo)</li>
5050
* <li>createAccessToken(authInfo)</li>
@@ -56,25 +56,14 @@ trait AuthorizationHandler[U] {
5656
/**
5757
* Verify proper client with parameters for issue an access token.
5858
*
59-
* @param clientCredential Client sends clientId and clientSecret which are registered by application.
60-
* @param grantType Client sends this value which is registered by application.
59+
* @param request Request sent by client.
6160
* @return true if request is a regular client, false if request is a illegal client.
6261
*/
63-
def validateClient(clientCredential: ClientCredential, grantType: String): Future[Boolean]
64-
65-
/**
66-
* Find userId with username and password these are used on your system.
67-
* If you don't support Resource Owner Password Credentials Grant then doesn't need implementing.
68-
*
69-
* @param username Client sends this value which is used on your system.
70-
* @param password Client sends this value which is used on your system.
71-
* @return Including UserId to Option if could find the user, None if couldn't find.
72-
*/
73-
def findUser(username: String, password: String): Future[Option[U]]
62+
def validateClient(request: AuthorizationRequest): Future[Boolean]
7463

7564
/**
7665
* Authenticate the user that issued the authorization request.
77-
* If you don't support Implicit Grant, then doesn't need implementing.
66+
* Client credential, Password and Implicit Grant call this method.
7867
*
7968
* @param request Request sent by client.
8069
*/
@@ -138,14 +127,4 @@ trait AuthorizationHandler[U] {
138127
*/
139128
def findAuthInfoByRefreshToken(refreshToken: String): Future[Option[AuthInfo[U]]]
140129

141-
/**
142-
* Find user by clientId and clientSecret.
143-
*
144-
* If you don't support Client Credentials Grant then doesn't need implementing.
145-
*
146-
* @param clientCredential Client sends clientId and clientSecret which are registered by application.
147-
* @return Return user that matched both values.
148-
*/
149-
def findClientUser(clientCredential: ClientCredential, scope: Option[String]): Future[Option[U]]
150-
151130
}
Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package scalaoauth2.provider
22

3-
case class AuthorizationRequest(headers: Map[String, Seq[String]], params: Map[String, Seq[String]]) extends RequestBase(headers, params) {
3+
import org.apache.commons.codec.binary.Base64
4+
5+
case class ClientCredential(clientId: String, clientSecret: Option[String])
6+
7+
class AuthorizationRequest(headers: Map[String, Seq[String]], params: Map[String, Seq[String]]) extends RequestBase(headers, params) {
48

59
/**
610
* Returns grant_type.
@@ -12,21 +16,7 @@ case class AuthorizationRequest(headers: Map[String, Seq[String]], params: Map[S
1216
*
1317
* @return grant_type
1418
*/
15-
def grantType: Option[String] = param("grant_type")
16-
17-
/**
18-
* Returns client_id.
19-
*
20-
* @return client_id
21-
*/
22-
def clientId: Option[String] = param("client_id")
23-
24-
/**
25-
* Returns client_secret.
26-
*
27-
* @return client_secret
28-
*/
29-
def clientSecret: Option[String] = param("client_secret")
19+
def grantType: String = requireParam("grant_type")
3020

3121
/**
3222
* Returns scope.
@@ -35,42 +25,84 @@ case class AuthorizationRequest(headers: Map[String, Seq[String]], params: Map[S
3525
*/
3626
def scope: Option[String] = param("scope")
3727

28+
lazy val clientCredential: Option[ClientCredential] = {
29+
header("Authorization").flatMap {
30+
"""^\s*Basic\s+(.+?)\s*$""".r.findFirstMatchIn
31+
} match {
32+
case Some(matcher) =>
33+
val authorization = matcher.group(1)
34+
val decoded = new String(Base64.decodeBase64(authorization.getBytes), "UTF-8")
35+
if (decoded.indexOf(':') > 0) {
36+
decoded.split(":", 2) match {
37+
case Array(clientId, clientSecret) => Some(ClientCredential(clientId, if (clientSecret == "") None else Some(clientSecret)))
38+
case Array(clientId) => Some(ClientCredential(clientId, None))
39+
}
40+
} else {
41+
None
42+
}
43+
case _ => param("client_id").map { clientId =>
44+
ClientCredential(clientId, param("client_secret"))
45+
}
46+
}
47+
}
48+
}
49+
50+
case class RefreshTokenRequest(request: AuthorizationRequest) extends AuthorizationRequest(request.headers, request.params) {
3851
/**
39-
* Returns redirect_uri.
40-
*
41-
* @return redirect_uri
42-
*/
43-
def redirectUri: Option[String] = param("redirect_uri")
52+
* returns refresh_token.
53+
*
54+
* @return code.
55+
* @throws InvalidRequest if the parameter is not found
56+
*/
57+
def refreshToken: String = requireParam("refresh_token")
4458

59+
override lazy val clientCredential = request.clientCredential
60+
}
61+
62+
case class PasswordRequest(request: AuthorizationRequest) extends AuthorizationRequest(request.headers, request.params) {
4563
/**
46-
* returns code.
47-
*
48-
* @return code.
49-
* @throws InvalidRequest If not found code
50-
*/
51-
def requireCode: String = requireParam("code")
64+
* returns username.
65+
*
66+
* @return username.
67+
* @throws InvalidRequest if the parameter is not found
68+
*/
69+
def username = requireParam("username")
5270

5371
/**
54-
* returns username.
55-
*
56-
* @return username.
57-
* @throws InvalidRequest If not found username
58-
*/
59-
def requireUsername: String = requireParam("username")
72+
* returns password.
73+
*
74+
* @return password.
75+
* @throws InvalidRequest if the parameter is not found
76+
*/
77+
def password = requireParam("password")
78+
79+
override lazy val clientCredential = request.clientCredential
80+
}
81+
82+
case class ClientCredentialsRequest(request: AuthorizationRequest) extends AuthorizationRequest(request.headers, request.params) {
83+
override lazy val clientCredential = request.clientCredential
84+
}
6085

86+
case class AuthorizationCodeRequest(request: AuthorizationRequest) extends AuthorizationRequest(request.headers, request.params) {
6187
/**
62-
* returns password.
63-
*
64-
* @return code.
65-
* @throws InvalidRequest If not found password
66-
*/
67-
def requirePassword: String = requireParam("password")
88+
* returns code.
89+
*
90+
* @return code.
91+
* @throws InvalidRequest if code is not found
92+
*/
93+
def code: String = requireParam("code")
6894

6995
/**
70-
* returns refresh_token.
71-
*
72-
* @return code.
73-
* @throws InvalidRequest If not found refresh_token
74-
*/
75-
def requireRefreshToken: String = requireParam("refresh_token")
96+
* Returns redirect_uri.
97+
*
98+
* @return redirect_uri
99+
*/
100+
def redirectUri: Option[String] = param("redirect_uri")
101+
102+
override lazy val clientCredential = request.clientCredential
103+
104+
}
105+
106+
case class ImplicitRequest(request: AuthorizationRequest) extends AuthorizationRequest(request.headers, request.params) {
107+
override lazy val clientCredential = request.clientCredential
76108
}

scala-oauth2-core/src/main/scala/scalaoauth2/provider/ClientCredentialFetcher.scala

Lines changed: 0 additions & 33 deletions
This file was deleted.

scala-oauth2-core/src/main/scala/scalaoauth2/provider/GrantHandler.scala

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ trait GrantHandler {
1313
*/
1414
def clientCredentialRequired = true
1515

16-
def handleRequest[U](request: AuthorizationRequest, maybeClientCredential: Option[ClientCredential], authorizationHandler: AuthorizationHandler[U]): Future[GrantHandlerResult]
16+
def handleRequest[U](request: AuthorizationRequest, authorizationHandler: AuthorizationHandler[U]): Future[GrantHandlerResult]
1717

1818
/**
1919
* Returns valid access token.
@@ -44,9 +44,10 @@ trait GrantHandler {
4444

4545
class RefreshToken extends GrantHandler {
4646

47-
override def handleRequest[U](request: AuthorizationRequest, maybeClientCredential: Option[ClientCredential], handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
48-
val clientCredential = maybeClientCredential.getOrElse(throw new InvalidRequest("Client credential is required"))
49-
val refreshToken = request.requireRefreshToken
47+
override def handleRequest[U](request: AuthorizationRequest, handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
48+
val refreshTokenRequest = new RefreshTokenRequest(request)
49+
val clientCredential = refreshTokenRequest.clientCredential.getOrElse(throw new InvalidRequest("Client credential is required"))
50+
val refreshToken = refreshTokenRequest.refreshToken
5051

5152
handler.findAuthInfoByRefreshToken(refreshToken).flatMap { authInfoOption =>
5253
val authInfo = authInfoOption.getOrElse(throw new InvalidGrant("Authorized information is not found by the refresh token"))
@@ -61,19 +62,17 @@ class RefreshToken extends GrantHandler {
6162

6263
class Password extends GrantHandler {
6364

64-
override def handleRequest[U](request: AuthorizationRequest, maybeClientCredential: Option[ClientCredential], handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
65-
if (clientCredentialRequired && maybeClientCredential.isEmpty) {
65+
override def handleRequest[U](request: AuthorizationRequest, handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
66+
val passwordRequest = new PasswordRequest(request)
67+
if (clientCredentialRequired && passwordRequest.clientCredential.isEmpty) {
6668
throw new InvalidRequest("Client credential is required")
6769
}
6870

69-
val username = request.requireUsername
70-
val password = request.requirePassword
71-
72-
handler.findUser(username, password).flatMap { userOption =>
73-
val user = userOption.getOrElse(throw new InvalidGrant("username or password is incorrect"))
74-
val scope = request.scope
75-
val clientId = maybeClientCredential.map { _.clientId }
76-
val authInfo = AuthInfo(user, clientId, scope, None)
71+
handler.findUser(passwordRequest).flatMap { maybeUser =>
72+
val user = maybeUser.getOrElse(throw new InvalidGrant("username or password is incorrect"))
73+
val scope = passwordRequest.scope
74+
val maybeClientId = passwordRequest.clientCredential.map(_.clientId)
75+
val authInfo = AuthInfo(user, maybeClientId, scope, None)
7776

7877
issueAccessToken(handler, authInfo)
7978
}
@@ -82,11 +81,12 @@ class Password extends GrantHandler {
8281

8382
class ClientCredentials extends GrantHandler {
8483

85-
override def handleRequest[U](request: AuthorizationRequest, maybeClientCredential: Option[ClientCredential], handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
86-
val clientCredential = maybeClientCredential.getOrElse(throw new InvalidRequest("Client credential is required"))
87-
val scope = request.scope
84+
override def handleRequest[U](request: AuthorizationRequest, handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
85+
val clientCredentialsRequest = new ClientCredentialsRequest(request)
86+
val clientCredential = clientCredentialsRequest.clientCredential.getOrElse(throw new InvalidRequest("Client credential is required"))
87+
val scope = clientCredentialsRequest.scope
8888

89-
handler.findClientUser(clientCredential, scope).flatMap { optionalUser =>
89+
handler.findUser(clientCredentialsRequest).flatMap { optionalUser =>
9090
val user = optionalUser.getOrElse(throw new InvalidGrant("client_id or client_secret or scope is incorrect"))
9191
val authInfo = AuthInfo(user, Some(clientCredential.clientId), scope, None)
9292

@@ -98,11 +98,12 @@ class ClientCredentials extends GrantHandler {
9898

9999
class AuthorizationCode extends GrantHandler {
100100

101-
override def handleRequest[U](request: AuthorizationRequest, maybeClientCredential: Option[ClientCredential], handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
102-
val clientCredential = maybeClientCredential.getOrElse(throw new InvalidRequest("Client credential is required"))
101+
override def handleRequest[U](request: AuthorizationRequest, handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
102+
val authorizationCodeRequest = new AuthorizationCodeRequest(request)
103+
val clientCredential = authorizationCodeRequest.clientCredential.getOrElse(throw new InvalidRequest("Client credential is required"))
103104
val clientId = clientCredential.clientId
104-
val code = request.requireCode
105-
val redirectUri = request.redirectUri
105+
val code = authorizationCodeRequest.code
106+
val redirectUri = authorizationCodeRequest.redirectUri
106107

107108
handler.findAuthInfoByCode(code).flatMap { optionalAuthInfo =>
108109
val authInfo = optionalAuthInfo.getOrElse(throw new InvalidGrant("Authorized information is not found by the code"))
@@ -124,13 +125,14 @@ class AuthorizationCode extends GrantHandler {
124125

125126
class Implicit extends GrantHandler {
126127

127-
override def handleRequest[U](request: AuthorizationRequest, maybeClientCredential: Option[ClientCredential], handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
128-
val clientId = request.clientId.getOrElse(throw new InvalidRequest("Client id is required"))
128+
override def handleRequest[U](request: AuthorizationRequest, handler: AuthorizationHandler[U]): Future[GrantHandlerResult] = {
129+
val implicitRequest = new ImplicitRequest(request)
130+
val clientCredential = implicitRequest.clientCredential.getOrElse(throw new InvalidRequest("Client credential is required"))
129131

130-
handler.findUser(request).flatMap { userOption =>
131-
val user = userOption.getOrElse(throw new InvalidGrant("user cannot be authenticated"))
132-
val scope = request.scope
133-
val authInfo = AuthInfo(user, Some(clientId), scope, None)
132+
handler.findUser(implicitRequest).flatMap { maybeUser =>
133+
val user = maybeUser.getOrElse(throw new InvalidGrant("user cannot be authenticated"))
134+
val scope = implicitRequest.scope
135+
val authInfo = AuthInfo(user, Some(clientCredential.clientId), scope, None)
134136

135137
issueAccessToken(handler, authInfo)
136138
}

0 commit comments

Comments
 (0)