Skip to content

Commit b5f6d59

Browse files
committed
Add AuthorizationHandler#deleteAuthCode
This allows us to implement [section 4.1.2](https://tools.ietf.org/html/rfc6749#section-4.1.2) of the Oauth2 RFC, which states that > The client MUST NOT use the authorization code more than once. If an authorization code is used more than once, the authorization server MUST deny the request and SHOULD revoke (when possible) all tokens previously issued based on that authorization code. The authorization code is bound to the client identifier and redirection URI.
1 parent 2ea64e3 commit b5f6d59

File tree

4 files changed

+37
-15
lines changed

4 files changed

+37
-15
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import scala.concurrent.Future
66
* Provide <b>Authorization</b> phases support for using OAuth 2.0.
77
*
88
* <h3>[Authorization phases]</h3>
9-
*
9+
*
1010
* <h4>Authorization Code Grant</h4>
1111
* <ul>
1212
* <li>validateClient(clientCredential, grantType)</li>
@@ -16,14 +16,14 @@ import scala.concurrent.Future
1616
* <li>refreshAccessToken(authInfo, token)
1717
* <li>createAccessToken(authInfo)</li>
1818
* </ul>
19-
*
19+
*
2020
* <h4>Refresh Token Grant</h4>
2121
* <ul>
2222
* <li>validateClient(clientCredential, grantType)</li>
2323
* <li>findAuthInfoByRefreshToken(refreshToken)</li>
2424
* <li>refreshAccessToken(authInfo, refreshToken)</li>
2525
* </ul>
26-
*
26+
*
2727
* <h4>Resource Owner Password Credentials Grant</h4>
2828
* <ul>
2929
* <li>validateClient(clientCredential, grantType)</li>
@@ -33,7 +33,7 @@ import scala.concurrent.Future
3333
* <li>refreshAccessToken(authInfo, token)
3434
* <li>createAccessToken(authInfo)</li>
3535
* </ul>
36-
*
36+
*
3737
* <h4>Client Credentials Grant</h4>
3838
* <ul>
3939
* <li>validateClient(clientCredential, grantType)</li>
@@ -102,6 +102,18 @@ trait AuthorizationHandler[U] {
102102
*/
103103
def findAuthInfoByCode(code: String): Future[Option[AuthInfo[U]]]
104104

105+
/**
106+
* Deletes an authorization code.
107+
*
108+
* Called when an AccessToken has been successfully issued via an authorization code.
109+
*
110+
* If you don't support Authorization Code Grant, then you don't need to implement this
111+
* method.
112+
*
113+
* @param code Client-sent authorization code
114+
*/
115+
def deleteAuthCode(code: String): Future[Unit]
116+
105117
/**
106118
* Find authorized information by refresh token.
107119
*

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ class AuthorizationCode extends GrantHandler {
122122
throw new RedirectUriMismatch
123123
}
124124

125-
issueAccessToken(handler, authInfo)
125+
val fGrantResult = issueAccessToken(handler, authInfo)
126+
fGrantResult onSuccess { case _ => handler.deleteAuthCode(code) }
127+
fGrantResult
126128
}
127129
}
128130

scala-oauth2-core/src/test/scala/scalaoauth2/provider/AuthorizationCodeSpec.scala

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,28 @@ class AuthorizationCodeSpec extends FlatSpec with ScalaFutures {
1111
it should "handle request" in {
1212
val authorizationCode = new AuthorizationCode()
1313
val request = AuthorizationRequest(Map(), Map("code" -> Seq("code1"), "redirect_uri" -> Seq("http://example.com/")))
14+
var codeDeleted: Boolean = false
1415
val f = authorizationCode.handleRequest(request, Some(ClientCredential("clientId1", Some("clientSecret1"))), new MockDataHandler() {
1516

1617
override def findAuthInfoByCode(code: String): Future[Option[AuthInfo[User]]] = Future.successful(Some(
1718
AuthInfo(user = MockUser(10000, "username"), clientId = Some("clientId1"), scope = Some("all"), redirectUri = Some("http://example.com/"))
1819
))
1920

2021
override def createAccessToken(authInfo: AuthInfo[User]): Future[AccessToken] = Future.successful(AccessToken("token1", Some("refreshToken1"), Some("all"), Some(3600), new java.util.Date()))
22+
23+
override def deleteAuthCode(code: String): Future[Unit] = {
24+
codeDeleted = true
25+
Future.successful(Unit)
26+
}
2127
})
2228

2329
whenReady(f) { result =>
24-
result.tokenType should be ("Bearer")
25-
result.accessToken should be ("token1")
26-
result.expiresIn should be (Some(3600))
27-
result.refreshToken should be (Some("refreshToken1"))
28-
result.scope should be (Some("all"))
30+
codeDeleted shouldBe true
31+
result.tokenType shouldBe "Bearer"
32+
result.accessToken shouldBe "token1"
33+
result.expiresIn shouldBe Some(3600)
34+
result.refreshToken shouldBe Some("refreshToken1")
35+
result.scope shouldBe Some("all")
2936
}
3037
}
3138

@@ -42,11 +49,11 @@ class AuthorizationCodeSpec extends FlatSpec with ScalaFutures {
4249
})
4350

4451
whenReady(f) { result =>
45-
result.tokenType should be ("Bearer")
46-
result.accessToken should be ("token1")
47-
result.expiresIn should be (Some(3600))
48-
result.refreshToken should be (Some("refreshToken1"))
49-
result.scope should be (Some("all"))
52+
result.tokenType shouldBe "Bearer"
53+
result.accessToken shouldBe "token1"
54+
result.expiresIn shouldBe Some(3600)
55+
result.refreshToken shouldBe Some("refreshToken1")
56+
result.scope shouldBe Some("all")
5057
}
5158
}
5259
}

scala-oauth2-core/src/test/scala/scalaoauth2/provider/MockDataHandler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class MockDataHandler extends DataHandler[User] {
2626

2727
def refreshAccessToken(authInfo: AuthInfo[User], refreshToken: String): Future[AccessToken] = Future.successful(AccessToken("", Some(""), Some(""), Some(0L), new Date()))
2828

29+
def deleteAuthCode(code: String): Future[Unit] = Future.successful(Unit)
2930
}
3031

3132
trait User {

0 commit comments

Comments
 (0)