Skip to content

Commit c83c314

Browse files
committed
Merge branch 'play-oauth-async-provider'
2 parents 89ced8d + 0a4b9d0 commit c83c314

File tree

2 files changed

+131
-52
lines changed

2 files changed

+131
-52
lines changed

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

Lines changed: 130 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,9 @@ import play.api.libs.json._
55
import scala.language.implicitConversions
66

77
/**
8-
* OAuth2Provider supports issue access token and authorize.
9-
*
10-
* <h3>Create controller for issue access token</h3>
11-
* <code>
12-
* object OAuth2Controller extends Controller with OAuth2Provider {
13-
* def accessToken = Action { implicit request =>
14-
* issueAccessToken(new MyDataHandler())
15-
* }
16-
* }
17-
* </code>
18-
*
19-
* <h3>Register routes</h3>
20-
* <code>
21-
* POST /oauth2/access_token controllers.OAuth2Controller.accessToken
22-
* </code>
23-
*
24-
* <h3>Authorized</h3>
25-
* <code>
26-
* import scalaoauth2.provider._
27-
* object BookController extends Controller with OAuthProvider {
28-
* def list = Action { implicit request =>
29-
* authorize(new MyDataHandler()) { authInfo =>
30-
* val user = authInfo.user // User is defined on your system
31-
* // access resource for the user
32-
* }
33-
* }
34-
* }
35-
* </code>
8+
* Basic OAuth2 provider trait.
369
*/
37-
trait OAuth2Provider extends Results {
10+
trait OAuth2BaseProvider extends Results {
3811

3912
implicit def play2oauthRequest(request: RequestHeader): AuthorizationRequest = {
4013
AuthorizationRequest(request.headers.toMap, request.queryString)
@@ -68,6 +41,66 @@ trait OAuth2Provider extends Results {
6841
}
6942
}
7043

44+
protected[scalaoauth2] def responseAccessToken(r: GrantHandlerResult) = {
45+
Map[String, JsValue](
46+
"token_type" -> JsString(r.tokenType),
47+
"access_token" -> JsString(r.accessToken)
48+
) ++ r.expiresIn.map {
49+
"expires_in" -> JsNumber(_)
50+
} ++ r.refreshToken.map {
51+
"refresh_token" -> JsString(_)
52+
} ++ r.scope.map {
53+
"scope" -> JsString(_)
54+
}
55+
}
56+
57+
protected[scalaoauth2] def responseOAuthErrorJson(e: OAuthError): JsValue = Json.obj(
58+
"error" -> e.errorType,
59+
"error_description" -> e.description
60+
)
61+
62+
protected[scalaoauth2] def responseOAuthErrorHeader(e: OAuthError): (String, String) = ("WWW-Authenticate" -> ("Bearer " + toOAuthErrorString(e)))
63+
64+
protected def toOAuthErrorString(e: OAuthError): String = {
65+
val params = Seq("error=\"" + e.errorType + "\"") ++
66+
(if (!e.description.isEmpty) { Seq("error_description=\"" + e.description + "\"") } else { Nil })
67+
params.mkString(", ")
68+
}
69+
70+
}
71+
72+
/**
73+
* OAuth2Provider supports issue access token and authorize.
74+
*
75+
* <h3>Create controller for issue access token</h3>
76+
* <code>
77+
* object OAuth2Controller extends Controller with OAuth2Provider {
78+
* def accessToken = Action { implicit request =>
79+
* issueAccessToken(new MyDataHandler())
80+
* }
81+
* }
82+
* </code>
83+
*
84+
* <h3>Register routes</h3>
85+
* <code>
86+
* POST /oauth2/access_token controllers.OAuth2Controller.accessToken
87+
* </code>
88+
*
89+
* <h3>Authorized</h3>
90+
* <code>
91+
* import scalaoauth2.provider._
92+
* object BookController extends Controller with OAuthProvider {
93+
* def list = Action { implicit request =>
94+
* authorize(new MyDataHandler()) { authInfo =>
95+
* val user = authInfo.user // User is defined on your system
96+
* // access resource for the user
97+
* }
98+
* }
99+
* }
100+
* </code>
101+
*/
102+
trait OAuth2Provider extends OAuth2BaseProvider {
103+
71104
/**
72105
* Issue access token in DataHandler process and return the response to client.
73106
*
@@ -85,19 +118,6 @@ trait OAuth2Provider extends Results {
85118
}
86119
}
87120

88-
protected[scalaoauth2] def responseAccessToken(r: GrantHandlerResult) = {
89-
Map[String, JsValue](
90-
"token_type" -> JsString(r.tokenType),
91-
"access_token" -> JsString(r.accessToken)
92-
) ++ r.expiresIn.map {
93-
"expires_in" -> JsNumber(_)
94-
} ++ r.refreshToken.map {
95-
"refresh_token" -> JsString(_)
96-
} ++ r.scope.map {
97-
"scope" -> JsString(_)
98-
}
99-
}
100-
101121
/**
102122
* Authorize to already created access token in DataHandler process and return the response to client.
103123
*
@@ -116,16 +136,75 @@ trait OAuth2Provider extends Results {
116136
}
117137
}
118138

119-
protected[scalaoauth2] def responseOAuthErrorJson(e: OAuthError): JsValue = Json.obj(
120-
"error" -> e.errorType,
121-
"error_description" -> e.description
122-
)
139+
}
123140

124-
protected[scalaoauth2] def responseOAuthErrorHeader(e: OAuthError): (String, String) = ("WWW-Authenticate" -> ("Bearer " + toOAuthErrorString(e)))
141+
/**
142+
* OAuth2AsyncProvider supports issue access token and authorize in asynchronous.
143+
*
144+
* <h3>Create controller for issue access token</h3>
145+
* <code>
146+
* object OAuth2Controller extends Controller with OAuth2AsyncProvider {
147+
* def accessToken = Action.async { implicit request =>
148+
* issueAccessToken(new MyDataHandler())
149+
* }
150+
* }
151+
* </code>
152+
*
153+
* <h3>Register routes</h3>
154+
* <code>
155+
* POST /oauth2/access_token controllers.OAuth2Controller.accessToken
156+
* </code>
157+
*
158+
* <h3>Authorized</h3>
159+
* <code>
160+
* import scalaoauth2.provider._
161+
* object BookController extends Controller with OAuth2AsyncProvider {
162+
* def list = Action.async { implicit request =>
163+
* authorize(new MyDataHandler()) { authInfo =>
164+
* val user = authInfo.user // User is defined on your system
165+
* // access resource for the user
166+
* }
167+
* }
168+
* }
169+
* </code>
170+
*/
171+
trait OAuth2AsyncProvider extends OAuth2BaseProvider {
125172

126-
protected def toOAuthErrorString(e: OAuthError): String = {
127-
val params = Seq("error=\"" + e.errorType + "\"") ++
128-
(if (!e.description.isEmpty) { Seq("error_description=\"" + e.description + "\"") } else { Nil })
129-
params.mkString(", ")
173+
import scala.concurrent.Future
174+
175+
/**
176+
* Issue access token in DataHandler process and return the response to client.
177+
*
178+
* @param dataHandler Implemented DataHander for register access token to your system.
179+
* @param request Playframework is provided HTTP request interface.
180+
* @tparam A play.api.mvc.Request has type.
181+
* @return Request is successful then return JSON to client in OAuth 2.0 format.
182+
* Request is failed then return BadRequest or Unauthorized status to client with cause into the JSON.
183+
*/
184+
def issueAccessToken[A, U](dataHandler: DataHandler[U])(implicit request: play.api.mvc.Request[A]): Future[SimpleResult] = {
185+
TokenEndpoint.handleRequest(request, dataHandler) match {
186+
case Left(e) if e.statusCode == 400 => Future.successful(BadRequest(responseOAuthErrorJson(e)))
187+
case Left(e) if e.statusCode == 401 => Future.successful(Unauthorized(responseOAuthErrorJson(e)))
188+
case Right(r) => Future.successful(Ok(Json.toJson(responseAccessToken(r))))
189+
}
190+
}
191+
192+
/**
193+
* Authorize to already created access token in DataHandler process and return the response to client.
194+
*
195+
* @param dataHandler Implemented DataHander for authenticate to your system.
196+
* @param callback Callback is called when authentication is successful.
197+
* @param request Playframework is provided HTTP request interface.
198+
* @tparam A play.api.mvc.Request has type.
199+
* @return Authentication is successful then the response use your API result.
200+
* Authentication is failed then return BadRequest or Unauthorized status to client with cause into the JSON.
201+
*/
202+
def authorize[A, U](dataHandler: DataHandler[U])(callback: AuthInfo[U] => Future[SimpleResult])(implicit request: play.api.mvc.Request[A]): Future[SimpleResult] = {
203+
ProtectedResource.handleRequest(request, dataHandler) match {
204+
case Left(e) if e.statusCode == 400 => Future.successful(BadRequest.withHeaders(responseOAuthErrorHeader(e)))
205+
case Left(e) if e.statusCode == 401 => Future.successful(Unauthorized.withHeaders(responseOAuthErrorHeader(e)))
206+
case Right(authInfo) => callback(authInfo)
207+
}
130208
}
209+
131210
}

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Keys._
44
object ScalaOAuth2Build extends Build {
55

66
lazy val _organization = "com.nulab-inc"
7-
lazy val _version = "0.5.1"
7+
lazy val _version = "0.6.0-SNAPSHOT"
88
lazy val _playVersion = "2.2.0"
99

1010
val _scalaVersion = "2.10.3"

0 commit comments

Comments
 (0)