@@ -5,36 +5,9 @@ import play.api.libs.json._
55import 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}
0 commit comments